From f857926ec35e29b83df142f4cd1aaa30c13018b9 Mon Sep 17 00:00:00 2001 From: bicarus-dev <202771338+bicarus-dev@users.noreply.github.com> Date: Sun, 16 Aug 2026 21:23:41 -0700 Subject: [PATCH] fix line endings --- .gitattributes | 42 + .github/workflows/ci.yml | 62 +- .github/workflows/release.yml | 98 +- src/spice2x/api/modules/ddr.cpp | 108 +- src/spice2x/api/modules/ddr.h | 34 +- .../cmake/check_no_static_dll_imports.cmake | 144 +- src/spice2x/external/dseg/DSEG-LICENSE.txt | 190 +- .../external/dseg/DSEG14Classic-Italic.sfd | 22046 ++++++++-------- .../external/easywsclient/easywsclient.cpp | 1104 +- .../external/easywsclient/easywsclient.hpp | 146 +- .../external/imgui/imgui_memory_editor.h | 1670 +- src/spice2x/games/gitadora/asio.cpp | 342 +- src/spice2x/games/gitadora/asio.h | 24 +- src/spice2x/games/iidx/mf_wrappers.cpp | 262 +- src/spice2x/games/iidx/mf_wrappers.h | 64 +- src/spice2x/games/nost/touch_mode.cpp | 466 +- src/spice2x/games/nost/touch_mode.h | 54 +- src/spice2x/games/rb/touch_debug.cpp | 286 +- src/spice2x/games/rb/touch_debug.h | 28 +- src/spice2x/games/rb/touch_defs.h | 34 +- src/spice2x/games/sdvx/sdvx_live2d.cpp | 144 +- src/spice2x/games/sdvx/sdvx_live2d.h | 28 +- src/spice2x/hooks/audio/asio_driver_scan.cpp | 168 +- src/spice2x/hooks/audio/asio_driver_scan.h | 30 +- src/spice2x/hooks/audio/asio_proxy.cpp | 2134 +- src/spice2x/hooks/audio/asio_proxy.h | 480 +- .../backends/mmdevice/audio_endpoint_volume.h | 84 +- .../audio/backends/mmdevice/null_device.cpp | 370 +- .../audio/backends/mmdevice/null_device.h | 78 +- .../mmdevice/null_discard_backend.cpp | 278 +- .../backends/mmdevice/null_discard_backend.h | 108 +- .../hooks/audio/backends/wasapi/downmix.cpp | 528 +- .../hooks/audio/backends/wasapi/downmix.h | 300 +- .../hooks/audio/backends/wasapi/resample.cpp | 874 +- .../hooks/audio/backends/wasapi/resample.h | 298 +- .../hooks/audio/backends/wasapi/shared.cpp | 374 +- .../hooks/audio/backends/wasapi/shared.h | 166 +- src/spice2x/hooks/audio/xact.cpp | 784 +- src/spice2x/hooks/audio/xact.h | 8 +- .../graphics/backends/d3d11/d3d11_backend.cpp | 608 +- .../graphics/backends/d3d11/d3d11_backend.h | 48 +- .../graphics/backends/d3d11/d3d11_factory.cpp | 204 +- .../graphics/backends/d3d11/d3d11_internal.h | 118 +- .../backends/d3d11/d3d11_screenshot.cpp | 330 +- .../backends/d3d11/d3d11_swapchain.cpp | 686 +- .../backends/d3d11/d3d11_vtable_capture.cpp | 350 +- .../graphics/backends/d3d9/d3d9_live2d.cpp | 288 +- .../graphics/backends/d3d9/d3d9_live2d.h | 88 +- src/spice2x/hooks/graphics/nvapi_impl.cpp | 960 +- src/spice2x/hooks/graphics/nvapi_impl.h | 28 +- src/spice2x/misc/hotkeys.cpp | 308 +- src/spice2x/misc/hotkeys.h | 26 +- src/spice2x/overlay/notifications.cpp | 530 +- src/spice2x/overlay/notifications.h | 110 +- .../overlay/windows/nostalgia_touch_piano.cpp | 298 +- .../overlay/windows/nostalgia_touch_piano.h | 30 +- src/spice2x/overlay/windows/obs.cpp | 580 +- src/spice2x/overlay/windows/obs.h | 262 +- src/spice2x/overlay/windows/obs_websocket.cpp | 868 +- src/spice2x/patcher/loader_group.cpp | 358 +- src/spice2x/rawinput/midi.cpp | 1452 +- src/spice2x/sdk/include/spicesdk.h | 586 +- src/spice2x/sdk/include/spicesdk_io.h | 3740 +-- src/spice2x/sdk/sample/v0/cpp/v0_cpp.def | 6 +- src/spice2x/sdk/sample/v0/flat_c/v0_flat_c.c | 636 +- .../sdk/sample/v0/flat_c/v0_flat_c.def | 6 +- src/spice2x/sdk/sdk.cpp | 1286 +- src/spice2x/sdk/sdk.h | 22 +- src/spice2x/touch/gdi_overlay.cpp | 426 +- src/spice2x/touch/gdi_overlay.h | 42 +- src/spice2x/touch/native/inject_internal.h | 62 +- src/spice2x/touch/native/inject_mouse.cpp | 292 +- src/spice2x/touch/native/inject_synthetic.cpp | 350 +- src/spice2x/touch/native/settings.h | 12 +- src/spice2x/touch/native/transform.cpp | 430 +- src/spice2x/touch/native/transform.h | 36 +- src/spice2x/update_spice.ps1 | 132 +- 77 files changed, 25537 insertions(+), 25495 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f58a91a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,42 @@ +# Normalize every text file to LF in the repository. +# The build runs under Linux/MinGW containers, so LF is also used in the +# working tree; Windows editors and toolchains handle LF fine. +* text=auto eol=lf + +# Windows-only files that must keep CRLF in the working tree. +*.bat text eol=crlf +*.cmd text eol=crlf +*.sln text eol=crlf +*.vcproj text eol=crlf +*.vcxproj text eol=crlf +*.props text eol=crlf +*.filters text eol=crlf + +# Files that must keep LF even if a Windows editor rewrites them. +*.sh text eol=lf +*.in text eol=lf +*.cmake text eol=lf +*.mk text eol=lf +Makefile text eol=lf +Dockerfile text eol=lf + +# Binary files - never touch the contents. +*.bin binary +*.ico binary +*.ttf binary +*.otf binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.bmp binary +*.zip binary +*.7z binary +*.gz binary +*.dll binary +*.exe binary +*.lib binary +*.a binary +*.o binary +*.obj binary +*.pdb binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b08cf7..6afb357 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,32 +1,32 @@ -on: [push, pull_request] -name: Continuous Integration -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - fw-ci: - name: Build - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./src/spice2x - steps: - - uses: actions/checkout@v5 - - name: Set ccache environment variables - run: | - echo "CCACHE_DIR=${{ github.workspace }}/src/spice2x/.ccache" >> $GITHUB_ENV - - name: Install ccache - uses: hendrikmuhs/ccache-action@v1.2.23 - - name: Calculate commit SHA - id: vars - run: | - calculatedSha=$(git rev-parse --short ${{ github.sha }}) - echo "COMMIT_SHORT_SHA=$calculatedSha" >> $GITHUB_ENV - - name: Compile - run: ./build_docker.sh - - uses: actions/upload-artifact@v6 - with: - name: spice2x-ci-${{ env.COMMIT_SHORT_SHA }} - path: src/spice2x/bin +on: [push, pull_request] +name: Continuous Integration +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + fw-ci: + name: Build + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./src/spice2x + steps: + - uses: actions/checkout@v5 + - name: Set ccache environment variables + run: | + echo "CCACHE_DIR=${{ github.workspace }}/src/spice2x/.ccache" >> $GITHUB_ENV + - name: Install ccache + uses: hendrikmuhs/ccache-action@v1.2.23 + - name: Calculate commit SHA + id: vars + run: | + calculatedSha=$(git rev-parse --short ${{ github.sha }}) + echo "COMMIT_SHORT_SHA=$calculatedSha" >> $GITHUB_ENV + - name: Compile + run: ./build_docker.sh + - uses: actions/upload-artifact@v6 + with: + name: spice2x-ci-${{ env.COMMIT_SHORT_SHA }} + path: src/spice2x/bin if-no-files-found: error \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4d610b0..2762ea0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,49 +1,49 @@ -name: Draft Release - -on: - workflow_dispatch: - -permissions: - contents: write - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - release: - name: Build and Draft Release - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./src/spice2x - steps: - - uses: actions/checkout@v5 - with: - ref: main - fetch-depth: 0 - - name: Clean leftover build artifacts - run: | - rm -rf .ccache dist bin cmake-build-* - - name: Compile - run: ./build_docker.sh - - name: Determine release name from dist filename - run: | - dist=$(basename "$(ls dist/spice2x-*.zip | grep -v -- '-full.zip')") - # strip the ".zip" to get the base name, e.g. spice2x-26-06-28 - name="${dist%.zip}" - # the tag is the date portion, e.g. 26-06-28 - tag="${name#spice2x-}" - echo "RELEASE_NAME=$name" >> $GITHUB_ENV - echo "RELEASE_TAG=$tag" >> $GITHUB_ENV - - name: Create draft release - uses: softprops/action-gh-release@v3 - with: - draft: true - prerelease: true - tag_name: ${{ env.RELEASE_TAG }} - name: ${{ env.RELEASE_NAME }} - target_commitish: main - generate_release_notes: true - files: | - src/spice2x/dist/spice2x-*.zip +name: Draft Release + +on: + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + release: + name: Build and Draft Release + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./src/spice2x + steps: + - uses: actions/checkout@v5 + with: + ref: main + fetch-depth: 0 + - name: Clean leftover build artifacts + run: | + rm -rf .ccache dist bin cmake-build-* + - name: Compile + run: ./build_docker.sh + - name: Determine release name from dist filename + run: | + dist=$(basename "$(ls dist/spice2x-*.zip | grep -v -- '-full.zip')") + # strip the ".zip" to get the base name, e.g. spice2x-26-06-28 + name="${dist%.zip}" + # the tag is the date portion, e.g. 26-06-28 + tag="${name#spice2x-}" + echo "RELEASE_NAME=$name" >> $GITHUB_ENV + echo "RELEASE_TAG=$tag" >> $GITHUB_ENV + - name: Create draft release + uses: softprops/action-gh-release@v3 + with: + draft: true + prerelease: true + tag_name: ${{ env.RELEASE_TAG }} + name: ${{ env.RELEASE_NAME }} + target_commitish: main + generate_release_notes: true + files: | + src/spice2x/dist/spice2x-*.zip diff --git a/src/spice2x/api/modules/ddr.cpp b/src/spice2x/api/modules/ddr.cpp index 6b4171f..2c3f3fe 100644 --- a/src/spice2x/api/modules/ddr.cpp +++ b/src/spice2x/api/modules/ddr.cpp @@ -1,54 +1,54 @@ -#include "ddr.h" -#include -#include "external/rapidjson/document.h" -#include "games/ddr/ddr.h" - -using namespace std::placeholders; -using namespace rapidjson; - -namespace api::modules { - - DDR::DDR() : Module("ddr") { - functions["tapeled_get"] = std::bind(&DDR::tapeled_get, this, _1, _2); - } - - /** - * Allows fetching of the RGB LED strips that are gold cabinets, via SpiceAPI - */ - void DDR::tapeled_get(Request &req, Response &res) { - static const char* device_names[11] = { - "p1_foot_up", - "p1_foot_right", - "p1_foot_left", - "p1_foot_down", - "p2_foot_up", - "p2_foot_right", - "p2_foot_left", - "p2_foot_down", - "top_panel", - "monitor_left", - "monitor_right" - }; - - Value response_object(kObjectType); - - // Iterate through each device and dump its lights data into the response - for (size_t device = 0; device < 11; device++) { - size_t num_leds = 25; - if (device > 7) - num_leds = 50; - - Value light_state(kArrayType); - light_state.Reserve(num_leds * 3, res.doc()->GetAllocator()); - for (size_t led = 0; led < num_leds; led++) { - light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][0], res.doc()->GetAllocator()); - light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][1], res.doc()->GetAllocator()); - light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][2], res.doc()->GetAllocator()); - } - - response_object.AddMember(StringRef(device_names[device]), light_state, res.doc()->GetAllocator()); - } - - res.add_data(response_object); - } -} +#include "ddr.h" +#include +#include "external/rapidjson/document.h" +#include "games/ddr/ddr.h" + +using namespace std::placeholders; +using namespace rapidjson; + +namespace api::modules { + + DDR::DDR() : Module("ddr") { + functions["tapeled_get"] = std::bind(&DDR::tapeled_get, this, _1, _2); + } + + /** + * Allows fetching of the RGB LED strips that are gold cabinets, via SpiceAPI + */ + void DDR::tapeled_get(Request &req, Response &res) { + static const char* device_names[11] = { + "p1_foot_up", + "p1_foot_right", + "p1_foot_left", + "p1_foot_down", + "p2_foot_up", + "p2_foot_right", + "p2_foot_left", + "p2_foot_down", + "top_panel", + "monitor_left", + "monitor_right" + }; + + Value response_object(kObjectType); + + // Iterate through each device and dump its lights data into the response + for (size_t device = 0; device < 11; device++) { + size_t num_leds = 25; + if (device > 7) + num_leds = 50; + + Value light_state(kArrayType); + light_state.Reserve(num_leds * 3, res.doc()->GetAllocator()); + for (size_t led = 0; led < num_leds; led++) { + light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][0], res.doc()->GetAllocator()); + light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][1], res.doc()->GetAllocator()); + light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][2], res.doc()->GetAllocator()); + } + + response_object.AddMember(StringRef(device_names[device]), light_state, res.doc()->GetAllocator()); + } + + res.add_data(response_object); + } +} diff --git a/src/spice2x/api/modules/ddr.h b/src/spice2x/api/modules/ddr.h index 13a8fc5..95ed300 100644 --- a/src/spice2x/api/modules/ddr.h +++ b/src/spice2x/api/modules/ddr.h @@ -1,17 +1,17 @@ -#pragma once - -#include -#include "api/module.h" -#include "api/request.h" - -namespace api::modules { - - class DDR : public Module { - public: - DDR(); - - private: - // function definitions - void tapeled_get(Request &req, Response &res); - }; -} +#pragma once + +#include +#include "api/module.h" +#include "api/request.h" + +namespace api::modules { + + class DDR : public Module { + public: + DDR(); + + private: + // function definitions + void tapeled_get(Request &req, Response &res); + }; +} diff --git a/src/spice2x/cmake/check_no_static_dll_imports.cmake b/src/spice2x/cmake/check_no_static_dll_imports.cmake index 130b0fc..232946b 100644 --- a/src/spice2x/cmake/check_no_static_dll_imports.cmake +++ b/src/spice2x/cmake/check_no_static_dll_imports.cmake @@ -1,72 +1,72 @@ -# fails the build if a PE binary statically imports a forbidden DLL. -# -# some DLLs must never end up in spice's static import table, for two reasons: -# -# 1. user-overridable DLLs (e.g. DXVK's d3d9.dll): users drop their own copy -# into the modules directory to replace the system one. a static import -# forces the loader to load the SYSTEM copy at process startup - before the -# modules directory is added to the DLL search path and before the game DLL -# loads - so the user-supplied override never takes effect (see issue #779). -# -# 2. DLLs that break games when present (e.g. Media Foundation: mf/mfplat/ -# mfreadwrite): a static import loads them eagerly and breaks Unity games. -# -# in both cases the DLL must instead be loaded dynamically (libutils::try_library -# / GetProcAddress / delay load) so it is only pulled in when actually needed. -# -# invoked via `cmake -P` from a POST_BUILD step. required -D variables: -# OBJDUMP - path to objdump (CMAKE_OBJDUMP) -# TARGET_FILE - path to the PE binary to inspect -# FORBIDDEN - semicolon-separated list of lowercase DLL names to reject - -if(NOT OBJDUMP OR NOT EXISTS "${OBJDUMP}") - message(WARNING - "check_no_static_dll_imports: objdump not found, skipping import check for ${TARGET_FILE}") - return() -endif() - -execute_process( - COMMAND "${OBJDUMP}" -p "${TARGET_FILE}" - OUTPUT_VARIABLE dump_output - RESULT_VARIABLE dump_result - ERROR_VARIABLE dump_error) - -if(NOT dump_result EQUAL 0) - message(WARNING - "check_no_static_dll_imports: objdump failed for ${TARGET_FILE}: ${dump_error}") - return() -endif() - -# both GNU objdump and llvm-objdump print one "DLL Name: " line per -# statically imported DLL in their PE private-header dump. -string(REGEX MATCHALL "DLL Name:[ \t]*[^\n\r]+" dll_lines "${dump_output}") - -set(violations "") -foreach(line IN LISTS dll_lines) - string(REGEX REPLACE "DLL Name:[ \t]*" "" dll_name "${line}") - string(STRIP "${dll_name}" dll_name) - string(TOLOWER "${dll_name}" dll_name_lower) - if(dll_name_lower IN_LIST FORBIDDEN) - list(APPEND violations "${dll_name}") - endif() -endforeach() - -if(violations) - list(REMOVE_DUPLICATES violations) - string(REPLACE ";" ", " violations_str "${violations}") - message(FATAL_ERROR - "static DLL import check FAILED for ${TARGET_FILE}\n" - " forbidden static imports found: ${violations_str}\n" - "\n" - " these DLLs must never be statically imported by spice:\n" - " * user-overridable DLLs (e.g. DXVK d3d9.dll) - a static import loads the\n" - " system copy at startup and preempts the modules override (issue #779).\n" - " * Media Foundation DLLs (mf/mfplat/mfreadwrite) - a static import breaks\n" - " Unity games.\n" - "\n" - " fix: load the DLL dynamically instead - replace the direct API call with a\n" - " libutils::try_library() + libutils::try_proc() lookup (or a delay load), then\n" - " call through the resolved function pointer.") -endif() - -message(STATUS "static DLL import check passed for ${TARGET_FILE}") +# fails the build if a PE binary statically imports a forbidden DLL. +# +# some DLLs must never end up in spice's static import table, for two reasons: +# +# 1. user-overridable DLLs (e.g. DXVK's d3d9.dll): users drop their own copy +# into the modules directory to replace the system one. a static import +# forces the loader to load the SYSTEM copy at process startup - before the +# modules directory is added to the DLL search path and before the game DLL +# loads - so the user-supplied override never takes effect (see issue #779). +# +# 2. DLLs that break games when present (e.g. Media Foundation: mf/mfplat/ +# mfreadwrite): a static import loads them eagerly and breaks Unity games. +# +# in both cases the DLL must instead be loaded dynamically (libutils::try_library +# / GetProcAddress / delay load) so it is only pulled in when actually needed. +# +# invoked via `cmake -P` from a POST_BUILD step. required -D variables: +# OBJDUMP - path to objdump (CMAKE_OBJDUMP) +# TARGET_FILE - path to the PE binary to inspect +# FORBIDDEN - semicolon-separated list of lowercase DLL names to reject + +if(NOT OBJDUMP OR NOT EXISTS "${OBJDUMP}") + message(WARNING + "check_no_static_dll_imports: objdump not found, skipping import check for ${TARGET_FILE}") + return() +endif() + +execute_process( + COMMAND "${OBJDUMP}" -p "${TARGET_FILE}" + OUTPUT_VARIABLE dump_output + RESULT_VARIABLE dump_result + ERROR_VARIABLE dump_error) + +if(NOT dump_result EQUAL 0) + message(WARNING + "check_no_static_dll_imports: objdump failed for ${TARGET_FILE}: ${dump_error}") + return() +endif() + +# both GNU objdump and llvm-objdump print one "DLL Name: " line per +# statically imported DLL in their PE private-header dump. +string(REGEX MATCHALL "DLL Name:[ \t]*[^\n\r]+" dll_lines "${dump_output}") + +set(violations "") +foreach(line IN LISTS dll_lines) + string(REGEX REPLACE "DLL Name:[ \t]*" "" dll_name "${line}") + string(STRIP "${dll_name}" dll_name) + string(TOLOWER "${dll_name}" dll_name_lower) + if(dll_name_lower IN_LIST FORBIDDEN) + list(APPEND violations "${dll_name}") + endif() +endforeach() + +if(violations) + list(REMOVE_DUPLICATES violations) + string(REPLACE ";" ", " violations_str "${violations}") + message(FATAL_ERROR + "static DLL import check FAILED for ${TARGET_FILE}\n" + " forbidden static imports found: ${violations_str}\n" + "\n" + " these DLLs must never be statically imported by spice:\n" + " * user-overridable DLLs (e.g. DXVK d3d9.dll) - a static import loads the\n" + " system copy at startup and preempts the modules override (issue #779).\n" + " * Media Foundation DLLs (mf/mfplat/mfreadwrite) - a static import breaks\n" + " Unity games.\n" + "\n" + " fix: load the DLL dynamically instead - replace the direct API call with a\n" + " libutils::try_library() + libutils::try_proc() lookup (or a delay load), then\n" + " call through the resolved function pointer.") +endif() + +message(STATUS "static DLL import check passed for ${TARGET_FILE}") diff --git a/src/spice2x/external/dseg/DSEG-LICENSE.txt b/src/spice2x/external/dseg/DSEG-LICENSE.txt index ad4c0bf..a6c8ffd 100644 --- a/src/spice2x/external/dseg/DSEG-LICENSE.txt +++ b/src/spice2x/external/dseg/DSEG-LICENSE.txt @@ -1,95 +1,95 @@ -Copyright (c) 2017, keshikan (http://www.keshikan.net), -with Reserved Font Name "DSEG". - - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright (c) 2017, keshikan (http://www.keshikan.net), +with Reserved Font Name "DSEG". + + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/spice2x/external/dseg/DSEG14Classic-Italic.sfd b/src/spice2x/external/dseg/DSEG14Classic-Italic.sfd index 2b814de..733018d 100644 --- a/src/spice2x/external/dseg/DSEG14Classic-Italic.sfd +++ b/src/spice2x/external/dseg/DSEG14Classic-Italic.sfd @@ -1,11023 +1,11023 @@ -SplineFontDB: 3.2 -FontName: DSEG14Classic-Italic -FullName: DSEG14 Classic-Italic -FamilyName: DSEG14 Classic -Weight: Regular -Copyright: Created by Keshikan(https://twitter.com/keshinomi_88pro)\nwith FontForge 2.0 (http://fontforge.sf.net) -UComments: "2014-8-31: Created." -Version: 0.46 -ItalicAngle: -5 -UnderlinePosition: -100 -UnderlineWidth: 50 -Ascent: 1000 -Descent: 0 -InvalidEm: 0 -LayerCount: 2 -Layer: 0 0 "+gMyXYgAA" 1 -Layer: 1 0 "+Uk2XYgAA" 0 -XUID: [1021 682 390630330 14528854] -FSType: 8 -OS2Version: 0 -OS2_WeightWidthSlopeOnly: 0 -OS2_UseTypoMetrics: 1 -CreationTime: 1409488158 -ModificationTime: 1681891359 -PfmFamily: 17 -TTFWeight: 400 -TTFWidth: 5 -LineGap: 90 -VLineGap: 0 -OS2TypoAscent: 0 -OS2TypoAOffset: 1 -OS2TypoDescent: 0 -OS2TypoDOffset: 1 -OS2TypoLinegap: 90 -OS2WinAscent: 0 -OS2WinAOffset: 1 -OS2WinDescent: 0 -OS2WinDOffset: 1 -HheadAscent: 0 -HheadAOffset: 1 -HheadDescent: 0 -HheadDOffset: 1 -OS2Vendor: 'PfEd' -MarkAttachClasses: 1 -DEI: 91125 -LangName: 1033 "Created by Keshikan+AAoA-with FontForge 2.0 (http://fontforge.sf.net)" "" "" "" "" "Version 0.46" "" "" "" "Keshikan(Twitter:@keshinomi_88pro)" "" "" "http://www.keshikan.net" "Copyright (c) 2018, keshikan (http://www.keshikan.net),+AAoA-with Reserved Font Name +ACIA-DSEG+ACIA.+AAoACgAA-This Font Software is licensed under the SIL Open Font License, Version 1.1.+AAoA-This license is copied below, and is also available with a FAQ at:+AAoA-http://scripts.sil.org/OFL+AAoACgAK------------------------------------------------------------+AAoA-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007+AAoA------------------------------------------------------------+AAoACgAA-PREAMBLE+AAoA-The goals of the Open Font License (OFL) are to stimulate worldwide+AAoA-development of collaborative font projects, to support the font creation+AAoA-efforts of academic and linguistic communities, and to provide a free and+AAoA-open framework in which fonts may be shared and improved in partnership+AAoA-with others.+AAoACgAA-The OFL allows the licensed fonts to be used, studied, modified and+AAoA-redistributed freely as long as they are not sold by themselves. The+AAoA-fonts, including any derivative works, can be bundled, embedded, +AAoA-redistributed and/or sold with any software provided that any reserved+AAoA-names are not used by derivative works. The fonts and derivatives,+AAoA-however, cannot be released under any other type of license. The+AAoA-requirement for fonts to remain under this license does not apply+AAoA-to any document created using the fonts or their derivatives.+AAoACgAA-DEFINITIONS+AAoAIgAA-Font Software+ACIA refers to the set of files released by the Copyright+AAoA-Holder(s) under this license and clearly marked as such. This may+AAoA-include source files, build scripts and documentation.+AAoACgAi-Reserved Font Name+ACIA refers to any names specified as such after the+AAoA-copyright statement(s).+AAoACgAi-Original Version+ACIA refers to the collection of Font Software components as+AAoA-distributed by the Copyright Holder(s).+AAoACgAi-Modified Version+ACIA refers to any derivative made by adding to, deleting,+AAoA-or substituting -- in part or in whole -- any of the components of the+AAoA-Original Version, by changing formats or by porting the Font Software to a+AAoA-new environment.+AAoACgAi-Author+ACIA refers to any designer, engineer, programmer, technical+AAoA-writer or other person who contributed to the Font Software.+AAoACgAA-PERMISSION & CONDITIONS+AAoA-Permission is hereby granted, free of charge, to any person obtaining+AAoA-a copy of the Font Software, to use, study, copy, merge, embed, modify,+AAoA-redistribute, and sell modified and unmodified copies of the Font+AAoA-Software, subject to the following conditions:+AAoACgAA-1) Neither the Font Software nor any of its individual components,+AAoA-in Original or Modified Versions, may be sold by itself.+AAoACgAA-2) Original or Modified Versions of the Font Software may be bundled,+AAoA-redistributed and/or sold with any software, provided that each copy+AAoA-contains the above copyright notice and this license. These can be+AAoA-included either as stand-alone text files, human-readable headers or+AAoA-in the appropriate machine-readable metadata fields within text or+AAoA-binary files as long as those fields can be easily viewed by the user.+AAoACgAA-3) No Modified Version of the Font Software may use the Reserved Font+AAoA-Name(s) unless explicit written permission is granted by the corresponding+AAoA-Copyright Holder. This restriction only applies to the primary font name as+AAoA-presented to the users.+AAoACgAA-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font+AAoA-Software shall not be used to promote, endorse or advertise any+AAoA-Modified Version, except to acknowledge the contribution(s) of the+AAoA-Copyright Holder(s) and the Author(s) or with their explicit written+AAoA-permission.+AAoACgAA-5) The Font Software, modified or unmodified, in part or in whole,+AAoA-must be distributed entirely under this license, and must not be+AAoA-distributed under any other license. The requirement for fonts to+AAoA-remain under this license does not apply to any document created+AAoA-using the Font Software.+AAoACgAA-TERMINATION+AAoA-This license becomes null and void if any of the above conditions are+AAoA-not met.+AAoACgAA-DISCLAIMER+AAoA-THE FONT SOFTWARE IS PROVIDED +ACIA-AS IS+ACIA, WITHOUT WARRANTY OF ANY KIND,+AAoA-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF+AAoA-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT+AAoA-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE+AAoA-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+AAoA-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL+AAoA-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+AAoA-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM+AAoA-OTHER DEALINGS IN THE FONT SOFTWARE." "http://scripts.sil.org/OFL" "" "" "" "" "DSEG14 12:34" -Encoding: UnicodeFull -UnicodeInterp: none -NameList: Adobe Glyph List -DisplaySize: -48 -AntiAlias: 1 -FitToEm: 1 -WinInfo: 0 32 9 -BeginPrivate: 0 -EndPrivate -TeXData: 1 0 0 209715 104857 69905 930087 1048576 69905 783286 444596 497025 792723 393216 433062 380633 303038 157286 324010 404750 52429 2506097 1059062 262144 -BeginChars: 1114112 814 - -StartChar: uEE00 -Encoding: 60928 60928 0 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --631 969 m 1 - -597 1000 l 1 - -131 1000 l 1 - -103 969 l 1 - -170 907 l 1 - -190 907 l 1 - -326 907 l 1 - -418 907 l 1 - -554 907 l 1 - -574 907 l 1 - -631 969 l 1 -EndSplineSet -EndChar - -StartChar: uEE01 -Encoding: 60929 60929 1 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --90 955 m 1 - -62 924 l 1 - -98 510 l 1 - -125 510 l 1 - -130 514 l 1 - -185 575 l 1 - -183 607 l 1 - -167 782 l 1 - -158 887 l 1 - -158 893 l 1 - -90 955 l 1 -EndSplineSet -EndChar - -StartChar: uEE02 -Encoding: 60930 60930 2 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --132 485 m 1 - -127 489 l 1 - -100 489 l 1 - -136 76 l 1 - -170 45 l 1 - -226 107 l 1 - -226 113 l 1 - -217 218 l 1 - -199 424 l 1 - -132 485 l 1 -EndSplineSet -EndChar - -StartChar: uEE03 -Encoding: 60931 60931 3 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --185 31 m 1 - -219 0 l 1 - -685 0 l 1 - -713 31 l 1 - -646 93 l 1 - -626 93 l 1 - -490 93 l 1 - -398 93 l 1 - -262 93 l 1 - -242 93 l 1 - -185 31 l 1 -EndSplineSet -EndChar - -StartChar: uEE04 -Encoding: 60932 60932 4 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --726 45 m 1 - -754 76 l 1 - -718 490 l 1 - -691 490 l 1 - -687 485 l 1 - -631 424 l 1 - -649 218 l 1 - -658 113 l 1 - -658 107 l 1 - -726 45 l 1 -EndSplineSet -EndChar - -StartChar: uEE05 -Encoding: 60933 60933 5 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --685 515 m 1 - -689 510 l 1 - -716 510 l 1 - -680 924 l 1 - -646 955 l 1 - -590 893 l 1 - -590 887 l 1 - -599 782 l 1 - -617 576 l 1 - -685 515 l 1 -EndSplineSet -EndChar - -StartChar: uEE06 -Encoding: 60934 60934 6 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --672 500 m 1 - -620 547 l 1 - -620 546 l 1 - -484 546 l 1 - -443 546 l 1 - -420 500 l 1 - -451 454 l 1 - -608 454 l 1 - -611 454 l 1 - -628 454 l 1 - -628 453 l 1 - -672 500 l 1 -EndSplineSet -EndChar - -StartChar: uEE07 -Encoding: 60935 60935 7 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --188 546 m 1 - -145 499 l 1 - -196 453 l 1 - -196 454 l 1 - -214 454 l 1 - -216 454 l 1 - -345 454 l 1 - -366 454 l 1 - -373 454 l 1 - -396 500 l 1 - -365 546 l 1 - -358 546 l 1 - -337 546 l 1 - -324 546 l 1 - -188 546 l 1 -EndSplineSet -EndChar - -StartChar: uEE08 -Encoding: 60936 60936 8 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --469 567 m 1 - -494 567 l 1 - -582 746 l 1 - -570 887 l 1 - -544 887 l 1 - -457 709 l 1 - -469 567 l 1 -EndSplineSet -EndChar - -StartChar: uEE09 -Encoding: 60937 60937 9 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --439 673 m 1 - -420 887 l 1 - -328 887 l 1 - -329 876 l 1 - -347 673 l 1 - -353 601 l 1 - -406 521 l 1 - -445 601 l 1 - -439 673 l 1 -EndSplineSet -EndChar - -StartChar: uEE0A -Encoding: 60938 60938 10 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --204 887 m 1 - -178 887 l 1 - -190 746 l 1 - -310 567 l 1 - -335 567 l 1 - -323 709 l 1 - -204 887 l 1 -EndSplineSet -EndChar - -StartChar: uEE0B -Encoding: 60939 60939 11 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --493 291 m 1 - -612 113 l 1 - -638 113 l 1 - -626 254 l 1 - -506 433 l 1 - -481 433 l 1 - -493 291 l 1 -EndSplineSet -EndChar - -StartChar: uEE0C -Encoding: 60940 60940 12 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --392 157 m 1 - -396 113 l 1 - -488 113 l 1 - -469 327 l 1 - -463 399 l 1 - -410 479 l 1 - -371 399 l 1 - -377 327 l 1 - -392 157 l 1 -EndSplineSet -EndChar - -StartChar: uEE0D -Encoding: 60941 60941 13 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet --347 433 m 1 - -322 433 l 1 - -234 254 l 1 - -246 113 l 1 - -272 113 l 1 - -359 291 l 1 - -347 433 l 1 -EndSplineSet -EndChar - -StartChar: uEE0E -Encoding: 60942 60942 14 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -SplineSet -18 62 m 0 - 18 53 16 45 13 38 c 0 - 10 31 6 24 0 18 c 0 - -6 12 -13 8 -20 5 c 0 - -27 2 -35 0 -44 0 c 0 - -53 0 -61 2 -68 5 c 0 - -75 8 -82 12 -88 18 c 0 - -94 24 -98 31 -101 38 c 0 - -104 45 -106 53 -106 62 c 0 - -106 71 -104 79 -101 86 c 0 - -98 93 -94 100 -88 106 c 0 - -82 112 -75 116 -68 119 c 0 - -61 122 -53 124 -44 124 c 0 - -35 124 -27 122 -20 119 c 0 - -13 116 -6 112 0 106 c 0 - 6 100 10 93 13 86 c 0 - 16 79 18 71 18 62 c 0 -EndSplineSet -EndChar - -StartChar: uEE0F -Encoding: 60943 60943 15 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -EndChar - -StartChar: uEE10 -Encoding: 60944 60944 16 -Width: 200 -VWidth: 0 -Flags: HW -LayerCount: 2 -Fore -SplineSet -180 693 m 0 - 180 684 178 676 175 669 c 0 - 172 662 168 655 162 649 c 0 - 156 643 149 639 142 636 c 0 - 135 633 127 631 118 631 c 0 - 109 631 101 633 94 636 c 0 - 87 639 80 643 74 649 c 0 - 68 655 64 662 61 669 c 0 - 58 676 56 684 56 693 c 0 - 56 702 58 710 61 717 c 0 - 64 724 68 730 74 736 c 0 - 80 742 87 747 94 750 c 0 - 101 753 109 754 118 754 c 0 - 127 754 135 753 142 750 c 0 - 149 747 156 742 162 736 c 0 - 168 730 172 724 175 717 c 0 - 178 710 180 702 180 693 c 0 -EndSplineSet -EndChar - -StartChar: uEE11 -Encoding: 60945 60945 17 -Width: 200 -VWidth: 0 -Flags: HW -LayerCount: 2 -Fore -SplineSet -144 281 m 0 - 144 272 142 264 139 257 c 0 - 136 250 132 243 126 237 c 0 - 120 231 113 227 106 224 c 0 - 99 221 91 219 82 219 c 0 - 73 219 65 221 58 224 c 0 - 51 227 44 231 38 237 c 0 - 32 243 28 250 25 257 c 0 - 22 264 20 272 20 281 c 0 - 20 290 22 298 25 305 c 0 - 28 312 32 318 38 324 c 0 - 44 330 51 335 58 338 c 0 - 65 341 73 342 82 342 c 0 - 91 342 99 341 106 338 c 0 - 113 335 120 330 126 324 c 0 - 132 318 136 312 139 305 c 0 - 142 298 144 290 144 281 c 0 -EndSplineSet -EndChar - -StartChar: u0020 -Encoding: 32 32 18 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -EndChar - -StartChar: u0021 -Encoding: 33 33 19 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -EndChar - -StartChar: u0022 -Encoding: 34 34 20 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0023 -Encoding: 35 35 21 -Width: 200 -VWidth: 0 -Flags: HW -LayerCount: 2 -Fore -Refer: 17 60945 N 1 0 0 1 0 0 2 -EndChar - -StartChar: u0024 -Encoding: 36 36 22 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0025 -Encoding: 37 37 23 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0026 -Encoding: 38 38 24 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0027 -Encoding: 39 39 25 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0028 -Encoding: 40 40 26 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0029 -Encoding: 41 41 27 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u002A -Encoding: 42 42 28 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u002B -Encoding: 43 43 29 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u002C -Encoding: 44 44 30 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u002D -Encoding: 45 45 31 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u002E -Encoding: 46 46 32 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 14 60942 N 1 0 0 1 0 0 2 -EndChar - -StartChar: u002F -Encoding: 47 47 33 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0030 -Encoding: 48 48 34 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0031 -Encoding: 49 49 35 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0032 -Encoding: 50 50 36 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0033 -Encoding: 51 51 37 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0034 -Encoding: 52 52 38 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0035 -Encoding: 53 53 39 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0036 -Encoding: 54 54 40 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0037 -Encoding: 55 55 41 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0038 -Encoding: 56 56 42 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0039 -Encoding: 57 57 43 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u003A -Encoding: 58 58 44 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u003B -Encoding: 59 59 45 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u003C -Encoding: 60 60 46 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u003D -Encoding: 61 61 47 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u003E -Encoding: 62 62 48 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u003F -Encoding: 63 63 49 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0040 -Encoding: 64 64 50 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0041 -Encoding: 65 65 51 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0042 -Encoding: 66 66 52 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0043 -Encoding: 67 67 53 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0044 -Encoding: 68 68 54 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0045 -Encoding: 69 69 55 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0046 -Encoding: 70 70 56 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0047 -Encoding: 71 71 57 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0048 -Encoding: 72 72 58 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0049 -Encoding: 73 73 59 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u004A -Encoding: 74 74 60 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u004B -Encoding: 75 75 61 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u004C -Encoding: 76 76 62 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u004D -Encoding: 77 77 63 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u004E -Encoding: 78 78 64 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u004F -Encoding: 79 79 65 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0050 -Encoding: 80 80 66 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0051 -Encoding: 81 81 67 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0052 -Encoding: 82 82 68 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0053 -Encoding: 83 83 69 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0054 -Encoding: 84 84 70 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0055 -Encoding: 85 85 71 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0056 -Encoding: 86 86 72 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0057 -Encoding: 87 87 73 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0058 -Encoding: 88 88 74 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0059 -Encoding: 89 89 75 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u005A -Encoding: 90 90 76 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u005B -Encoding: 91 91 77 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u005C -Encoding: 92 92 78 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u005D -Encoding: 93 93 79 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u005E -Encoding: 94 94 80 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u005F -Encoding: 95 95 81 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0060 -Encoding: 96 96 82 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0061 -Encoding: 97 97 83 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0062 -Encoding: 98 98 84 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0063 -Encoding: 99 99 85 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0064 -Encoding: 100 100 86 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0065 -Encoding: 101 101 87 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0066 -Encoding: 102 102 88 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0067 -Encoding: 103 103 89 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0068 -Encoding: 104 104 90 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0069 -Encoding: 105 105 91 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u006A -Encoding: 106 106 92 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u006B -Encoding: 107 107 93 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u006C -Encoding: 108 108 94 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u006D -Encoding: 109 109 95 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 14 60942 N 1 0 0 1 0 0 2 -EndChar - -StartChar: u006E -Encoding: 110 110 96 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u006F -Encoding: 111 111 97 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0070 -Encoding: 112 112 98 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0071 -Encoding: 113 113 99 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0072 -Encoding: 114 114 100 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0073 -Encoding: 115 115 101 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0074 -Encoding: 116 116 102 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0075 -Encoding: 117 117 103 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0076 -Encoding: 118 118 104 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0077 -Encoding: 119 119 105 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0078 -Encoding: 120 120 106 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u0079 -Encoding: 121 121 107 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u007A -Encoding: 122 122 108 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u007B -Encoding: 123 123 109 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u007C -Encoding: 124 124 110 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u007D -Encoding: 125 125 111 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u007E -Encoding: 126 126 112 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 S 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00A0 -Encoding: 160 160 113 -Width: 200 -VWidth: 0 -Flags: HW -LayerCount: 2 -EndChar - -StartChar: u00A1 -Encoding: 161 161 114 -Width: 200 -VWidth: 0 -Flags: HW -LayerCount: 2 -Fore -Refer: 16 60944 N 1 0 0 1 0 0 2 -EndChar - -StartChar: u00A5 -Encoding: 165 165 115 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00A6 -Encoding: 166 166 116 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00AC -Encoding: 172 172 117 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00AD -Encoding: 173 173 118 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00AF -Encoding: 175 175 119 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00B0 -Encoding: 176 176 120 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00B1 -Encoding: 177 177 121 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00B4 -Encoding: 180 180 122 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00B5 -Encoding: 181 181 123 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00B9 -Encoding: 185 185 124 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00BA -Encoding: 186 186 125 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u00BF -Encoding: 191 191 126 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2007 -Encoding: 8199 8199 127 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -EndChar - -StartChar: u2008 -Encoding: 8200 8200 128 -Width: 200 -VWidth: 0 -Flags: HW -LayerCount: 2 -EndChar - -StartChar: u200B -Encoding: 8203 8203 129 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -EndChar - -StartChar: u200C -Encoding: 8204 8204 130 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -EndChar - -StartChar: u200D -Encoding: 8205 8205 131 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -EndChar - -StartChar: u2010 -Encoding: 8208 8208 132 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2011 -Encoding: 8209 8209 133 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2012 -Encoding: 8210 8210 134 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2015 -Encoding: 8213 8213 135 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2018 -Encoding: 8216 8216 136 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2019 -Encoding: 8217 8217 137 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u201A -Encoding: 8218 8218 138 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u201B -Encoding: 8219 8219 139 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u201C -Encoding: 8220 8220 140 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u201D -Encoding: 8221 8221 141 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u201E -Encoding: 8222 8222 142 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u201F -Encoding: 8223 8223 143 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2190 -Encoding: 8592 8592 144 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2191 -Encoding: 8593 8593 145 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2192 -Encoding: 8594 8594 146 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2193 -Encoding: 8595 8595 147 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2196 -Encoding: 8598 8598 148 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2197 -Encoding: 8599 8599 149 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2198 -Encoding: 8600 8600 150 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2199 -Encoding: 8601 8601 151 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21BC -Encoding: 8636 8636 152 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21BD -Encoding: 8637 8637 153 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21BE -Encoding: 8638 8638 154 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21BF -Encoding: 8639 8639 155 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21C0 -Encoding: 8640 8640 156 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21C1 -Encoding: 8641 8641 157 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21C2 -Encoding: 8642 8642 158 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21C3 -Encoding: 8643 8643 159 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21E6 -Encoding: 8678 8678 160 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21E7 -Encoding: 8679 8679 161 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21E8 -Encoding: 8680 8680 162 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u21E9 -Encoding: 8681 8681 163 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2800 -Encoding: 10240 10240 164 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -EndChar - -StartChar: u2801 -Encoding: 10241 10241 165 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2802 -Encoding: 10242 10242 166 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2803 -Encoding: 10243 10243 167 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2804 -Encoding: 10244 10244 168 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2805 -Encoding: 10245 10245 169 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2806 -Encoding: 10246 10246 170 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2807 -Encoding: 10247 10247 171 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2808 -Encoding: 10248 10248 172 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2809 -Encoding: 10249 10249 173 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u280A -Encoding: 10250 10250 174 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u280B -Encoding: 10251 10251 175 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u280C -Encoding: 10252 10252 176 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u280D -Encoding: 10253 10253 177 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u280E -Encoding: 10254 10254 178 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u280F -Encoding: 10255 10255 179 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2810 -Encoding: 10256 10256 180 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2811 -Encoding: 10257 10257 181 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2812 -Encoding: 10258 10258 182 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2813 -Encoding: 10259 10259 183 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2814 -Encoding: 10260 10260 184 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2815 -Encoding: 10261 10261 185 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2816 -Encoding: 10262 10262 186 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2817 -Encoding: 10263 10263 187 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2818 -Encoding: 10264 10264 188 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2819 -Encoding: 10265 10265 189 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u281A -Encoding: 10266 10266 190 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u281B -Encoding: 10267 10267 191 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u281C -Encoding: 10268 10268 192 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u281D -Encoding: 10269 10269 193 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u281E -Encoding: 10270 10270 194 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u281F -Encoding: 10271 10271 195 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2820 -Encoding: 10272 10272 196 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2821 -Encoding: 10273 10273 197 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2822 -Encoding: 10274 10274 198 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2823 -Encoding: 10275 10275 199 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2824 -Encoding: 10276 10276 200 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2825 -Encoding: 10277 10277 201 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2826 -Encoding: 10278 10278 202 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2827 -Encoding: 10279 10279 203 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2828 -Encoding: 10280 10280 204 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2829 -Encoding: 10281 10281 205 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u282A -Encoding: 10282 10282 206 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u282B -Encoding: 10283 10283 207 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u282C -Encoding: 10284 10284 208 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u282D -Encoding: 10285 10285 209 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u282E -Encoding: 10286 10286 210 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u282F -Encoding: 10287 10287 211 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2830 -Encoding: 10288 10288 212 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2831 -Encoding: 10289 10289 213 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2832 -Encoding: 10290 10290 214 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2833 -Encoding: 10291 10291 215 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2834 -Encoding: 10292 10292 216 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2835 -Encoding: 10293 10293 217 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2836 -Encoding: 10294 10294 218 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2837 -Encoding: 10295 10295 219 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2838 -Encoding: 10296 10296 220 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2839 -Encoding: 10297 10297 221 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u283A -Encoding: 10298 10298 222 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u283B -Encoding: 10299 10299 223 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u283C -Encoding: 10300 10300 224 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u283D -Encoding: 10301 10301 225 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u283E -Encoding: 10302 10302 226 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u283F -Encoding: 10303 10303 227 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2840 -Encoding: 10304 10304 228 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2841 -Encoding: 10305 10305 229 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2842 -Encoding: 10306 10306 230 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2843 -Encoding: 10307 10307 231 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2844 -Encoding: 10308 10308 232 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2845 -Encoding: 10309 10309 233 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2846 -Encoding: 10310 10310 234 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2847 -Encoding: 10311 10311 235 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2848 -Encoding: 10312 10312 236 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2849 -Encoding: 10313 10313 237 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u284A -Encoding: 10314 10314 238 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u284B -Encoding: 10315 10315 239 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u284C -Encoding: 10316 10316 240 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u284D -Encoding: 10317 10317 241 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u284E -Encoding: 10318 10318 242 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u284F -Encoding: 10319 10319 243 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2850 -Encoding: 10320 10320 244 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2851 -Encoding: 10321 10321 245 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2852 -Encoding: 10322 10322 246 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2853 -Encoding: 10323 10323 247 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2854 -Encoding: 10324 10324 248 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2855 -Encoding: 10325 10325 249 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2856 -Encoding: 10326 10326 250 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2857 -Encoding: 10327 10327 251 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2858 -Encoding: 10328 10328 252 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2859 -Encoding: 10329 10329 253 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u285A -Encoding: 10330 10330 254 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u285B -Encoding: 10331 10331 255 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u285C -Encoding: 10332 10332 256 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u285D -Encoding: 10333 10333 257 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u285E -Encoding: 10334 10334 258 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u285F -Encoding: 10335 10335 259 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2860 -Encoding: 10336 10336 260 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2861 -Encoding: 10337 10337 261 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2862 -Encoding: 10338 10338 262 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2863 -Encoding: 10339 10339 263 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2864 -Encoding: 10340 10340 264 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2865 -Encoding: 10341 10341 265 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2866 -Encoding: 10342 10342 266 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2867 -Encoding: 10343 10343 267 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2868 -Encoding: 10344 10344 268 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2869 -Encoding: 10345 10345 269 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u286A -Encoding: 10346 10346 270 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u286B -Encoding: 10347 10347 271 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u286C -Encoding: 10348 10348 272 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u286D -Encoding: 10349 10349 273 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u286E -Encoding: 10350 10350 274 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u286F -Encoding: 10351 10351 275 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2870 -Encoding: 10352 10352 276 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2871 -Encoding: 10353 10353 277 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2872 -Encoding: 10354 10354 278 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2873 -Encoding: 10355 10355 279 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2874 -Encoding: 10356 10356 280 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2875 -Encoding: 10357 10357 281 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2876 -Encoding: 10358 10358 282 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2877 -Encoding: 10359 10359 283 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2878 -Encoding: 10360 10360 284 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2879 -Encoding: 10361 10361 285 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u287A -Encoding: 10362 10362 286 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u287B -Encoding: 10363 10363 287 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u287C -Encoding: 10364 10364 288 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u287D -Encoding: 10365 10365 289 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u287E -Encoding: 10366 10366 290 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u287F -Encoding: 10367 10367 291 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2880 -Encoding: 10368 10368 292 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2881 -Encoding: 10369 10369 293 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2882 -Encoding: 10370 10370 294 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2883 -Encoding: 10371 10371 295 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2884 -Encoding: 10372 10372 296 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2885 -Encoding: 10373 10373 297 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2886 -Encoding: 10374 10374 298 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2887 -Encoding: 10375 10375 299 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2888 -Encoding: 10376 10376 300 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2889 -Encoding: 10377 10377 301 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u288A -Encoding: 10378 10378 302 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u288B -Encoding: 10379 10379 303 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u288C -Encoding: 10380 10380 304 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u288D -Encoding: 10381 10381 305 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u288E -Encoding: 10382 10382 306 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u288F -Encoding: 10383 10383 307 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2890 -Encoding: 10384 10384 308 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2891 -Encoding: 10385 10385 309 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2892 -Encoding: 10386 10386 310 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2893 -Encoding: 10387 10387 311 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2894 -Encoding: 10388 10388 312 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2895 -Encoding: 10389 10389 313 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2896 -Encoding: 10390 10390 314 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2897 -Encoding: 10391 10391 315 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2898 -Encoding: 10392 10392 316 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2899 -Encoding: 10393 10393 317 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u289A -Encoding: 10394 10394 318 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u289B -Encoding: 10395 10395 319 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u289C -Encoding: 10396 10396 320 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u289D -Encoding: 10397 10397 321 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u289E -Encoding: 10398 10398 322 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u289F -Encoding: 10399 10399 323 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28A0 -Encoding: 10400 10400 324 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28A1 -Encoding: 10401 10401 325 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28A2 -Encoding: 10402 10402 326 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28A3 -Encoding: 10403 10403 327 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28A4 -Encoding: 10404 10404 328 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28A5 -Encoding: 10405 10405 329 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28A6 -Encoding: 10406 10406 330 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28A7 -Encoding: 10407 10407 331 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28A8 -Encoding: 10408 10408 332 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28A9 -Encoding: 10409 10409 333 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28AA -Encoding: 10410 10410 334 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28AB -Encoding: 10411 10411 335 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28AC -Encoding: 10412 10412 336 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28AD -Encoding: 10413 10413 337 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28AE -Encoding: 10414 10414 338 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28AF -Encoding: 10415 10415 339 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28B0 -Encoding: 10416 10416 340 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28B1 -Encoding: 10417 10417 341 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28B2 -Encoding: 10418 10418 342 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28B3 -Encoding: 10419 10419 343 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28B4 -Encoding: 10420 10420 344 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28B5 -Encoding: 10421 10421 345 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28B6 -Encoding: 10422 10422 346 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28B7 -Encoding: 10423 10423 347 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28B8 -Encoding: 10424 10424 348 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28B9 -Encoding: 10425 10425 349 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28BA -Encoding: 10426 10426 350 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28BB -Encoding: 10427 10427 351 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28BC -Encoding: 10428 10428 352 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28BD -Encoding: 10429 10429 353 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28BE -Encoding: 10430 10430 354 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28BF -Encoding: 10431 10431 355 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28C0 -Encoding: 10432 10432 356 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28C1 -Encoding: 10433 10433 357 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28C2 -Encoding: 10434 10434 358 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28C3 -Encoding: 10435 10435 359 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28C4 -Encoding: 10436 10436 360 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28C5 -Encoding: 10437 10437 361 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28C6 -Encoding: 10438 10438 362 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28C7 -Encoding: 10439 10439 363 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28C8 -Encoding: 10440 10440 364 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28C9 -Encoding: 10441 10441 365 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28CA -Encoding: 10442 10442 366 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28CB -Encoding: 10443 10443 367 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28CC -Encoding: 10444 10444 368 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28CD -Encoding: 10445 10445 369 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28CE -Encoding: 10446 10446 370 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28CF -Encoding: 10447 10447 371 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28D0 -Encoding: 10448 10448 372 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28D1 -Encoding: 10449 10449 373 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28D2 -Encoding: 10450 10450 374 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28D3 -Encoding: 10451 10451 375 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28D4 -Encoding: 10452 10452 376 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28D5 -Encoding: 10453 10453 377 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28D6 -Encoding: 10454 10454 378 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28D7 -Encoding: 10455 10455 379 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28D8 -Encoding: 10456 10456 380 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28D9 -Encoding: 10457 10457 381 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28DA -Encoding: 10458 10458 382 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28DB -Encoding: 10459 10459 383 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28DC -Encoding: 10460 10460 384 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28DD -Encoding: 10461 10461 385 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28DE -Encoding: 10462 10462 386 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28DF -Encoding: 10463 10463 387 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28E0 -Encoding: 10464 10464 388 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28E1 -Encoding: 10465 10465 389 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28E2 -Encoding: 10466 10466 390 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28E3 -Encoding: 10467 10467 391 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28E4 -Encoding: 10468 10468 392 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28E5 -Encoding: 10469 10469 393 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28E6 -Encoding: 10470 10470 394 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28E7 -Encoding: 10471 10471 395 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28E8 -Encoding: 10472 10472 396 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28E9 -Encoding: 10473 10473 397 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28EA -Encoding: 10474 10474 398 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28EB -Encoding: 10475 10475 399 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28EC -Encoding: 10476 10476 400 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28ED -Encoding: 10477 10477 401 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28EE -Encoding: 10478 10478 402 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28EF -Encoding: 10479 10479 403 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28F0 -Encoding: 10480 10480 404 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28F1 -Encoding: 10481 10481 405 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28F2 -Encoding: 10482 10482 406 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28F3 -Encoding: 10483 10483 407 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28F4 -Encoding: 10484 10484 408 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28F5 -Encoding: 10485 10485 409 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28F6 -Encoding: 10486 10486 410 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28F7 -Encoding: 10487 10487 411 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28F8 -Encoding: 10488 10488 412 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28F9 -Encoding: 10489 10489 413 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28FA -Encoding: 10490 10490 414 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28FB -Encoding: 10491 10491 415 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28FC -Encoding: 10492 10492 416 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28FD -Encoding: 10493 10493 417 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28FE -Encoding: 10494 10494 418 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u28FF -Encoding: 10495 10495 419 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 14 60942 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2B00 -Encoding: 11008 11008 420 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2B01 -Encoding: 11009 11009 421 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2B02 -Encoding: 11010 11010 422 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u2B03 -Encoding: 11011 11011 423 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3041 -Encoding: 12353 12353 424 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3042 -Encoding: 12354 12354 425 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3043 -Encoding: 12355 12355 426 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3044 -Encoding: 12356 12356 427 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3045 -Encoding: 12357 12357 428 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3046 -Encoding: 12358 12358 429 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3047 -Encoding: 12359 12359 430 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3048 -Encoding: 12360 12360 431 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3049 -Encoding: 12361 12361 432 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u304A -Encoding: 12362 12362 433 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u304B -Encoding: 12363 12363 434 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u304C -Encoding: 12364 12364 435 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u304D -Encoding: 12365 12365 436 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u304E -Encoding: 12366 12366 437 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u304F -Encoding: 12367 12367 438 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3050 -Encoding: 12368 12368 439 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3051 -Encoding: 12369 12369 440 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3052 -Encoding: 12370 12370 441 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3053 -Encoding: 12371 12371 442 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3054 -Encoding: 12372 12372 443 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3055 -Encoding: 12373 12373 444 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3056 -Encoding: 12374 12374 445 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3057 -Encoding: 12375 12375 446 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3058 -Encoding: 12376 12376 447 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3059 -Encoding: 12377 12377 448 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u305A -Encoding: 12378 12378 449 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u305B -Encoding: 12379 12379 450 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u305C -Encoding: 12380 12380 451 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u305D -Encoding: 12381 12381 452 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u305E -Encoding: 12382 12382 453 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u305F -Encoding: 12383 12383 454 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3060 -Encoding: 12384 12384 455 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3061 -Encoding: 12385 12385 456 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3062 -Encoding: 12386 12386 457 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3063 -Encoding: 12387 12387 458 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3064 -Encoding: 12388 12388 459 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3065 -Encoding: 12389 12389 460 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3066 -Encoding: 12390 12390 461 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3067 -Encoding: 12391 12391 462 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3068 -Encoding: 12392 12392 463 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3069 -Encoding: 12393 12393 464 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u306A -Encoding: 12394 12394 465 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u306B -Encoding: 12395 12395 466 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u306C -Encoding: 12396 12396 467 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u306D -Encoding: 12397 12397 468 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u306E -Encoding: 12398 12398 469 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u306F -Encoding: 12399 12399 470 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3070 -Encoding: 12400 12400 471 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3071 -Encoding: 12401 12401 472 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 0 60928 N 1 0 0 1 1632 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 10 60938 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3072 -Encoding: 12402 12402 473 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3073 -Encoding: 12403 12403 474 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3074 -Encoding: 12404 12404 475 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 0 60928 N 1 0 0 1 1632 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 10 60938 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3075 -Encoding: 12405 12405 476 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3076 -Encoding: 12406 12406 477 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3077 -Encoding: 12407 12407 478 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 0 60928 N 1 0 0 1 1632 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 10 60938 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3078 -Encoding: 12408 12408 479 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3079 -Encoding: 12409 12409 480 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u307A -Encoding: 12410 12410 481 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 0 60928 N 1 0 0 1 1632 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 10 60938 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u307B -Encoding: 12411 12411 482 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u307C -Encoding: 12412 12412 483 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u307D -Encoding: 12413 12413 484 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 0 60928 N 1 0 0 1 1632 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 10 60938 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u307E -Encoding: 12414 12414 485 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u307F -Encoding: 12415 12415 486 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3080 -Encoding: 12416 12416 487 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3081 -Encoding: 12417 12417 488 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3082 -Encoding: 12418 12418 489 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3083 -Encoding: 12419 12419 490 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3084 -Encoding: 12420 12420 491 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3085 -Encoding: 12421 12421 492 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3086 -Encoding: 12422 12422 493 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3087 -Encoding: 12423 12423 494 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3088 -Encoding: 12424 12424 495 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3089 -Encoding: 12425 12425 496 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u308A -Encoding: 12426 12426 497 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u308B -Encoding: 12427 12427 498 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u308C -Encoding: 12428 12428 499 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u308D -Encoding: 12429 12429 500 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u308E -Encoding: 12430 12430 501 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u308F -Encoding: 12431 12431 502 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3090 -Encoding: 12432 12432 503 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3091 -Encoding: 12433 12433 504 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3092 -Encoding: 12434 12434 505 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3093 -Encoding: 12435 12435 506 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3094 -Encoding: 12436 12436 507 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u3095 -Encoding: 12437 12437 508 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3096 -Encoding: 12438 12438 509 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u3099 -Encoding: 12441 12441 510 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u309A -Encoding: 12442 12442 511 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u309B -Encoding: 12443 12443 512 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u309C -Encoding: 12444 12444 513 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u309D -Encoding: 12445 12445 514 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u309E -Encoding: 12446 12446 515 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u309F -Encoding: 12447 12447 516 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30A0 -Encoding: 12448 12448 517 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30A1 -Encoding: 12449 12449 518 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30A2 -Encoding: 12450 12450 519 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30A3 -Encoding: 12451 12451 520 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30A4 -Encoding: 12452 12452 521 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30A5 -Encoding: 12453 12453 522 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30A6 -Encoding: 12454 12454 523 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30A7 -Encoding: 12455 12455 524 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30A8 -Encoding: 12456 12456 525 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30A9 -Encoding: 12457 12457 526 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30AA -Encoding: 12458 12458 527 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30AB -Encoding: 12459 12459 528 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30AC -Encoding: 12460 12460 529 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30AD -Encoding: 12461 12461 530 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30AE -Encoding: 12462 12462 531 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30AF -Encoding: 12463 12463 532 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30B0 -Encoding: 12464 12464 533 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30B1 -Encoding: 12465 12465 534 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30B2 -Encoding: 12466 12466 535 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30B3 -Encoding: 12467 12467 536 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30B4 -Encoding: 12468 12468 537 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30B5 -Encoding: 12469 12469 538 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30B6 -Encoding: 12470 12470 539 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30B7 -Encoding: 12471 12471 540 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30B8 -Encoding: 12472 12472 541 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30B9 -Encoding: 12473 12473 542 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30BA -Encoding: 12474 12474 543 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30BB -Encoding: 12475 12475 544 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30BC -Encoding: 12476 12476 545 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30BD -Encoding: 12477 12477 546 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30BE -Encoding: 12478 12478 547 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30BF -Encoding: 12479 12479 548 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30C0 -Encoding: 12480 12480 549 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30C1 -Encoding: 12481 12481 550 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30C2 -Encoding: 12482 12482 551 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30C3 -Encoding: 12483 12483 552 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30C4 -Encoding: 12484 12484 553 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30C5 -Encoding: 12485 12485 554 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30C6 -Encoding: 12486 12486 555 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30C7 -Encoding: 12487 12487 556 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30C8 -Encoding: 12488 12488 557 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30C9 -Encoding: 12489 12489 558 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30CA -Encoding: 12490 12490 559 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30CB -Encoding: 12491 12491 560 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30CC -Encoding: 12492 12492 561 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30CD -Encoding: 12493 12493 562 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30CE -Encoding: 12494 12494 563 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30CF -Encoding: 12495 12495 564 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30D0 -Encoding: 12496 12496 565 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30D1 -Encoding: 12497 12497 566 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 0 60928 N 1 0 0 1 1632 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 10 60938 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30D2 -Encoding: 12498 12498 567 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30D3 -Encoding: 12499 12499 568 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30D4 -Encoding: 12500 12500 569 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 0 60928 N 1 0 0 1 1632 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 10 60938 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30D5 -Encoding: 12501 12501 570 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30D6 -Encoding: 12502 12502 571 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30D7 -Encoding: 12503 12503 572 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 0 60928 N 1 0 0 1 1632 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 10 60938 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30D8 -Encoding: 12504 12504 573 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30D9 -Encoding: 12505 12505 574 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30DA -Encoding: 12506 12506 575 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 0 60928 N 1 0 0 1 1632 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 10 60938 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30DB -Encoding: 12507 12507 576 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30DC -Encoding: 12508 12508 577 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30DD -Encoding: 12509 12509 578 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -Refer: 0 60928 N 1 0 0 1 1632 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 10 60938 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30DE -Encoding: 12510 12510 579 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30DF -Encoding: 12511 12511 580 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30E0 -Encoding: 12512 12512 581 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30E1 -Encoding: 12513 12513 582 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30E2 -Encoding: 12514 12514 583 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30E3 -Encoding: 12515 12515 584 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30E4 -Encoding: 12516 12516 585 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30E5 -Encoding: 12517 12517 586 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30E6 -Encoding: 12518 12518 587 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30E7 -Encoding: 12519 12519 588 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30E8 -Encoding: 12520 12520 589 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30E9 -Encoding: 12521 12521 590 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30EA -Encoding: 12522 12522 591 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30EB -Encoding: 12523 12523 592 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30EC -Encoding: 12524 12524 593 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30ED -Encoding: 12525 12525 594 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30EE -Encoding: 12526 12526 595 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30EF -Encoding: 12527 12527 596 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30F0 -Encoding: 12528 12528 597 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30F1 -Encoding: 12529 12529 598 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30F2 -Encoding: 12530 12530 599 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30F3 -Encoding: 12531 12531 600 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30F4 -Encoding: 12532 12532 601 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30F5 -Encoding: 12533 12533 602 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30F6 -Encoding: 12534 12534 603 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30F7 -Encoding: 12535 12535 604 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30F8 -Encoding: 12536 12536 605 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30F9 -Encoding: 12537 12537 606 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30FA -Encoding: 12538 12538 607 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30FB -Encoding: 12539 12539 608 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 14 60942 N 1 0 0 1 0 0 2 -EndChar - -StartChar: u30FC -Encoding: 12540 12540 609 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30FD -Encoding: 12541 12541 610 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u30FE -Encoding: 12542 12542 611 -Width: 1632 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 1632 0 2 -Refer: 9 60937 N 1 0 0 1 1632 0 2 -EndChar - -StartChar: u30FF -Encoding: 12543 12543 612 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31F0 -Encoding: 12784 12784 613 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31F1 -Encoding: 12785 12785 614 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31F2 -Encoding: 12786 12786 615 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31F3 -Encoding: 12787 12787 616 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31F4 -Encoding: 12788 12788 617 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31F5 -Encoding: 12789 12789 618 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31F6 -Encoding: 12790 12790 619 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31F7 -Encoding: 12791 12791 620 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31F8 -Encoding: 12792 12792 621 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31F9 -Encoding: 12793 12793 622 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31FA -Encoding: 12794 12794 623 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31FB -Encoding: 12795 12795 624 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31FC -Encoding: 12796 12796 625 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31FD -Encoding: 12797 12797 626 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31FE -Encoding: 12798 12798 627 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u31FF -Encoding: 12799 12799 628 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uEE12 -Encoding: 60946 60946 629 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uEE13 -Encoding: 60947 60947 630 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uEE14 -Encoding: 60948 60948 631 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF01 -Encoding: 65281 65281 632 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -EndChar - -StartChar: uFF02 -Encoding: 65282 65282 633 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF03 -Encoding: 65283 65283 634 -Width: 200 -VWidth: 0 -Flags: HW -LayerCount: 2 -Fore -Refer: 17 60945 N 1 0 0 1 0 0 2 -EndChar - -StartChar: uFF04 -Encoding: 65284 65284 635 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF05 -Encoding: 65285 65285 636 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF06 -Encoding: 65286 65286 637 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF07 -Encoding: 65287 65287 638 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF08 -Encoding: 65288 65288 639 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF09 -Encoding: 65289 65289 640 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF0A -Encoding: 65290 65290 641 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF0B -Encoding: 65291 65291 642 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF0C -Encoding: 65292 65292 643 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF0D -Encoding: 65293 65293 644 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF0E -Encoding: 65294 65294 645 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 14 60942 N 1 0 0 1 0 0 2 -EndChar - -StartChar: uFF0F -Encoding: 65295 65295 646 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF10 -Encoding: 65296 65296 647 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF11 -Encoding: 65297 65297 648 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF12 -Encoding: 65298 65298 649 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF13 -Encoding: 65299 65299 650 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF14 -Encoding: 65300 65300 651 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF15 -Encoding: 65301 65301 652 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF16 -Encoding: 65302 65302 653 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF17 -Encoding: 65303 65303 654 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF18 -Encoding: 65304 65304 655 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF19 -Encoding: 65305 65305 656 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF1A -Encoding: 65306 65306 657 -Width: 200 -VWidth: 0 -Flags: HW -LayerCount: 2 -Fore -Refer: 16 60944 N 1 0 0 1 0 0 2 -Refer: 17 60945 N 1 0 0 1 0 0 2 -EndChar - -StartChar: uFF1B -Encoding: 65307 65307 658 -Width: 200 -VWidth: 0 -Flags: HW -LayerCount: 2 -Fore -Refer: 16 60944 N 1 0 0 1 0 0 2 -EndChar - -StartChar: uFF1C -Encoding: 65308 65308 659 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF1D -Encoding: 65309 65309 660 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF1E -Encoding: 65310 65310 661 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF1F -Encoding: 65311 65311 662 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF20 -Encoding: 65312 65312 663 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF21 -Encoding: 65313 65313 664 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF22 -Encoding: 65314 65314 665 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF23 -Encoding: 65315 65315 666 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF24 -Encoding: 65316 65316 667 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF25 -Encoding: 65317 65317 668 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF26 -Encoding: 65318 65318 669 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF27 -Encoding: 65319 65319 670 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF28 -Encoding: 65320 65320 671 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF29 -Encoding: 65321 65321 672 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF2A -Encoding: 65322 65322 673 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF2B -Encoding: 65323 65323 674 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF2C -Encoding: 65324 65324 675 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF2D -Encoding: 65325 65325 676 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF2E -Encoding: 65326 65326 677 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF2F -Encoding: 65327 65327 678 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF30 -Encoding: 65328 65328 679 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF31 -Encoding: 65329 65329 680 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF32 -Encoding: 65330 65330 681 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF33 -Encoding: 65331 65331 682 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF34 -Encoding: 65332 65332 683 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF35 -Encoding: 65333 65333 684 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF36 -Encoding: 65334 65334 685 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF37 -Encoding: 65335 65335 686 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF38 -Encoding: 65336 65336 687 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF39 -Encoding: 65337 65337 688 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF3A -Encoding: 65338 65338 689 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF3B -Encoding: 65339 65339 690 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF3C -Encoding: 65340 65340 691 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF3D -Encoding: 65341 65341 692 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF3E -Encoding: 65342 65342 693 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF3F -Encoding: 65343 65343 694 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF40 -Encoding: 65344 65344 695 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF41 -Encoding: 65345 65345 696 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF42 -Encoding: 65346 65346 697 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF43 -Encoding: 65347 65347 698 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF44 -Encoding: 65348 65348 699 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF45 -Encoding: 65349 65349 700 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF46 -Encoding: 65350 65350 701 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF47 -Encoding: 65351 65351 702 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF48 -Encoding: 65352 65352 703 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF49 -Encoding: 65353 65353 704 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF4A -Encoding: 65354 65354 705 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF4B -Encoding: 65355 65355 706 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF4C -Encoding: 65356 65356 707 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF4D -Encoding: 65357 65357 708 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF4E -Encoding: 65358 65358 709 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF4F -Encoding: 65359 65359 710 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF50 -Encoding: 65360 65360 711 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF51 -Encoding: 65361 65361 712 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF52 -Encoding: 65362 65362 713 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF53 -Encoding: 65363 65363 714 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF54 -Encoding: 65364 65364 715 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF55 -Encoding: 65365 65365 716 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF56 -Encoding: 65366 65366 717 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF57 -Encoding: 65367 65367 718 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF58 -Encoding: 65368 65368 719 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF59 -Encoding: 65369 65369 720 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF5A -Encoding: 65370 65370 721 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF5B -Encoding: 65371 65371 722 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF5C -Encoding: 65372 65372 723 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF5D -Encoding: 65373 65373 724 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF5E -Encoding: 65374 65374 725 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF65 -Encoding: 65381 65381 726 -Width: 0 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 14 60942 N 1 0 0 1 0 0 2 -EndChar - -StartChar: uFF66 -Encoding: 65382 65382 727 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF67 -Encoding: 65383 65383 728 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF68 -Encoding: 65384 65384 729 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF69 -Encoding: 65385 65385 730 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF6A -Encoding: 65386 65386 731 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF6B -Encoding: 65387 65387 732 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF6C -Encoding: 65388 65388 733 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF6D -Encoding: 65389 65389 734 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF6E -Encoding: 65390 65390 735 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF6F -Encoding: 65391 65391 736 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF70 -Encoding: 65392 65392 737 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF71 -Encoding: 65393 65393 738 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF72 -Encoding: 65394 65394 739 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF73 -Encoding: 65395 65395 740 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF74 -Encoding: 65396 65396 741 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF75 -Encoding: 65397 65397 742 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF76 -Encoding: 65398 65398 743 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF77 -Encoding: 65399 65399 744 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF78 -Encoding: 65400 65400 745 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF79 -Encoding: 65401 65401 746 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF7A -Encoding: 65402 65402 747 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF7B -Encoding: 65403 65403 748 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF7C -Encoding: 65404 65404 749 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF7D -Encoding: 65405 65405 750 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF7E -Encoding: 65406 65406 751 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF7F -Encoding: 65407 65407 752 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF80 -Encoding: 65408 65408 753 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF81 -Encoding: 65409 65409 754 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF82 -Encoding: 65410 65410 755 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF83 -Encoding: 65411 65411 756 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF84 -Encoding: 65412 65412 757 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF85 -Encoding: 65413 65413 758 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF86 -Encoding: 65414 65414 759 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF87 -Encoding: 65415 65415 760 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF88 -Encoding: 65416 65416 761 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF89 -Encoding: 65417 65417 762 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF8A -Encoding: 65418 65418 763 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF8B -Encoding: 65419 65419 764 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF8C -Encoding: 65420 65420 765 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF8D -Encoding: 65421 65421 766 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF8E -Encoding: 65422 65422 767 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF8F -Encoding: 65423 65423 768 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF90 -Encoding: 65424 65424 769 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF91 -Encoding: 65425 65425 770 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF92 -Encoding: 65426 65426 771 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF93 -Encoding: 65427 65427 772 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF94 -Encoding: 65428 65428 773 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF95 -Encoding: 65429 65429 774 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF96 -Encoding: 65430 65430 775 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF97 -Encoding: 65431 65431 776 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF98 -Encoding: 65432 65432 777 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF99 -Encoding: 65433 65433 778 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF9A -Encoding: 65434 65434 779 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF9B -Encoding: 65435 65435 780 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF9C -Encoding: 65436 65436 781 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF9D -Encoding: 65437 65437 782 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF9E -Encoding: 65438 65438 783 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFF9F -Encoding: 65439 65439 784 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFFE2 -Encoding: 65506 65506 785 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFFE3 -Encoding: 65507 65507 786 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFFE4 -Encoding: 65508 65508 787 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFFE5 -Encoding: 65509 65509 788 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFFE8 -Encoding: 65512 65512 789 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFFE9 -Encoding: 65513 65513 790 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFFEA -Encoding: 65514 65514 791 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFFEB -Encoding: 65515 65515 792 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uFFEC -Encoding: 65516 65516 793 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 8 60936 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1B000 -Encoding: 110592 110592 794 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1B001 -Encoding: 110593 110593 795 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -Refer: 13 60941 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1B150 -Encoding: 110928 110928 796 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1B151 -Encoding: 110929 110929 797 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1B152 -Encoding: 110930 110930 798 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1B164 -Encoding: 110948 110948 799 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1B165 -Encoding: 110949 110949 800 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 9 60937 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 12 60940 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1B166 -Encoding: 110950 110950 801 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 10 60938 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1B167 -Encoding: 110951 110951 802 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 11 60939 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1FBF0 -Encoding: 130032 130032 803 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1FBF1 -Encoding: 130033 130033 804 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1FBF2 -Encoding: 130034 130034 805 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1FBF3 -Encoding: 130035 130035 806 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1FBF4 -Encoding: 130036 130036 807 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1FBF5 -Encoding: 130037 130037 808 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1FBF6 -Encoding: 130038 130038 809 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1FBF7 -Encoding: 130039 130039 810 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1FBF8 -Encoding: 130040 130040 811 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 4 60932 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: u1FBF9 -Encoding: 130041 130041 812 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -Fore -Refer: 0 60928 N 1 0 0 1 816 0 2 -Refer: 1 60929 N 1 0 0 1 816 0 2 -Refer: 2 60930 N 1 0 0 1 816 0 2 -Refer: 3 60931 N 1 0 0 1 816 0 2 -Refer: 5 60933 N 1 0 0 1 816 0 2 -Refer: 6 60934 N 1 0 0 1 816 0 2 -Refer: 7 60935 N 1 0 0 1 816 0 2 -EndChar - -StartChar: uni0009 -Encoding: 9 9 813 -Width: 816 -VWidth: 200 -Flags: HW -LayerCount: 2 -EndChar -EndChars -EndSplineFont +SplineFontDB: 3.2 +FontName: DSEG14Classic-Italic +FullName: DSEG14 Classic-Italic +FamilyName: DSEG14 Classic +Weight: Regular +Copyright: Created by Keshikan(https://twitter.com/keshinomi_88pro)\nwith FontForge 2.0 (http://fontforge.sf.net) +UComments: "2014-8-31: Created." +Version: 0.46 +ItalicAngle: -5 +UnderlinePosition: -100 +UnderlineWidth: 50 +Ascent: 1000 +Descent: 0 +InvalidEm: 0 +LayerCount: 2 +Layer: 0 0 "+gMyXYgAA" 1 +Layer: 1 0 "+Uk2XYgAA" 0 +XUID: [1021 682 390630330 14528854] +FSType: 8 +OS2Version: 0 +OS2_WeightWidthSlopeOnly: 0 +OS2_UseTypoMetrics: 1 +CreationTime: 1409488158 +ModificationTime: 1681891359 +PfmFamily: 17 +TTFWeight: 400 +TTFWidth: 5 +LineGap: 90 +VLineGap: 0 +OS2TypoAscent: 0 +OS2TypoAOffset: 1 +OS2TypoDescent: 0 +OS2TypoDOffset: 1 +OS2TypoLinegap: 90 +OS2WinAscent: 0 +OS2WinAOffset: 1 +OS2WinDescent: 0 +OS2WinDOffset: 1 +HheadAscent: 0 +HheadAOffset: 1 +HheadDescent: 0 +HheadDOffset: 1 +OS2Vendor: 'PfEd' +MarkAttachClasses: 1 +DEI: 91125 +LangName: 1033 "Created by Keshikan+AAoA-with FontForge 2.0 (http://fontforge.sf.net)" "" "" "" "" "Version 0.46" "" "" "" "Keshikan(Twitter:@keshinomi_88pro)" "" "" "http://www.keshikan.net" "Copyright (c) 2018, keshikan (http://www.keshikan.net),+AAoA-with Reserved Font Name +ACIA-DSEG+ACIA.+AAoACgAA-This Font Software is licensed under the SIL Open Font License, Version 1.1.+AAoA-This license is copied below, and is also available with a FAQ at:+AAoA-http://scripts.sil.org/OFL+AAoACgAK------------------------------------------------------------+AAoA-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007+AAoA------------------------------------------------------------+AAoACgAA-PREAMBLE+AAoA-The goals of the Open Font License (OFL) are to stimulate worldwide+AAoA-development of collaborative font projects, to support the font creation+AAoA-efforts of academic and linguistic communities, and to provide a free and+AAoA-open framework in which fonts may be shared and improved in partnership+AAoA-with others.+AAoACgAA-The OFL allows the licensed fonts to be used, studied, modified and+AAoA-redistributed freely as long as they are not sold by themselves. The+AAoA-fonts, including any derivative works, can be bundled, embedded, +AAoA-redistributed and/or sold with any software provided that any reserved+AAoA-names are not used by derivative works. The fonts and derivatives,+AAoA-however, cannot be released under any other type of license. The+AAoA-requirement for fonts to remain under this license does not apply+AAoA-to any document created using the fonts or their derivatives.+AAoACgAA-DEFINITIONS+AAoAIgAA-Font Software+ACIA refers to the set of files released by the Copyright+AAoA-Holder(s) under this license and clearly marked as such. This may+AAoA-include source files, build scripts and documentation.+AAoACgAi-Reserved Font Name+ACIA refers to any names specified as such after the+AAoA-copyright statement(s).+AAoACgAi-Original Version+ACIA refers to the collection of Font Software components as+AAoA-distributed by the Copyright Holder(s).+AAoACgAi-Modified Version+ACIA refers to any derivative made by adding to, deleting,+AAoA-or substituting -- in part or in whole -- any of the components of the+AAoA-Original Version, by changing formats or by porting the Font Software to a+AAoA-new environment.+AAoACgAi-Author+ACIA refers to any designer, engineer, programmer, technical+AAoA-writer or other person who contributed to the Font Software.+AAoACgAA-PERMISSION & CONDITIONS+AAoA-Permission is hereby granted, free of charge, to any person obtaining+AAoA-a copy of the Font Software, to use, study, copy, merge, embed, modify,+AAoA-redistribute, and sell modified and unmodified copies of the Font+AAoA-Software, subject to the following conditions:+AAoACgAA-1) Neither the Font Software nor any of its individual components,+AAoA-in Original or Modified Versions, may be sold by itself.+AAoACgAA-2) Original or Modified Versions of the Font Software may be bundled,+AAoA-redistributed and/or sold with any software, provided that each copy+AAoA-contains the above copyright notice and this license. These can be+AAoA-included either as stand-alone text files, human-readable headers or+AAoA-in the appropriate machine-readable metadata fields within text or+AAoA-binary files as long as those fields can be easily viewed by the user.+AAoACgAA-3) No Modified Version of the Font Software may use the Reserved Font+AAoA-Name(s) unless explicit written permission is granted by the corresponding+AAoA-Copyright Holder. This restriction only applies to the primary font name as+AAoA-presented to the users.+AAoACgAA-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font+AAoA-Software shall not be used to promote, endorse or advertise any+AAoA-Modified Version, except to acknowledge the contribution(s) of the+AAoA-Copyright Holder(s) and the Author(s) or with their explicit written+AAoA-permission.+AAoACgAA-5) The Font Software, modified or unmodified, in part or in whole,+AAoA-must be distributed entirely under this license, and must not be+AAoA-distributed under any other license. The requirement for fonts to+AAoA-remain under this license does not apply to any document created+AAoA-using the Font Software.+AAoACgAA-TERMINATION+AAoA-This license becomes null and void if any of the above conditions are+AAoA-not met.+AAoACgAA-DISCLAIMER+AAoA-THE FONT SOFTWARE IS PROVIDED +ACIA-AS IS+ACIA, WITHOUT WARRANTY OF ANY KIND,+AAoA-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF+AAoA-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT+AAoA-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE+AAoA-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+AAoA-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL+AAoA-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+AAoA-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM+AAoA-OTHER DEALINGS IN THE FONT SOFTWARE." "http://scripts.sil.org/OFL" "" "" "" "" "DSEG14 12:34" +Encoding: UnicodeFull +UnicodeInterp: none +NameList: Adobe Glyph List +DisplaySize: -48 +AntiAlias: 1 +FitToEm: 1 +WinInfo: 0 32 9 +BeginPrivate: 0 +EndPrivate +TeXData: 1 0 0 209715 104857 69905 930087 1048576 69905 783286 444596 497025 792723 393216 433062 380633 303038 157286 324010 404750 52429 2506097 1059062 262144 +BeginChars: 1114112 814 + +StartChar: uEE00 +Encoding: 60928 60928 0 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-631 969 m 1 + -597 1000 l 1 + -131 1000 l 1 + -103 969 l 1 + -170 907 l 1 + -190 907 l 1 + -326 907 l 1 + -418 907 l 1 + -554 907 l 1 + -574 907 l 1 + -631 969 l 1 +EndSplineSet +EndChar + +StartChar: uEE01 +Encoding: 60929 60929 1 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-90 955 m 1 + -62 924 l 1 + -98 510 l 1 + -125 510 l 1 + -130 514 l 1 + -185 575 l 1 + -183 607 l 1 + -167 782 l 1 + -158 887 l 1 + -158 893 l 1 + -90 955 l 1 +EndSplineSet +EndChar + +StartChar: uEE02 +Encoding: 60930 60930 2 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-132 485 m 1 + -127 489 l 1 + -100 489 l 1 + -136 76 l 1 + -170 45 l 1 + -226 107 l 1 + -226 113 l 1 + -217 218 l 1 + -199 424 l 1 + -132 485 l 1 +EndSplineSet +EndChar + +StartChar: uEE03 +Encoding: 60931 60931 3 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-185 31 m 1 + -219 0 l 1 + -685 0 l 1 + -713 31 l 1 + -646 93 l 1 + -626 93 l 1 + -490 93 l 1 + -398 93 l 1 + -262 93 l 1 + -242 93 l 1 + -185 31 l 1 +EndSplineSet +EndChar + +StartChar: uEE04 +Encoding: 60932 60932 4 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-726 45 m 1 + -754 76 l 1 + -718 490 l 1 + -691 490 l 1 + -687 485 l 1 + -631 424 l 1 + -649 218 l 1 + -658 113 l 1 + -658 107 l 1 + -726 45 l 1 +EndSplineSet +EndChar + +StartChar: uEE05 +Encoding: 60933 60933 5 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-685 515 m 1 + -689 510 l 1 + -716 510 l 1 + -680 924 l 1 + -646 955 l 1 + -590 893 l 1 + -590 887 l 1 + -599 782 l 1 + -617 576 l 1 + -685 515 l 1 +EndSplineSet +EndChar + +StartChar: uEE06 +Encoding: 60934 60934 6 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-672 500 m 1 + -620 547 l 1 + -620 546 l 1 + -484 546 l 1 + -443 546 l 1 + -420 500 l 1 + -451 454 l 1 + -608 454 l 1 + -611 454 l 1 + -628 454 l 1 + -628 453 l 1 + -672 500 l 1 +EndSplineSet +EndChar + +StartChar: uEE07 +Encoding: 60935 60935 7 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-188 546 m 1 + -145 499 l 1 + -196 453 l 1 + -196 454 l 1 + -214 454 l 1 + -216 454 l 1 + -345 454 l 1 + -366 454 l 1 + -373 454 l 1 + -396 500 l 1 + -365 546 l 1 + -358 546 l 1 + -337 546 l 1 + -324 546 l 1 + -188 546 l 1 +EndSplineSet +EndChar + +StartChar: uEE08 +Encoding: 60936 60936 8 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-469 567 m 1 + -494 567 l 1 + -582 746 l 1 + -570 887 l 1 + -544 887 l 1 + -457 709 l 1 + -469 567 l 1 +EndSplineSet +EndChar + +StartChar: uEE09 +Encoding: 60937 60937 9 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-439 673 m 1 + -420 887 l 1 + -328 887 l 1 + -329 876 l 1 + -347 673 l 1 + -353 601 l 1 + -406 521 l 1 + -445 601 l 1 + -439 673 l 1 +EndSplineSet +EndChar + +StartChar: uEE0A +Encoding: 60938 60938 10 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-204 887 m 1 + -178 887 l 1 + -190 746 l 1 + -310 567 l 1 + -335 567 l 1 + -323 709 l 1 + -204 887 l 1 +EndSplineSet +EndChar + +StartChar: uEE0B +Encoding: 60939 60939 11 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-493 291 m 1 + -612 113 l 1 + -638 113 l 1 + -626 254 l 1 + -506 433 l 1 + -481 433 l 1 + -493 291 l 1 +EndSplineSet +EndChar + +StartChar: uEE0C +Encoding: 60940 60940 12 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-392 157 m 1 + -396 113 l 1 + -488 113 l 1 + -469 327 l 1 + -463 399 l 1 + -410 479 l 1 + -371 399 l 1 + -377 327 l 1 + -392 157 l 1 +EndSplineSet +EndChar + +StartChar: uEE0D +Encoding: 60941 60941 13 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +-347 433 m 1 + -322 433 l 1 + -234 254 l 1 + -246 113 l 1 + -272 113 l 1 + -359 291 l 1 + -347 433 l 1 +EndSplineSet +EndChar + +StartChar: uEE0E +Encoding: 60942 60942 14 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +SplineSet +18 62 m 0 + 18 53 16 45 13 38 c 0 + 10 31 6 24 0 18 c 0 + -6 12 -13 8 -20 5 c 0 + -27 2 -35 0 -44 0 c 0 + -53 0 -61 2 -68 5 c 0 + -75 8 -82 12 -88 18 c 0 + -94 24 -98 31 -101 38 c 0 + -104 45 -106 53 -106 62 c 0 + -106 71 -104 79 -101 86 c 0 + -98 93 -94 100 -88 106 c 0 + -82 112 -75 116 -68 119 c 0 + -61 122 -53 124 -44 124 c 0 + -35 124 -27 122 -20 119 c 0 + -13 116 -6 112 0 106 c 0 + 6 100 10 93 13 86 c 0 + 16 79 18 71 18 62 c 0 +EndSplineSet +EndChar + +StartChar: uEE0F +Encoding: 60943 60943 15 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +EndChar + +StartChar: uEE10 +Encoding: 60944 60944 16 +Width: 200 +VWidth: 0 +Flags: HW +LayerCount: 2 +Fore +SplineSet +180 693 m 0 + 180 684 178 676 175 669 c 0 + 172 662 168 655 162 649 c 0 + 156 643 149 639 142 636 c 0 + 135 633 127 631 118 631 c 0 + 109 631 101 633 94 636 c 0 + 87 639 80 643 74 649 c 0 + 68 655 64 662 61 669 c 0 + 58 676 56 684 56 693 c 0 + 56 702 58 710 61 717 c 0 + 64 724 68 730 74 736 c 0 + 80 742 87 747 94 750 c 0 + 101 753 109 754 118 754 c 0 + 127 754 135 753 142 750 c 0 + 149 747 156 742 162 736 c 0 + 168 730 172 724 175 717 c 0 + 178 710 180 702 180 693 c 0 +EndSplineSet +EndChar + +StartChar: uEE11 +Encoding: 60945 60945 17 +Width: 200 +VWidth: 0 +Flags: HW +LayerCount: 2 +Fore +SplineSet +144 281 m 0 + 144 272 142 264 139 257 c 0 + 136 250 132 243 126 237 c 0 + 120 231 113 227 106 224 c 0 + 99 221 91 219 82 219 c 0 + 73 219 65 221 58 224 c 0 + 51 227 44 231 38 237 c 0 + 32 243 28 250 25 257 c 0 + 22 264 20 272 20 281 c 0 + 20 290 22 298 25 305 c 0 + 28 312 32 318 38 324 c 0 + 44 330 51 335 58 338 c 0 + 65 341 73 342 82 342 c 0 + 91 342 99 341 106 338 c 0 + 113 335 120 330 126 324 c 0 + 132 318 136 312 139 305 c 0 + 142 298 144 290 144 281 c 0 +EndSplineSet +EndChar + +StartChar: u0020 +Encoding: 32 32 18 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +EndChar + +StartChar: u0021 +Encoding: 33 33 19 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +EndChar + +StartChar: u0022 +Encoding: 34 34 20 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0023 +Encoding: 35 35 21 +Width: 200 +VWidth: 0 +Flags: HW +LayerCount: 2 +Fore +Refer: 17 60945 N 1 0 0 1 0 0 2 +EndChar + +StartChar: u0024 +Encoding: 36 36 22 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0025 +Encoding: 37 37 23 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0026 +Encoding: 38 38 24 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0027 +Encoding: 39 39 25 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0028 +Encoding: 40 40 26 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0029 +Encoding: 41 41 27 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u002A +Encoding: 42 42 28 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u002B +Encoding: 43 43 29 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u002C +Encoding: 44 44 30 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u002D +Encoding: 45 45 31 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u002E +Encoding: 46 46 32 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 14 60942 N 1 0 0 1 0 0 2 +EndChar + +StartChar: u002F +Encoding: 47 47 33 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0030 +Encoding: 48 48 34 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0031 +Encoding: 49 49 35 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0032 +Encoding: 50 50 36 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0033 +Encoding: 51 51 37 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0034 +Encoding: 52 52 38 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0035 +Encoding: 53 53 39 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0036 +Encoding: 54 54 40 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0037 +Encoding: 55 55 41 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0038 +Encoding: 56 56 42 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0039 +Encoding: 57 57 43 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u003A +Encoding: 58 58 44 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u003B +Encoding: 59 59 45 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u003C +Encoding: 60 60 46 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u003D +Encoding: 61 61 47 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u003E +Encoding: 62 62 48 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u003F +Encoding: 63 63 49 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0040 +Encoding: 64 64 50 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0041 +Encoding: 65 65 51 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0042 +Encoding: 66 66 52 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0043 +Encoding: 67 67 53 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0044 +Encoding: 68 68 54 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0045 +Encoding: 69 69 55 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0046 +Encoding: 70 70 56 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0047 +Encoding: 71 71 57 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0048 +Encoding: 72 72 58 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0049 +Encoding: 73 73 59 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u004A +Encoding: 74 74 60 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u004B +Encoding: 75 75 61 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u004C +Encoding: 76 76 62 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u004D +Encoding: 77 77 63 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u004E +Encoding: 78 78 64 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u004F +Encoding: 79 79 65 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0050 +Encoding: 80 80 66 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0051 +Encoding: 81 81 67 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0052 +Encoding: 82 82 68 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0053 +Encoding: 83 83 69 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0054 +Encoding: 84 84 70 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0055 +Encoding: 85 85 71 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0056 +Encoding: 86 86 72 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0057 +Encoding: 87 87 73 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0058 +Encoding: 88 88 74 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0059 +Encoding: 89 89 75 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u005A +Encoding: 90 90 76 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u005B +Encoding: 91 91 77 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u005C +Encoding: 92 92 78 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u005D +Encoding: 93 93 79 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u005E +Encoding: 94 94 80 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u005F +Encoding: 95 95 81 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0060 +Encoding: 96 96 82 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0061 +Encoding: 97 97 83 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0062 +Encoding: 98 98 84 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0063 +Encoding: 99 99 85 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0064 +Encoding: 100 100 86 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0065 +Encoding: 101 101 87 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0066 +Encoding: 102 102 88 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0067 +Encoding: 103 103 89 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0068 +Encoding: 104 104 90 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0069 +Encoding: 105 105 91 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u006A +Encoding: 106 106 92 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u006B +Encoding: 107 107 93 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u006C +Encoding: 108 108 94 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u006D +Encoding: 109 109 95 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 14 60942 N 1 0 0 1 0 0 2 +EndChar + +StartChar: u006E +Encoding: 110 110 96 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u006F +Encoding: 111 111 97 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0070 +Encoding: 112 112 98 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0071 +Encoding: 113 113 99 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0072 +Encoding: 114 114 100 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0073 +Encoding: 115 115 101 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0074 +Encoding: 116 116 102 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0075 +Encoding: 117 117 103 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0076 +Encoding: 118 118 104 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0077 +Encoding: 119 119 105 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0078 +Encoding: 120 120 106 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u0079 +Encoding: 121 121 107 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u007A +Encoding: 122 122 108 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u007B +Encoding: 123 123 109 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u007C +Encoding: 124 124 110 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u007D +Encoding: 125 125 111 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u007E +Encoding: 126 126 112 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 S 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00A0 +Encoding: 160 160 113 +Width: 200 +VWidth: 0 +Flags: HW +LayerCount: 2 +EndChar + +StartChar: u00A1 +Encoding: 161 161 114 +Width: 200 +VWidth: 0 +Flags: HW +LayerCount: 2 +Fore +Refer: 16 60944 N 1 0 0 1 0 0 2 +EndChar + +StartChar: u00A5 +Encoding: 165 165 115 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00A6 +Encoding: 166 166 116 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00AC +Encoding: 172 172 117 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00AD +Encoding: 173 173 118 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00AF +Encoding: 175 175 119 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00B0 +Encoding: 176 176 120 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00B1 +Encoding: 177 177 121 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00B4 +Encoding: 180 180 122 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00B5 +Encoding: 181 181 123 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00B9 +Encoding: 185 185 124 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00BA +Encoding: 186 186 125 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u00BF +Encoding: 191 191 126 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2007 +Encoding: 8199 8199 127 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +EndChar + +StartChar: u2008 +Encoding: 8200 8200 128 +Width: 200 +VWidth: 0 +Flags: HW +LayerCount: 2 +EndChar + +StartChar: u200B +Encoding: 8203 8203 129 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +EndChar + +StartChar: u200C +Encoding: 8204 8204 130 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +EndChar + +StartChar: u200D +Encoding: 8205 8205 131 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +EndChar + +StartChar: u2010 +Encoding: 8208 8208 132 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2011 +Encoding: 8209 8209 133 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2012 +Encoding: 8210 8210 134 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2015 +Encoding: 8213 8213 135 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2018 +Encoding: 8216 8216 136 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2019 +Encoding: 8217 8217 137 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u201A +Encoding: 8218 8218 138 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u201B +Encoding: 8219 8219 139 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u201C +Encoding: 8220 8220 140 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u201D +Encoding: 8221 8221 141 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u201E +Encoding: 8222 8222 142 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u201F +Encoding: 8223 8223 143 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2190 +Encoding: 8592 8592 144 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2191 +Encoding: 8593 8593 145 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2192 +Encoding: 8594 8594 146 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2193 +Encoding: 8595 8595 147 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2196 +Encoding: 8598 8598 148 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2197 +Encoding: 8599 8599 149 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2198 +Encoding: 8600 8600 150 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2199 +Encoding: 8601 8601 151 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21BC +Encoding: 8636 8636 152 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21BD +Encoding: 8637 8637 153 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21BE +Encoding: 8638 8638 154 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21BF +Encoding: 8639 8639 155 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21C0 +Encoding: 8640 8640 156 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21C1 +Encoding: 8641 8641 157 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21C2 +Encoding: 8642 8642 158 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21C3 +Encoding: 8643 8643 159 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21E6 +Encoding: 8678 8678 160 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21E7 +Encoding: 8679 8679 161 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21E8 +Encoding: 8680 8680 162 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u21E9 +Encoding: 8681 8681 163 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2800 +Encoding: 10240 10240 164 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +EndChar + +StartChar: u2801 +Encoding: 10241 10241 165 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2802 +Encoding: 10242 10242 166 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2803 +Encoding: 10243 10243 167 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2804 +Encoding: 10244 10244 168 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2805 +Encoding: 10245 10245 169 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2806 +Encoding: 10246 10246 170 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2807 +Encoding: 10247 10247 171 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2808 +Encoding: 10248 10248 172 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2809 +Encoding: 10249 10249 173 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u280A +Encoding: 10250 10250 174 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u280B +Encoding: 10251 10251 175 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u280C +Encoding: 10252 10252 176 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u280D +Encoding: 10253 10253 177 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u280E +Encoding: 10254 10254 178 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u280F +Encoding: 10255 10255 179 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2810 +Encoding: 10256 10256 180 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2811 +Encoding: 10257 10257 181 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2812 +Encoding: 10258 10258 182 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2813 +Encoding: 10259 10259 183 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2814 +Encoding: 10260 10260 184 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2815 +Encoding: 10261 10261 185 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2816 +Encoding: 10262 10262 186 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2817 +Encoding: 10263 10263 187 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2818 +Encoding: 10264 10264 188 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2819 +Encoding: 10265 10265 189 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u281A +Encoding: 10266 10266 190 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u281B +Encoding: 10267 10267 191 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u281C +Encoding: 10268 10268 192 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u281D +Encoding: 10269 10269 193 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u281E +Encoding: 10270 10270 194 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u281F +Encoding: 10271 10271 195 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2820 +Encoding: 10272 10272 196 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2821 +Encoding: 10273 10273 197 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2822 +Encoding: 10274 10274 198 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2823 +Encoding: 10275 10275 199 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2824 +Encoding: 10276 10276 200 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2825 +Encoding: 10277 10277 201 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2826 +Encoding: 10278 10278 202 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2827 +Encoding: 10279 10279 203 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2828 +Encoding: 10280 10280 204 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2829 +Encoding: 10281 10281 205 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u282A +Encoding: 10282 10282 206 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u282B +Encoding: 10283 10283 207 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u282C +Encoding: 10284 10284 208 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u282D +Encoding: 10285 10285 209 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u282E +Encoding: 10286 10286 210 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u282F +Encoding: 10287 10287 211 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2830 +Encoding: 10288 10288 212 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2831 +Encoding: 10289 10289 213 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2832 +Encoding: 10290 10290 214 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2833 +Encoding: 10291 10291 215 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2834 +Encoding: 10292 10292 216 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2835 +Encoding: 10293 10293 217 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2836 +Encoding: 10294 10294 218 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2837 +Encoding: 10295 10295 219 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2838 +Encoding: 10296 10296 220 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2839 +Encoding: 10297 10297 221 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u283A +Encoding: 10298 10298 222 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u283B +Encoding: 10299 10299 223 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u283C +Encoding: 10300 10300 224 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u283D +Encoding: 10301 10301 225 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u283E +Encoding: 10302 10302 226 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u283F +Encoding: 10303 10303 227 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2840 +Encoding: 10304 10304 228 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2841 +Encoding: 10305 10305 229 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2842 +Encoding: 10306 10306 230 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2843 +Encoding: 10307 10307 231 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2844 +Encoding: 10308 10308 232 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2845 +Encoding: 10309 10309 233 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2846 +Encoding: 10310 10310 234 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2847 +Encoding: 10311 10311 235 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2848 +Encoding: 10312 10312 236 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2849 +Encoding: 10313 10313 237 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u284A +Encoding: 10314 10314 238 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u284B +Encoding: 10315 10315 239 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u284C +Encoding: 10316 10316 240 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u284D +Encoding: 10317 10317 241 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u284E +Encoding: 10318 10318 242 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u284F +Encoding: 10319 10319 243 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2850 +Encoding: 10320 10320 244 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2851 +Encoding: 10321 10321 245 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2852 +Encoding: 10322 10322 246 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2853 +Encoding: 10323 10323 247 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2854 +Encoding: 10324 10324 248 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2855 +Encoding: 10325 10325 249 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2856 +Encoding: 10326 10326 250 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2857 +Encoding: 10327 10327 251 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2858 +Encoding: 10328 10328 252 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2859 +Encoding: 10329 10329 253 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u285A +Encoding: 10330 10330 254 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u285B +Encoding: 10331 10331 255 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u285C +Encoding: 10332 10332 256 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u285D +Encoding: 10333 10333 257 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u285E +Encoding: 10334 10334 258 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u285F +Encoding: 10335 10335 259 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2860 +Encoding: 10336 10336 260 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2861 +Encoding: 10337 10337 261 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2862 +Encoding: 10338 10338 262 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2863 +Encoding: 10339 10339 263 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2864 +Encoding: 10340 10340 264 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2865 +Encoding: 10341 10341 265 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2866 +Encoding: 10342 10342 266 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2867 +Encoding: 10343 10343 267 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2868 +Encoding: 10344 10344 268 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2869 +Encoding: 10345 10345 269 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u286A +Encoding: 10346 10346 270 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u286B +Encoding: 10347 10347 271 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u286C +Encoding: 10348 10348 272 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u286D +Encoding: 10349 10349 273 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u286E +Encoding: 10350 10350 274 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u286F +Encoding: 10351 10351 275 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2870 +Encoding: 10352 10352 276 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2871 +Encoding: 10353 10353 277 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2872 +Encoding: 10354 10354 278 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2873 +Encoding: 10355 10355 279 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2874 +Encoding: 10356 10356 280 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2875 +Encoding: 10357 10357 281 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2876 +Encoding: 10358 10358 282 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2877 +Encoding: 10359 10359 283 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2878 +Encoding: 10360 10360 284 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2879 +Encoding: 10361 10361 285 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u287A +Encoding: 10362 10362 286 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u287B +Encoding: 10363 10363 287 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u287C +Encoding: 10364 10364 288 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u287D +Encoding: 10365 10365 289 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u287E +Encoding: 10366 10366 290 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u287F +Encoding: 10367 10367 291 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2880 +Encoding: 10368 10368 292 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2881 +Encoding: 10369 10369 293 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2882 +Encoding: 10370 10370 294 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2883 +Encoding: 10371 10371 295 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2884 +Encoding: 10372 10372 296 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2885 +Encoding: 10373 10373 297 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2886 +Encoding: 10374 10374 298 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2887 +Encoding: 10375 10375 299 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2888 +Encoding: 10376 10376 300 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2889 +Encoding: 10377 10377 301 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u288A +Encoding: 10378 10378 302 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u288B +Encoding: 10379 10379 303 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u288C +Encoding: 10380 10380 304 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u288D +Encoding: 10381 10381 305 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u288E +Encoding: 10382 10382 306 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u288F +Encoding: 10383 10383 307 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2890 +Encoding: 10384 10384 308 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2891 +Encoding: 10385 10385 309 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2892 +Encoding: 10386 10386 310 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2893 +Encoding: 10387 10387 311 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2894 +Encoding: 10388 10388 312 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2895 +Encoding: 10389 10389 313 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2896 +Encoding: 10390 10390 314 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2897 +Encoding: 10391 10391 315 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2898 +Encoding: 10392 10392 316 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2899 +Encoding: 10393 10393 317 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u289A +Encoding: 10394 10394 318 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u289B +Encoding: 10395 10395 319 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u289C +Encoding: 10396 10396 320 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u289D +Encoding: 10397 10397 321 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u289E +Encoding: 10398 10398 322 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u289F +Encoding: 10399 10399 323 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28A0 +Encoding: 10400 10400 324 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28A1 +Encoding: 10401 10401 325 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28A2 +Encoding: 10402 10402 326 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28A3 +Encoding: 10403 10403 327 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28A4 +Encoding: 10404 10404 328 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28A5 +Encoding: 10405 10405 329 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28A6 +Encoding: 10406 10406 330 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28A7 +Encoding: 10407 10407 331 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28A8 +Encoding: 10408 10408 332 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28A9 +Encoding: 10409 10409 333 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28AA +Encoding: 10410 10410 334 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28AB +Encoding: 10411 10411 335 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28AC +Encoding: 10412 10412 336 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28AD +Encoding: 10413 10413 337 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28AE +Encoding: 10414 10414 338 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28AF +Encoding: 10415 10415 339 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28B0 +Encoding: 10416 10416 340 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28B1 +Encoding: 10417 10417 341 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28B2 +Encoding: 10418 10418 342 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28B3 +Encoding: 10419 10419 343 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28B4 +Encoding: 10420 10420 344 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28B5 +Encoding: 10421 10421 345 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28B6 +Encoding: 10422 10422 346 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28B7 +Encoding: 10423 10423 347 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28B8 +Encoding: 10424 10424 348 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28B9 +Encoding: 10425 10425 349 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28BA +Encoding: 10426 10426 350 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28BB +Encoding: 10427 10427 351 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28BC +Encoding: 10428 10428 352 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28BD +Encoding: 10429 10429 353 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28BE +Encoding: 10430 10430 354 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28BF +Encoding: 10431 10431 355 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28C0 +Encoding: 10432 10432 356 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28C1 +Encoding: 10433 10433 357 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28C2 +Encoding: 10434 10434 358 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28C3 +Encoding: 10435 10435 359 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28C4 +Encoding: 10436 10436 360 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28C5 +Encoding: 10437 10437 361 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28C6 +Encoding: 10438 10438 362 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28C7 +Encoding: 10439 10439 363 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28C8 +Encoding: 10440 10440 364 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28C9 +Encoding: 10441 10441 365 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28CA +Encoding: 10442 10442 366 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28CB +Encoding: 10443 10443 367 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28CC +Encoding: 10444 10444 368 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28CD +Encoding: 10445 10445 369 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28CE +Encoding: 10446 10446 370 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28CF +Encoding: 10447 10447 371 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28D0 +Encoding: 10448 10448 372 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28D1 +Encoding: 10449 10449 373 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28D2 +Encoding: 10450 10450 374 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28D3 +Encoding: 10451 10451 375 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28D4 +Encoding: 10452 10452 376 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28D5 +Encoding: 10453 10453 377 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28D6 +Encoding: 10454 10454 378 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28D7 +Encoding: 10455 10455 379 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28D8 +Encoding: 10456 10456 380 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28D9 +Encoding: 10457 10457 381 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28DA +Encoding: 10458 10458 382 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28DB +Encoding: 10459 10459 383 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28DC +Encoding: 10460 10460 384 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28DD +Encoding: 10461 10461 385 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28DE +Encoding: 10462 10462 386 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28DF +Encoding: 10463 10463 387 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28E0 +Encoding: 10464 10464 388 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28E1 +Encoding: 10465 10465 389 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28E2 +Encoding: 10466 10466 390 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28E3 +Encoding: 10467 10467 391 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28E4 +Encoding: 10468 10468 392 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28E5 +Encoding: 10469 10469 393 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28E6 +Encoding: 10470 10470 394 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28E7 +Encoding: 10471 10471 395 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28E8 +Encoding: 10472 10472 396 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28E9 +Encoding: 10473 10473 397 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28EA +Encoding: 10474 10474 398 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28EB +Encoding: 10475 10475 399 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28EC +Encoding: 10476 10476 400 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28ED +Encoding: 10477 10477 401 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28EE +Encoding: 10478 10478 402 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28EF +Encoding: 10479 10479 403 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28F0 +Encoding: 10480 10480 404 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28F1 +Encoding: 10481 10481 405 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28F2 +Encoding: 10482 10482 406 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28F3 +Encoding: 10483 10483 407 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28F4 +Encoding: 10484 10484 408 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28F5 +Encoding: 10485 10485 409 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28F6 +Encoding: 10486 10486 410 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28F7 +Encoding: 10487 10487 411 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28F8 +Encoding: 10488 10488 412 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28F9 +Encoding: 10489 10489 413 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28FA +Encoding: 10490 10490 414 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28FB +Encoding: 10491 10491 415 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28FC +Encoding: 10492 10492 416 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28FD +Encoding: 10493 10493 417 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28FE +Encoding: 10494 10494 418 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u28FF +Encoding: 10495 10495 419 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 14 60942 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2B00 +Encoding: 11008 11008 420 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2B01 +Encoding: 11009 11009 421 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2B02 +Encoding: 11010 11010 422 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u2B03 +Encoding: 11011 11011 423 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3041 +Encoding: 12353 12353 424 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3042 +Encoding: 12354 12354 425 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3043 +Encoding: 12355 12355 426 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3044 +Encoding: 12356 12356 427 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3045 +Encoding: 12357 12357 428 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3046 +Encoding: 12358 12358 429 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3047 +Encoding: 12359 12359 430 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3048 +Encoding: 12360 12360 431 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3049 +Encoding: 12361 12361 432 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u304A +Encoding: 12362 12362 433 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u304B +Encoding: 12363 12363 434 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u304C +Encoding: 12364 12364 435 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u304D +Encoding: 12365 12365 436 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u304E +Encoding: 12366 12366 437 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u304F +Encoding: 12367 12367 438 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3050 +Encoding: 12368 12368 439 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3051 +Encoding: 12369 12369 440 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3052 +Encoding: 12370 12370 441 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3053 +Encoding: 12371 12371 442 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3054 +Encoding: 12372 12372 443 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3055 +Encoding: 12373 12373 444 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3056 +Encoding: 12374 12374 445 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3057 +Encoding: 12375 12375 446 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3058 +Encoding: 12376 12376 447 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3059 +Encoding: 12377 12377 448 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u305A +Encoding: 12378 12378 449 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u305B +Encoding: 12379 12379 450 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u305C +Encoding: 12380 12380 451 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u305D +Encoding: 12381 12381 452 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u305E +Encoding: 12382 12382 453 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u305F +Encoding: 12383 12383 454 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3060 +Encoding: 12384 12384 455 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3061 +Encoding: 12385 12385 456 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3062 +Encoding: 12386 12386 457 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3063 +Encoding: 12387 12387 458 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3064 +Encoding: 12388 12388 459 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3065 +Encoding: 12389 12389 460 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3066 +Encoding: 12390 12390 461 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3067 +Encoding: 12391 12391 462 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3068 +Encoding: 12392 12392 463 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3069 +Encoding: 12393 12393 464 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u306A +Encoding: 12394 12394 465 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u306B +Encoding: 12395 12395 466 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u306C +Encoding: 12396 12396 467 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u306D +Encoding: 12397 12397 468 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u306E +Encoding: 12398 12398 469 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u306F +Encoding: 12399 12399 470 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3070 +Encoding: 12400 12400 471 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3071 +Encoding: 12401 12401 472 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 0 60928 N 1 0 0 1 1632 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 10 60938 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3072 +Encoding: 12402 12402 473 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3073 +Encoding: 12403 12403 474 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3074 +Encoding: 12404 12404 475 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 0 60928 N 1 0 0 1 1632 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 10 60938 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3075 +Encoding: 12405 12405 476 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3076 +Encoding: 12406 12406 477 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3077 +Encoding: 12407 12407 478 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 0 60928 N 1 0 0 1 1632 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 10 60938 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3078 +Encoding: 12408 12408 479 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3079 +Encoding: 12409 12409 480 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u307A +Encoding: 12410 12410 481 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 0 60928 N 1 0 0 1 1632 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 10 60938 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u307B +Encoding: 12411 12411 482 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u307C +Encoding: 12412 12412 483 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u307D +Encoding: 12413 12413 484 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 0 60928 N 1 0 0 1 1632 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 10 60938 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u307E +Encoding: 12414 12414 485 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u307F +Encoding: 12415 12415 486 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3080 +Encoding: 12416 12416 487 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3081 +Encoding: 12417 12417 488 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3082 +Encoding: 12418 12418 489 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3083 +Encoding: 12419 12419 490 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3084 +Encoding: 12420 12420 491 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3085 +Encoding: 12421 12421 492 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3086 +Encoding: 12422 12422 493 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3087 +Encoding: 12423 12423 494 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3088 +Encoding: 12424 12424 495 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3089 +Encoding: 12425 12425 496 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u308A +Encoding: 12426 12426 497 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u308B +Encoding: 12427 12427 498 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u308C +Encoding: 12428 12428 499 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u308D +Encoding: 12429 12429 500 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u308E +Encoding: 12430 12430 501 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u308F +Encoding: 12431 12431 502 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3090 +Encoding: 12432 12432 503 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3091 +Encoding: 12433 12433 504 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3092 +Encoding: 12434 12434 505 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3093 +Encoding: 12435 12435 506 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3094 +Encoding: 12436 12436 507 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u3095 +Encoding: 12437 12437 508 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3096 +Encoding: 12438 12438 509 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u3099 +Encoding: 12441 12441 510 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u309A +Encoding: 12442 12442 511 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u309B +Encoding: 12443 12443 512 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u309C +Encoding: 12444 12444 513 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u309D +Encoding: 12445 12445 514 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u309E +Encoding: 12446 12446 515 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u309F +Encoding: 12447 12447 516 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30A0 +Encoding: 12448 12448 517 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30A1 +Encoding: 12449 12449 518 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30A2 +Encoding: 12450 12450 519 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30A3 +Encoding: 12451 12451 520 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30A4 +Encoding: 12452 12452 521 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30A5 +Encoding: 12453 12453 522 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30A6 +Encoding: 12454 12454 523 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30A7 +Encoding: 12455 12455 524 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30A8 +Encoding: 12456 12456 525 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30A9 +Encoding: 12457 12457 526 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30AA +Encoding: 12458 12458 527 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30AB +Encoding: 12459 12459 528 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30AC +Encoding: 12460 12460 529 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30AD +Encoding: 12461 12461 530 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30AE +Encoding: 12462 12462 531 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30AF +Encoding: 12463 12463 532 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30B0 +Encoding: 12464 12464 533 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30B1 +Encoding: 12465 12465 534 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30B2 +Encoding: 12466 12466 535 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30B3 +Encoding: 12467 12467 536 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30B4 +Encoding: 12468 12468 537 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30B5 +Encoding: 12469 12469 538 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30B6 +Encoding: 12470 12470 539 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30B7 +Encoding: 12471 12471 540 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30B8 +Encoding: 12472 12472 541 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30B9 +Encoding: 12473 12473 542 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30BA +Encoding: 12474 12474 543 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30BB +Encoding: 12475 12475 544 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30BC +Encoding: 12476 12476 545 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30BD +Encoding: 12477 12477 546 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30BE +Encoding: 12478 12478 547 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30BF +Encoding: 12479 12479 548 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30C0 +Encoding: 12480 12480 549 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30C1 +Encoding: 12481 12481 550 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30C2 +Encoding: 12482 12482 551 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30C3 +Encoding: 12483 12483 552 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30C4 +Encoding: 12484 12484 553 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30C5 +Encoding: 12485 12485 554 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30C6 +Encoding: 12486 12486 555 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30C7 +Encoding: 12487 12487 556 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30C8 +Encoding: 12488 12488 557 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30C9 +Encoding: 12489 12489 558 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30CA +Encoding: 12490 12490 559 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30CB +Encoding: 12491 12491 560 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30CC +Encoding: 12492 12492 561 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30CD +Encoding: 12493 12493 562 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30CE +Encoding: 12494 12494 563 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30CF +Encoding: 12495 12495 564 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30D0 +Encoding: 12496 12496 565 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30D1 +Encoding: 12497 12497 566 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 0 60928 N 1 0 0 1 1632 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 10 60938 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30D2 +Encoding: 12498 12498 567 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30D3 +Encoding: 12499 12499 568 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30D4 +Encoding: 12500 12500 569 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 0 60928 N 1 0 0 1 1632 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 10 60938 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30D5 +Encoding: 12501 12501 570 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30D6 +Encoding: 12502 12502 571 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30D7 +Encoding: 12503 12503 572 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 0 60928 N 1 0 0 1 1632 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 10 60938 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30D8 +Encoding: 12504 12504 573 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30D9 +Encoding: 12505 12505 574 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30DA +Encoding: 12506 12506 575 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 0 60928 N 1 0 0 1 1632 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 10 60938 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30DB +Encoding: 12507 12507 576 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30DC +Encoding: 12508 12508 577 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30DD +Encoding: 12509 12509 578 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +Refer: 0 60928 N 1 0 0 1 1632 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 10 60938 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30DE +Encoding: 12510 12510 579 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30DF +Encoding: 12511 12511 580 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30E0 +Encoding: 12512 12512 581 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30E1 +Encoding: 12513 12513 582 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30E2 +Encoding: 12514 12514 583 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30E3 +Encoding: 12515 12515 584 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30E4 +Encoding: 12516 12516 585 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30E5 +Encoding: 12517 12517 586 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30E6 +Encoding: 12518 12518 587 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30E7 +Encoding: 12519 12519 588 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30E8 +Encoding: 12520 12520 589 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30E9 +Encoding: 12521 12521 590 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30EA +Encoding: 12522 12522 591 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30EB +Encoding: 12523 12523 592 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30EC +Encoding: 12524 12524 593 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30ED +Encoding: 12525 12525 594 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30EE +Encoding: 12526 12526 595 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30EF +Encoding: 12527 12527 596 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30F0 +Encoding: 12528 12528 597 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30F1 +Encoding: 12529 12529 598 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30F2 +Encoding: 12530 12530 599 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30F3 +Encoding: 12531 12531 600 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30F4 +Encoding: 12532 12532 601 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30F5 +Encoding: 12533 12533 602 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30F6 +Encoding: 12534 12534 603 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30F7 +Encoding: 12535 12535 604 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30F8 +Encoding: 12536 12536 605 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30F9 +Encoding: 12537 12537 606 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30FA +Encoding: 12538 12538 607 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30FB +Encoding: 12539 12539 608 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 14 60942 N 1 0 0 1 0 0 2 +EndChar + +StartChar: u30FC +Encoding: 12540 12540 609 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30FD +Encoding: 12541 12541 610 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u30FE +Encoding: 12542 12542 611 +Width: 1632 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 1632 0 2 +Refer: 9 60937 N 1 0 0 1 1632 0 2 +EndChar + +StartChar: u30FF +Encoding: 12543 12543 612 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31F0 +Encoding: 12784 12784 613 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31F1 +Encoding: 12785 12785 614 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31F2 +Encoding: 12786 12786 615 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31F3 +Encoding: 12787 12787 616 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31F4 +Encoding: 12788 12788 617 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31F5 +Encoding: 12789 12789 618 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31F6 +Encoding: 12790 12790 619 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31F7 +Encoding: 12791 12791 620 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31F8 +Encoding: 12792 12792 621 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31F9 +Encoding: 12793 12793 622 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31FA +Encoding: 12794 12794 623 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31FB +Encoding: 12795 12795 624 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31FC +Encoding: 12796 12796 625 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31FD +Encoding: 12797 12797 626 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31FE +Encoding: 12798 12798 627 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u31FF +Encoding: 12799 12799 628 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uEE12 +Encoding: 60946 60946 629 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uEE13 +Encoding: 60947 60947 630 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uEE14 +Encoding: 60948 60948 631 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF01 +Encoding: 65281 65281 632 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +EndChar + +StartChar: uFF02 +Encoding: 65282 65282 633 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF03 +Encoding: 65283 65283 634 +Width: 200 +VWidth: 0 +Flags: HW +LayerCount: 2 +Fore +Refer: 17 60945 N 1 0 0 1 0 0 2 +EndChar + +StartChar: uFF04 +Encoding: 65284 65284 635 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF05 +Encoding: 65285 65285 636 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF06 +Encoding: 65286 65286 637 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF07 +Encoding: 65287 65287 638 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF08 +Encoding: 65288 65288 639 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF09 +Encoding: 65289 65289 640 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF0A +Encoding: 65290 65290 641 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF0B +Encoding: 65291 65291 642 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF0C +Encoding: 65292 65292 643 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF0D +Encoding: 65293 65293 644 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF0E +Encoding: 65294 65294 645 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 14 60942 N 1 0 0 1 0 0 2 +EndChar + +StartChar: uFF0F +Encoding: 65295 65295 646 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF10 +Encoding: 65296 65296 647 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF11 +Encoding: 65297 65297 648 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF12 +Encoding: 65298 65298 649 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF13 +Encoding: 65299 65299 650 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF14 +Encoding: 65300 65300 651 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF15 +Encoding: 65301 65301 652 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF16 +Encoding: 65302 65302 653 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF17 +Encoding: 65303 65303 654 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF18 +Encoding: 65304 65304 655 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF19 +Encoding: 65305 65305 656 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF1A +Encoding: 65306 65306 657 +Width: 200 +VWidth: 0 +Flags: HW +LayerCount: 2 +Fore +Refer: 16 60944 N 1 0 0 1 0 0 2 +Refer: 17 60945 N 1 0 0 1 0 0 2 +EndChar + +StartChar: uFF1B +Encoding: 65307 65307 658 +Width: 200 +VWidth: 0 +Flags: HW +LayerCount: 2 +Fore +Refer: 16 60944 N 1 0 0 1 0 0 2 +EndChar + +StartChar: uFF1C +Encoding: 65308 65308 659 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF1D +Encoding: 65309 65309 660 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF1E +Encoding: 65310 65310 661 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF1F +Encoding: 65311 65311 662 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF20 +Encoding: 65312 65312 663 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF21 +Encoding: 65313 65313 664 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF22 +Encoding: 65314 65314 665 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF23 +Encoding: 65315 65315 666 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF24 +Encoding: 65316 65316 667 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF25 +Encoding: 65317 65317 668 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF26 +Encoding: 65318 65318 669 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF27 +Encoding: 65319 65319 670 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF28 +Encoding: 65320 65320 671 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF29 +Encoding: 65321 65321 672 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF2A +Encoding: 65322 65322 673 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF2B +Encoding: 65323 65323 674 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF2C +Encoding: 65324 65324 675 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF2D +Encoding: 65325 65325 676 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF2E +Encoding: 65326 65326 677 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF2F +Encoding: 65327 65327 678 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF30 +Encoding: 65328 65328 679 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF31 +Encoding: 65329 65329 680 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF32 +Encoding: 65330 65330 681 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF33 +Encoding: 65331 65331 682 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF34 +Encoding: 65332 65332 683 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF35 +Encoding: 65333 65333 684 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF36 +Encoding: 65334 65334 685 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF37 +Encoding: 65335 65335 686 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF38 +Encoding: 65336 65336 687 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF39 +Encoding: 65337 65337 688 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF3A +Encoding: 65338 65338 689 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF3B +Encoding: 65339 65339 690 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF3C +Encoding: 65340 65340 691 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF3D +Encoding: 65341 65341 692 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF3E +Encoding: 65342 65342 693 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF3F +Encoding: 65343 65343 694 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF40 +Encoding: 65344 65344 695 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF41 +Encoding: 65345 65345 696 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF42 +Encoding: 65346 65346 697 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF43 +Encoding: 65347 65347 698 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF44 +Encoding: 65348 65348 699 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF45 +Encoding: 65349 65349 700 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF46 +Encoding: 65350 65350 701 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF47 +Encoding: 65351 65351 702 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF48 +Encoding: 65352 65352 703 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF49 +Encoding: 65353 65353 704 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF4A +Encoding: 65354 65354 705 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF4B +Encoding: 65355 65355 706 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF4C +Encoding: 65356 65356 707 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF4D +Encoding: 65357 65357 708 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF4E +Encoding: 65358 65358 709 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF4F +Encoding: 65359 65359 710 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF50 +Encoding: 65360 65360 711 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF51 +Encoding: 65361 65361 712 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF52 +Encoding: 65362 65362 713 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF53 +Encoding: 65363 65363 714 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF54 +Encoding: 65364 65364 715 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF55 +Encoding: 65365 65365 716 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF56 +Encoding: 65366 65366 717 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF57 +Encoding: 65367 65367 718 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF58 +Encoding: 65368 65368 719 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF59 +Encoding: 65369 65369 720 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF5A +Encoding: 65370 65370 721 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF5B +Encoding: 65371 65371 722 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF5C +Encoding: 65372 65372 723 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF5D +Encoding: 65373 65373 724 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF5E +Encoding: 65374 65374 725 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF65 +Encoding: 65381 65381 726 +Width: 0 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 14 60942 N 1 0 0 1 0 0 2 +EndChar + +StartChar: uFF66 +Encoding: 65382 65382 727 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF67 +Encoding: 65383 65383 728 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF68 +Encoding: 65384 65384 729 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF69 +Encoding: 65385 65385 730 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF6A +Encoding: 65386 65386 731 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF6B +Encoding: 65387 65387 732 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF6C +Encoding: 65388 65388 733 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF6D +Encoding: 65389 65389 734 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF6E +Encoding: 65390 65390 735 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF6F +Encoding: 65391 65391 736 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF70 +Encoding: 65392 65392 737 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF71 +Encoding: 65393 65393 738 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF72 +Encoding: 65394 65394 739 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF73 +Encoding: 65395 65395 740 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF74 +Encoding: 65396 65396 741 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF75 +Encoding: 65397 65397 742 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF76 +Encoding: 65398 65398 743 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF77 +Encoding: 65399 65399 744 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF78 +Encoding: 65400 65400 745 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF79 +Encoding: 65401 65401 746 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF7A +Encoding: 65402 65402 747 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF7B +Encoding: 65403 65403 748 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF7C +Encoding: 65404 65404 749 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF7D +Encoding: 65405 65405 750 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF7E +Encoding: 65406 65406 751 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF7F +Encoding: 65407 65407 752 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF80 +Encoding: 65408 65408 753 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF81 +Encoding: 65409 65409 754 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF82 +Encoding: 65410 65410 755 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF83 +Encoding: 65411 65411 756 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF84 +Encoding: 65412 65412 757 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF85 +Encoding: 65413 65413 758 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF86 +Encoding: 65414 65414 759 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF87 +Encoding: 65415 65415 760 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF88 +Encoding: 65416 65416 761 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF89 +Encoding: 65417 65417 762 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF8A +Encoding: 65418 65418 763 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF8B +Encoding: 65419 65419 764 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF8C +Encoding: 65420 65420 765 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF8D +Encoding: 65421 65421 766 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF8E +Encoding: 65422 65422 767 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF8F +Encoding: 65423 65423 768 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF90 +Encoding: 65424 65424 769 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF91 +Encoding: 65425 65425 770 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF92 +Encoding: 65426 65426 771 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF93 +Encoding: 65427 65427 772 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF94 +Encoding: 65428 65428 773 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF95 +Encoding: 65429 65429 774 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF96 +Encoding: 65430 65430 775 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF97 +Encoding: 65431 65431 776 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF98 +Encoding: 65432 65432 777 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF99 +Encoding: 65433 65433 778 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF9A +Encoding: 65434 65434 779 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF9B +Encoding: 65435 65435 780 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF9C +Encoding: 65436 65436 781 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF9D +Encoding: 65437 65437 782 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF9E +Encoding: 65438 65438 783 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFF9F +Encoding: 65439 65439 784 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFFE2 +Encoding: 65506 65506 785 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFFE3 +Encoding: 65507 65507 786 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFFE4 +Encoding: 65508 65508 787 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFFE5 +Encoding: 65509 65509 788 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFFE8 +Encoding: 65512 65512 789 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFFE9 +Encoding: 65513 65513 790 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFFEA +Encoding: 65514 65514 791 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFFEB +Encoding: 65515 65515 792 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uFFEC +Encoding: 65516 65516 793 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 8 60936 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1B000 +Encoding: 110592 110592 794 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1B001 +Encoding: 110593 110593 795 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +Refer: 13 60941 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1B150 +Encoding: 110928 110928 796 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1B151 +Encoding: 110929 110929 797 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1B152 +Encoding: 110930 110930 798 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1B164 +Encoding: 110948 110948 799 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1B165 +Encoding: 110949 110949 800 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 9 60937 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 12 60940 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1B166 +Encoding: 110950 110950 801 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 10 60938 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1B167 +Encoding: 110951 110951 802 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 11 60939 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1FBF0 +Encoding: 130032 130032 803 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1FBF1 +Encoding: 130033 130033 804 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1FBF2 +Encoding: 130034 130034 805 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1FBF3 +Encoding: 130035 130035 806 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1FBF4 +Encoding: 130036 130036 807 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1FBF5 +Encoding: 130037 130037 808 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1FBF6 +Encoding: 130038 130038 809 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1FBF7 +Encoding: 130039 130039 810 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1FBF8 +Encoding: 130040 130040 811 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 4 60932 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: u1FBF9 +Encoding: 130041 130041 812 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +Fore +Refer: 0 60928 N 1 0 0 1 816 0 2 +Refer: 1 60929 N 1 0 0 1 816 0 2 +Refer: 2 60930 N 1 0 0 1 816 0 2 +Refer: 3 60931 N 1 0 0 1 816 0 2 +Refer: 5 60933 N 1 0 0 1 816 0 2 +Refer: 6 60934 N 1 0 0 1 816 0 2 +Refer: 7 60935 N 1 0 0 1 816 0 2 +EndChar + +StartChar: uni0009 +Encoding: 9 9 813 +Width: 816 +VWidth: 200 +Flags: HW +LayerCount: 2 +EndChar +EndChars +EndSplineFont diff --git a/src/spice2x/external/easywsclient/easywsclient.cpp b/src/spice2x/external/easywsclient/easywsclient.cpp index a183978..55092d1 100644 --- a/src/spice2x/external/easywsclient/easywsclient.cpp +++ b/src/spice2x/external/easywsclient/easywsclient.cpp @@ -1,552 +1,552 @@ -// This code comes from: -// https://github.com/dhbaird/easywsclient -// -// To get the latest version: -// wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.hpp -// wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.cpp - -#include "easywsclient.hpp" - -#ifdef _WIN32 - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include - #include - #pragma comment( lib, "ws2_32" ) - #include - #include - #include - #include - #include - typedef SOCKET socket_t; - #ifndef _SSIZE_T_DEFINED - typedef int ssize_t; - #define _SSIZE_T_DEFINED - #endif - #ifndef _SOCKET_T_DEFINED - typedef SOCKET socket_t; - #define _SOCKET_T_DEFINED - #endif - #if defined(_MSC_VER) && !defined(snprintf) - #define snprintf _snprintf_s - #endif - #include - #define socketerrno WSAGetLastError() - #define SOCKET_EAGAIN_EINPROGRESS WSAEINPROGRESS - #define SOCKET_EWOULDBLOCK WSAEWOULDBLOCK -#else - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - typedef int socket_t; - #ifndef INVALID_SOCKET - #define INVALID_SOCKET (-1) - #endif - #ifndef SOCKET_ERROR - #define SOCKET_ERROR (-1) - #endif - #define closesocket(s) ::close(s) - #include - #define socketerrno errno - #define SOCKET_EAGAIN_EINPROGRESS EAGAIN - #define SOCKET_EWOULDBLOCK EWOULDBLOCK -#endif - -#include -#include - -#include - -#include "util/logging.h" - -// When false, easywsclient's diagnostics are suppressed. The host application -// opts in by setting this flag (see obs_websocket.cpp). -bool EASYWSCLIENT_LOGGING_ENABLED = false; - -// Route easywsclient's diagnostic output through the project logger instead of -// writing to stderr, without editing the upstream source lines below: these two -// macros redirect fprintf(stderr, ...) / fputs(..., stderr) to log_misc. -// Only emitted when EASYWSCLIENT_LOGGING_ENABLED is set. -static inline void easywsclient_logf(const char *fmt, ...) { - if (!EASYWSCLIENT_LOGGING_ENABLED) { - return; - } - char buf[1024]; - va_list args; - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - // trim trailing newline(s); the logger appends its own - size_t len = strlen(buf); - while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r')) { - buf[--len] = '\0'; - } - log_misc("easywsclient", "{}", buf); -} -#define fprintf(stream, ...) easywsclient_logf(__VA_ARGS__) -#define fputs(str, stream) easywsclient_logf("%s", str) - -using namespace easywsclient; - -namespace { // private module-only namespace - -socket_t hostname_connect(const std::string& hostname, int port) { - struct addrinfo hints; - struct addrinfo *result; - struct addrinfo *p; - int ret; - socket_t sockfd = INVALID_SOCKET; - char sport[16]; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - snprintf(sport, 16, "%d", port); - if ((ret = getaddrinfo(hostname.c_str(), sport, &hints, &result)) != 0) - { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); - return 1; - } - for(p = result; p != NULL; p = p->ai_next) - { - sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); - if (sockfd == INVALID_SOCKET) { continue; } - if (connect(sockfd, p->ai_addr, p->ai_addrlen) != SOCKET_ERROR) { - break; - } - closesocket(sockfd); - sockfd = INVALID_SOCKET; - } - freeaddrinfo(result); - return sockfd; -} - - -class _DummyWebSocket : public easywsclient::WebSocket -{ - public: - void poll(int timeout) { } - void send(const std::string& message) { } - void sendBinary(const std::string& message) { } - void sendBinary(const std::vector& message) { } - void sendPing() { } - void close() { } - readyStateValues getReadyState() const { return CLOSED; } - void _dispatch(Callback_Imp & callable) { } - void _dispatchBinary(BytesCallback_Imp& callable) { } -}; - - -class _RealWebSocket : public easywsclient::WebSocket -{ - public: - // http://tools.ietf.org/html/rfc6455#section-5.2 Base Framing Protocol - // - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-------+-+-------------+-------------------------------+ - // |F|R|R|R| opcode|M| Payload len | Extended payload length | - // |I|S|S|S| (4) |A| (7) | (16/64) | - // |N|V|V|V| |S| | (if payload len==126/127) | - // | |1|2|3| |K| | | - // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - // | Extended payload length continued, if payload len == 127 | - // + - - - - - - - - - - - - - - - +-------------------------------+ - // | |Masking-key, if MASK set to 1 | - // +-------------------------------+-------------------------------+ - // | Masking-key (continued) | Payload Data | - // +-------------------------------- - - - - - - - - - - - - - - - + - // : Payload Data continued ... : - // + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - // | Payload Data continued ... | - // +---------------------------------------------------------------+ - - struct wsheader_type { - unsigned header_size; - bool fin; - bool mask; - enum opcode_type { - CONTINUATION = 0x0, - TEXT_FRAME = 0x1, - BINARY_FRAME = 0x2, - CLOSE = 8, - PING = 9, - PONG = 0xa, - } opcode; - int N0; - uint64_t N; - uint8_t masking_key[4]; - }; - - std::vector rxbuf; - std::vector txbuf; - std::vector receivedData; - - socket_t sockfd; - readyStateValues readyState; - bool useMask; - bool isRxBad; - - _RealWebSocket(socket_t sockfd, bool useMask) : sockfd(sockfd), readyState(OPEN), useMask(useMask), isRxBad(false) { - } - - readyStateValues getReadyState() const { - return readyState; - } - - void poll(int timeout) { // timeout in milliseconds - if (readyState == CLOSED) { - if (timeout > 0) { - timeval tv = { timeout/1000, (timeout%1000) * 1000 }; - select(0, NULL, NULL, NULL, &tv); - } - return; - } - if (timeout != 0) { - fd_set rfds; - fd_set wfds; - timeval tv = { timeout/1000, (timeout%1000) * 1000 }; - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_SET(sockfd, &rfds); - if (txbuf.size()) { FD_SET(sockfd, &wfds); } - select(sockfd + 1, &rfds, &wfds, NULL, timeout > 0 ? &tv : NULL); - } - while (true) { - // FD_ISSET(0, &rfds) will be true - int N = rxbuf.size(); - ssize_t ret; - rxbuf.resize(N + 1500); - ret = recv(sockfd, (char*)&rxbuf[0] + N, 1500, 0); - if (false) { } - else if (ret < 0 && (socketerrno == SOCKET_EWOULDBLOCK || socketerrno == SOCKET_EAGAIN_EINPROGRESS)) { - rxbuf.resize(N); - break; - } - else if (ret <= 0) { - rxbuf.resize(N); - closesocket(sockfd); - readyState = CLOSED; - fputs(ret < 0 ? "Connection error!\n" : "Connection closed!\n", stderr); - break; - } - else { - rxbuf.resize(N + ret); - } - } - while (txbuf.size()) { - int ret = ::send(sockfd, (char*)&txbuf[0], txbuf.size(), 0); - if (false) { } // ?? - else if (ret < 0 && (socketerrno == SOCKET_EWOULDBLOCK || socketerrno == SOCKET_EAGAIN_EINPROGRESS)) { - break; - } - else if (ret <= 0) { - closesocket(sockfd); - readyState = CLOSED; - fputs(ret < 0 ? "Connection error!\n" : "Connection closed!\n", stderr); - break; - } - else { - txbuf.erase(txbuf.begin(), txbuf.begin() + ret); - } - } - if (!txbuf.size() && readyState == CLOSING) { - closesocket(sockfd); - readyState = CLOSED; - } - } - - virtual void _dispatch(Callback_Imp & callable) { - struct CallbackAdapter : public BytesCallback_Imp - // Adapt void(const std::string&) to void(const std::string&) - { - Callback_Imp& callable; - CallbackAdapter(Callback_Imp& callable) : callable(callable) { } - void operator()(const std::vector& message) { - std::string stringMessage(message.begin(), message.end()); - callable(stringMessage); - } - }; - CallbackAdapter bytesCallback(callable); - _dispatchBinary(bytesCallback); - } - - virtual void _dispatchBinary(BytesCallback_Imp& callable) { - // TODO: consider acquiring a lock on rxbuf... - while (true) { - wsheader_type ws; - if (rxbuf.size() < 2) { return; /* Need at least 2 */ } - const uint8_t * data = (uint8_t *) &rxbuf[0]; // peek, but don't consume - ws.fin = (data[0] & 0x80) == 0x80; - ws.opcode = (wsheader_type::opcode_type) (data[0] & 0x0f); - ws.mask = (data[1] & 0x80) == 0x80; - ws.N0 = (data[1] & 0x7f); - ws.header_size = 2 + (ws.N0 == 126? 2 : 0) + (ws.N0 == 127? 8 : 0) + (ws.mask? 4 : 0); - if (rxbuf.size() < ws.header_size) { return; /* Need: ws.header_size - rxbuf.size() */ } - int i = 0; - if (ws.N0 < 126) { - ws.N = ws.N0; - i = 2; - } - else if (ws.N0 == 126) { - ws.N = 0; - ws.N |= ((uint64_t) data[2]) << 8; - ws.N |= ((uint64_t) data[3]) << 0; - i = 4; - } - else if (ws.N0 == 127) { - ws.N = 0; - ws.N |= ((uint64_t) data[2]) << 56; - ws.N |= ((uint64_t) data[3]) << 48; - ws.N |= ((uint64_t) data[4]) << 40; - ws.N |= ((uint64_t) data[5]) << 32; - ws.N |= ((uint64_t) data[6]) << 24; - ws.N |= ((uint64_t) data[7]) << 16; - ws.N |= ((uint64_t) data[8]) << 8; - ws.N |= ((uint64_t) data[9]) << 0; - i = 10; - } - if (ws.mask) { - ws.masking_key[0] = ((uint8_t) data[i+0]) << 0; - ws.masking_key[1] = ((uint8_t) data[i+1]) << 0; - ws.masking_key[2] = ((uint8_t) data[i+2]) << 0; - ws.masking_key[3] = ((uint8_t) data[i+3]) << 0; - } - else { - ws.masking_key[0] = 0; - ws.masking_key[1] = 0; - ws.masking_key[2] = 0; - ws.masking_key[3] = 0; - } - - // Note: The checks above should hopefully ensure this addition - // cannot overflow: - if (rxbuf.size() < ws.header_size+ws.N) { return; /* Need: ws.header_size+ws.N - rxbuf.size() */ } - - // We got a whole message, now do something with it: - if (false) { } - else if ( - ws.opcode == wsheader_type::TEXT_FRAME - || ws.opcode == wsheader_type::BINARY_FRAME - || ws.opcode == wsheader_type::CONTINUATION - ) { - if (ws.mask) { for (size_t i = 0; i != ws.N; ++i) { rxbuf[i+ws.header_size] ^= ws.masking_key[i&0x3]; } } - receivedData.insert(receivedData.end(), rxbuf.begin()+ws.header_size, rxbuf.begin()+ws.header_size+(size_t)ws.N);// just feed - if (ws.fin) { - callable((const std::vector) receivedData); - receivedData.erase(receivedData.begin(), receivedData.end()); - std::vector ().swap(receivedData);// free memory - } - } - else if (ws.opcode == wsheader_type::PING) { - if (ws.mask) { for (size_t i = 0; i != ws.N; ++i) { rxbuf[i+ws.header_size] ^= ws.masking_key[i&0x3]; } } - std::string data(rxbuf.begin()+ws.header_size, rxbuf.begin()+ws.header_size+(size_t)ws.N); - sendData(wsheader_type::PONG, data.size(), data.begin(), data.end()); - } - else if (ws.opcode == wsheader_type::PONG) { } - else if (ws.opcode == wsheader_type::CLOSE) { close(); } - else { fprintf(stderr, "ERROR: Got unexpected WebSocket message.\n"); close(); } - - rxbuf.erase(rxbuf.begin(), rxbuf.begin() + ws.header_size+(size_t)ws.N); - } - } - - void sendPing() { - std::string empty; - sendData(wsheader_type::PING, empty.size(), empty.begin(), empty.end()); - } - - void send(const std::string& message) { - sendData(wsheader_type::TEXT_FRAME, message.size(), message.begin(), message.end()); - } - - void sendBinary(const std::string& message) { - sendData(wsheader_type::BINARY_FRAME, message.size(), message.begin(), message.end()); - } - - void sendBinary(const std::vector& message) { - sendData(wsheader_type::BINARY_FRAME, message.size(), message.begin(), message.end()); - } - - template - void sendData(wsheader_type::opcode_type type, uint64_t message_size, Iterator message_begin, Iterator message_end) { - // TODO: - // Masking key should (must) be derived from a high quality random - // number generator, to mitigate attacks on non-WebSocket friendly - // middleware: - const uint8_t masking_key[4] = { 0x12, 0x34, 0x56, 0x78 }; - // TODO: consider acquiring a lock on txbuf... - if (readyState == CLOSING || readyState == CLOSED) { return; } - std::vector header; - header.assign(2 + (message_size >= 126 ? 2 : 0) + (message_size >= 65536 ? 6 : 0) + (useMask ? 4 : 0), 0); - header[0] = 0x80 | type; - if (false) { } - else if (message_size < 126) { - header[1] = (message_size & 0xff) | (useMask ? 0x80 : 0); - if (useMask) { - header[2] = masking_key[0]; - header[3] = masking_key[1]; - header[4] = masking_key[2]; - header[5] = masking_key[3]; - } - } - else if (message_size < 65536) { - header[1] = 126 | (useMask ? 0x80 : 0); - header[2] = (message_size >> 8) & 0xff; - header[3] = (message_size >> 0) & 0xff; - if (useMask) { - header[4] = masking_key[0]; - header[5] = masking_key[1]; - header[6] = masking_key[2]; - header[7] = masking_key[3]; - } - } - else { // TODO: run coverage testing here - header[1] = 127 | (useMask ? 0x80 : 0); - header[2] = (message_size >> 56) & 0xff; - header[3] = (message_size >> 48) & 0xff; - header[4] = (message_size >> 40) & 0xff; - header[5] = (message_size >> 32) & 0xff; - header[6] = (message_size >> 24) & 0xff; - header[7] = (message_size >> 16) & 0xff; - header[8] = (message_size >> 8) & 0xff; - header[9] = (message_size >> 0) & 0xff; - if (useMask) { - header[10] = masking_key[0]; - header[11] = masking_key[1]; - header[12] = masking_key[2]; - header[13] = masking_key[3]; - } - } - // N.B. - txbuf will keep growing until it can be transmitted over the socket: - txbuf.insert(txbuf.end(), header.begin(), header.end()); - txbuf.insert(txbuf.end(), message_begin, message_end); - if (useMask) { - size_t message_offset = txbuf.size() - message_size; - for (size_t i = 0; i != message_size; ++i) { - txbuf[message_offset + i] ^= masking_key[i&0x3]; - } - } - } - - void close() { - if(readyState == CLOSING || readyState == CLOSED) { return; } - readyState = CLOSING; - uint8_t closeFrame[6] = {0x88, 0x80, 0x00, 0x00, 0x00, 0x00}; // last 4 bytes are a masking key - std::vector header(closeFrame, closeFrame+6); - txbuf.insert(txbuf.end(), header.begin(), header.end()); - } - -}; - - -easywsclient::WebSocket::pointer from_url(const std::string& url, bool useMask, const std::string& origin) { - char host[512]; - int port; - char path[512]; - if (url.size() >= 512) { - fprintf(stderr, "ERROR: url size limit exceeded: %s\n", url.c_str()); - return NULL; - } - if (origin.size() >= 200) { - fprintf(stderr, "ERROR: origin size limit exceeded: %s\n", origin.c_str()); - return NULL; - } - if (false) { } - else if (sscanf(url.c_str(), "ws://%[^:/]:%d/%s", host, &port, path) == 3) { - } - else if (sscanf(url.c_str(), "ws://%[^:/]/%s", host, path) == 2) { - port = 80; - } - else if (sscanf(url.c_str(), "ws://%[^:/]:%d", host, &port) == 2) { - path[0] = '\0'; - } - else if (sscanf(url.c_str(), "ws://%[^:/]", host) == 1) { - port = 80; - path[0] = '\0'; - } - else { - fprintf(stderr, "ERROR: Could not parse WebSocket url: %s\n", url.c_str()); - return NULL; - } - //fprintf(stderr, "easywsclient: connecting: host=%s port=%d path=/%s\n", host, port, path); - socket_t sockfd = hostname_connect(host, port); - if (sockfd == INVALID_SOCKET) { - fprintf(stderr, "Unable to connect to %s:%d\n", host, port); - return NULL; - } - { - // XXX: this should be done non-blocking, - char line[1024]; - int status; - int i; - snprintf(line, 1024, "GET /%s HTTP/1.1\r\n", path); ::send(sockfd, line, strlen(line), 0); - if (port == 80) { - snprintf(line, 1024, "Host: %s\r\n", host); ::send(sockfd, line, strlen(line), 0); - } - else { - snprintf(line, 1024, "Host: %s:%d\r\n", host, port); ::send(sockfd, line, strlen(line), 0); - } - snprintf(line, 1024, "Upgrade: websocket\r\n"); ::send(sockfd, line, strlen(line), 0); - snprintf(line, 1024, "Connection: Upgrade\r\n"); ::send(sockfd, line, strlen(line), 0); - if (!origin.empty()) { - snprintf(line, 1024, "Origin: %s\r\n", origin.c_str()); ::send(sockfd, line, strlen(line), 0); - } - snprintf(line, 1024, "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"); ::send(sockfd, line, strlen(line), 0); - snprintf(line, 1024, "Sec-WebSocket-Version: 13\r\n"); ::send(sockfd, line, strlen(line), 0); - snprintf(line, 1024, "\r\n"); ::send(sockfd, line, strlen(line), 0); - for (i = 0; i < 2 || (i < 1023 && line[i-2] != '\r' && line[i-1] != '\n'); ++i) { if (recv(sockfd, line+i, 1, 0) == 0) { return NULL; } } - line[i] = 0; - if (i == 1023) { fprintf(stderr, "ERROR: Got invalid status line connecting to: %s\n", url.c_str()); return NULL; } - if (sscanf(line, "HTTP/1.1 %d", &status) != 1 || status != 101) { fprintf(stderr, "ERROR: Got bad status connecting to %s: %s", url.c_str(), line); return NULL; } - // TODO: verify response headers, - while (true) { - for (i = 0; i < 2 || (i < 1023 && line[i-2] != '\r' && line[i-1] != '\n'); ++i) { if (recv(sockfd, line+i, 1, 0) == 0) { return NULL; } } - if (line[0] == '\r' && line[1] == '\n') { break; } - } - } - int flag = 1; - setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)); // Disable Nagle's algorithm -#ifdef _WIN32 - u_long on = 1; - ioctlsocket(sockfd, FIONBIO, &on); -#else - fcntl(sockfd, F_SETFL, O_NONBLOCK); -#endif - //fprintf(stderr, "Connected to: %s\n", url.c_str()); - return easywsclient::WebSocket::pointer(new _RealWebSocket(sockfd, useMask)); -} - -} // end of module-only namespace - - - -namespace easywsclient { - -WebSocket::pointer WebSocket::create_dummy() { - static pointer dummy = pointer(new _DummyWebSocket); - return dummy; -} - - -WebSocket::pointer WebSocket::from_url(const std::string& url, const std::string& origin) { - return ::from_url(url, true, origin); -} - -WebSocket::pointer WebSocket::from_url_no_mask(const std::string& url, const std::string& origin) { - return ::from_url(url, false, origin); -} - - -} // namespace easywsclient +// This code comes from: +// https://github.com/dhbaird/easywsclient +// +// To get the latest version: +// wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.hpp +// wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.cpp + +#include "easywsclient.hpp" + +#ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + #include + #pragma comment( lib, "ws2_32" ) + #include + #include + #include + #include + #include + typedef SOCKET socket_t; + #ifndef _SSIZE_T_DEFINED + typedef int ssize_t; + #define _SSIZE_T_DEFINED + #endif + #ifndef _SOCKET_T_DEFINED + typedef SOCKET socket_t; + #define _SOCKET_T_DEFINED + #endif + #if defined(_MSC_VER) && !defined(snprintf) + #define snprintf _snprintf_s + #endif + #include + #define socketerrno WSAGetLastError() + #define SOCKET_EAGAIN_EINPROGRESS WSAEINPROGRESS + #define SOCKET_EWOULDBLOCK WSAEWOULDBLOCK +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + typedef int socket_t; + #ifndef INVALID_SOCKET + #define INVALID_SOCKET (-1) + #endif + #ifndef SOCKET_ERROR + #define SOCKET_ERROR (-1) + #endif + #define closesocket(s) ::close(s) + #include + #define socketerrno errno + #define SOCKET_EAGAIN_EINPROGRESS EAGAIN + #define SOCKET_EWOULDBLOCK EWOULDBLOCK +#endif + +#include +#include + +#include + +#include "util/logging.h" + +// When false, easywsclient's diagnostics are suppressed. The host application +// opts in by setting this flag (see obs_websocket.cpp). +bool EASYWSCLIENT_LOGGING_ENABLED = false; + +// Route easywsclient's diagnostic output through the project logger instead of +// writing to stderr, without editing the upstream source lines below: these two +// macros redirect fprintf(stderr, ...) / fputs(..., stderr) to log_misc. +// Only emitted when EASYWSCLIENT_LOGGING_ENABLED is set. +static inline void easywsclient_logf(const char *fmt, ...) { + if (!EASYWSCLIENT_LOGGING_ENABLED) { + return; + } + char buf[1024]; + va_list args; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + // trim trailing newline(s); the logger appends its own + size_t len = strlen(buf); + while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r')) { + buf[--len] = '\0'; + } + log_misc("easywsclient", "{}", buf); +} +#define fprintf(stream, ...) easywsclient_logf(__VA_ARGS__) +#define fputs(str, stream) easywsclient_logf("%s", str) + +using namespace easywsclient; + +namespace { // private module-only namespace + +socket_t hostname_connect(const std::string& hostname, int port) { + struct addrinfo hints; + struct addrinfo *result; + struct addrinfo *p; + int ret; + socket_t sockfd = INVALID_SOCKET; + char sport[16]; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + snprintf(sport, 16, "%d", port); + if ((ret = getaddrinfo(hostname.c_str(), sport, &hints, &result)) != 0) + { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); + return 1; + } + for(p = result; p != NULL; p = p->ai_next) + { + sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (sockfd == INVALID_SOCKET) { continue; } + if (connect(sockfd, p->ai_addr, p->ai_addrlen) != SOCKET_ERROR) { + break; + } + closesocket(sockfd); + sockfd = INVALID_SOCKET; + } + freeaddrinfo(result); + return sockfd; +} + + +class _DummyWebSocket : public easywsclient::WebSocket +{ + public: + void poll(int timeout) { } + void send(const std::string& message) { } + void sendBinary(const std::string& message) { } + void sendBinary(const std::vector& message) { } + void sendPing() { } + void close() { } + readyStateValues getReadyState() const { return CLOSED; } + void _dispatch(Callback_Imp & callable) { } + void _dispatchBinary(BytesCallback_Imp& callable) { } +}; + + +class _RealWebSocket : public easywsclient::WebSocket +{ + public: + // http://tools.ietf.org/html/rfc6455#section-5.2 Base Framing Protocol + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-------+-+-------------+-------------------------------+ + // |F|R|R|R| opcode|M| Payload len | Extended payload length | + // |I|S|S|S| (4) |A| (7) | (16/64) | + // |N|V|V|V| |S| | (if payload len==126/127) | + // | |1|2|3| |K| | | + // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + // | Extended payload length continued, if payload len == 127 | + // + - - - - - - - - - - - - - - - +-------------------------------+ + // | |Masking-key, if MASK set to 1 | + // +-------------------------------+-------------------------------+ + // | Masking-key (continued) | Payload Data | + // +-------------------------------- - - - - - - - - - - - - - - - + + // : Payload Data continued ... : + // + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + // | Payload Data continued ... | + // +---------------------------------------------------------------+ + + struct wsheader_type { + unsigned header_size; + bool fin; + bool mask; + enum opcode_type { + CONTINUATION = 0x0, + TEXT_FRAME = 0x1, + BINARY_FRAME = 0x2, + CLOSE = 8, + PING = 9, + PONG = 0xa, + } opcode; + int N0; + uint64_t N; + uint8_t masking_key[4]; + }; + + std::vector rxbuf; + std::vector txbuf; + std::vector receivedData; + + socket_t sockfd; + readyStateValues readyState; + bool useMask; + bool isRxBad; + + _RealWebSocket(socket_t sockfd, bool useMask) : sockfd(sockfd), readyState(OPEN), useMask(useMask), isRxBad(false) { + } + + readyStateValues getReadyState() const { + return readyState; + } + + void poll(int timeout) { // timeout in milliseconds + if (readyState == CLOSED) { + if (timeout > 0) { + timeval tv = { timeout/1000, (timeout%1000) * 1000 }; + select(0, NULL, NULL, NULL, &tv); + } + return; + } + if (timeout != 0) { + fd_set rfds; + fd_set wfds; + timeval tv = { timeout/1000, (timeout%1000) * 1000 }; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_SET(sockfd, &rfds); + if (txbuf.size()) { FD_SET(sockfd, &wfds); } + select(sockfd + 1, &rfds, &wfds, NULL, timeout > 0 ? &tv : NULL); + } + while (true) { + // FD_ISSET(0, &rfds) will be true + int N = rxbuf.size(); + ssize_t ret; + rxbuf.resize(N + 1500); + ret = recv(sockfd, (char*)&rxbuf[0] + N, 1500, 0); + if (false) { } + else if (ret < 0 && (socketerrno == SOCKET_EWOULDBLOCK || socketerrno == SOCKET_EAGAIN_EINPROGRESS)) { + rxbuf.resize(N); + break; + } + else if (ret <= 0) { + rxbuf.resize(N); + closesocket(sockfd); + readyState = CLOSED; + fputs(ret < 0 ? "Connection error!\n" : "Connection closed!\n", stderr); + break; + } + else { + rxbuf.resize(N + ret); + } + } + while (txbuf.size()) { + int ret = ::send(sockfd, (char*)&txbuf[0], txbuf.size(), 0); + if (false) { } // ?? + else if (ret < 0 && (socketerrno == SOCKET_EWOULDBLOCK || socketerrno == SOCKET_EAGAIN_EINPROGRESS)) { + break; + } + else if (ret <= 0) { + closesocket(sockfd); + readyState = CLOSED; + fputs(ret < 0 ? "Connection error!\n" : "Connection closed!\n", stderr); + break; + } + else { + txbuf.erase(txbuf.begin(), txbuf.begin() + ret); + } + } + if (!txbuf.size() && readyState == CLOSING) { + closesocket(sockfd); + readyState = CLOSED; + } + } + + virtual void _dispatch(Callback_Imp & callable) { + struct CallbackAdapter : public BytesCallback_Imp + // Adapt void(const std::string&) to void(const std::string&) + { + Callback_Imp& callable; + CallbackAdapter(Callback_Imp& callable) : callable(callable) { } + void operator()(const std::vector& message) { + std::string stringMessage(message.begin(), message.end()); + callable(stringMessage); + } + }; + CallbackAdapter bytesCallback(callable); + _dispatchBinary(bytesCallback); + } + + virtual void _dispatchBinary(BytesCallback_Imp& callable) { + // TODO: consider acquiring a lock on rxbuf... + while (true) { + wsheader_type ws; + if (rxbuf.size() < 2) { return; /* Need at least 2 */ } + const uint8_t * data = (uint8_t *) &rxbuf[0]; // peek, but don't consume + ws.fin = (data[0] & 0x80) == 0x80; + ws.opcode = (wsheader_type::opcode_type) (data[0] & 0x0f); + ws.mask = (data[1] & 0x80) == 0x80; + ws.N0 = (data[1] & 0x7f); + ws.header_size = 2 + (ws.N0 == 126? 2 : 0) + (ws.N0 == 127? 8 : 0) + (ws.mask? 4 : 0); + if (rxbuf.size() < ws.header_size) { return; /* Need: ws.header_size - rxbuf.size() */ } + int i = 0; + if (ws.N0 < 126) { + ws.N = ws.N0; + i = 2; + } + else if (ws.N0 == 126) { + ws.N = 0; + ws.N |= ((uint64_t) data[2]) << 8; + ws.N |= ((uint64_t) data[3]) << 0; + i = 4; + } + else if (ws.N0 == 127) { + ws.N = 0; + ws.N |= ((uint64_t) data[2]) << 56; + ws.N |= ((uint64_t) data[3]) << 48; + ws.N |= ((uint64_t) data[4]) << 40; + ws.N |= ((uint64_t) data[5]) << 32; + ws.N |= ((uint64_t) data[6]) << 24; + ws.N |= ((uint64_t) data[7]) << 16; + ws.N |= ((uint64_t) data[8]) << 8; + ws.N |= ((uint64_t) data[9]) << 0; + i = 10; + } + if (ws.mask) { + ws.masking_key[0] = ((uint8_t) data[i+0]) << 0; + ws.masking_key[1] = ((uint8_t) data[i+1]) << 0; + ws.masking_key[2] = ((uint8_t) data[i+2]) << 0; + ws.masking_key[3] = ((uint8_t) data[i+3]) << 0; + } + else { + ws.masking_key[0] = 0; + ws.masking_key[1] = 0; + ws.masking_key[2] = 0; + ws.masking_key[3] = 0; + } + + // Note: The checks above should hopefully ensure this addition + // cannot overflow: + if (rxbuf.size() < ws.header_size+ws.N) { return; /* Need: ws.header_size+ws.N - rxbuf.size() */ } + + // We got a whole message, now do something with it: + if (false) { } + else if ( + ws.opcode == wsheader_type::TEXT_FRAME + || ws.opcode == wsheader_type::BINARY_FRAME + || ws.opcode == wsheader_type::CONTINUATION + ) { + if (ws.mask) { for (size_t i = 0; i != ws.N; ++i) { rxbuf[i+ws.header_size] ^= ws.masking_key[i&0x3]; } } + receivedData.insert(receivedData.end(), rxbuf.begin()+ws.header_size, rxbuf.begin()+ws.header_size+(size_t)ws.N);// just feed + if (ws.fin) { + callable((const std::vector) receivedData); + receivedData.erase(receivedData.begin(), receivedData.end()); + std::vector ().swap(receivedData);// free memory + } + } + else if (ws.opcode == wsheader_type::PING) { + if (ws.mask) { for (size_t i = 0; i != ws.N; ++i) { rxbuf[i+ws.header_size] ^= ws.masking_key[i&0x3]; } } + std::string data(rxbuf.begin()+ws.header_size, rxbuf.begin()+ws.header_size+(size_t)ws.N); + sendData(wsheader_type::PONG, data.size(), data.begin(), data.end()); + } + else if (ws.opcode == wsheader_type::PONG) { } + else if (ws.opcode == wsheader_type::CLOSE) { close(); } + else { fprintf(stderr, "ERROR: Got unexpected WebSocket message.\n"); close(); } + + rxbuf.erase(rxbuf.begin(), rxbuf.begin() + ws.header_size+(size_t)ws.N); + } + } + + void sendPing() { + std::string empty; + sendData(wsheader_type::PING, empty.size(), empty.begin(), empty.end()); + } + + void send(const std::string& message) { + sendData(wsheader_type::TEXT_FRAME, message.size(), message.begin(), message.end()); + } + + void sendBinary(const std::string& message) { + sendData(wsheader_type::BINARY_FRAME, message.size(), message.begin(), message.end()); + } + + void sendBinary(const std::vector& message) { + sendData(wsheader_type::BINARY_FRAME, message.size(), message.begin(), message.end()); + } + + template + void sendData(wsheader_type::opcode_type type, uint64_t message_size, Iterator message_begin, Iterator message_end) { + // TODO: + // Masking key should (must) be derived from a high quality random + // number generator, to mitigate attacks on non-WebSocket friendly + // middleware: + const uint8_t masking_key[4] = { 0x12, 0x34, 0x56, 0x78 }; + // TODO: consider acquiring a lock on txbuf... + if (readyState == CLOSING || readyState == CLOSED) { return; } + std::vector header; + header.assign(2 + (message_size >= 126 ? 2 : 0) + (message_size >= 65536 ? 6 : 0) + (useMask ? 4 : 0), 0); + header[0] = 0x80 | type; + if (false) { } + else if (message_size < 126) { + header[1] = (message_size & 0xff) | (useMask ? 0x80 : 0); + if (useMask) { + header[2] = masking_key[0]; + header[3] = masking_key[1]; + header[4] = masking_key[2]; + header[5] = masking_key[3]; + } + } + else if (message_size < 65536) { + header[1] = 126 | (useMask ? 0x80 : 0); + header[2] = (message_size >> 8) & 0xff; + header[3] = (message_size >> 0) & 0xff; + if (useMask) { + header[4] = masking_key[0]; + header[5] = masking_key[1]; + header[6] = masking_key[2]; + header[7] = masking_key[3]; + } + } + else { // TODO: run coverage testing here + header[1] = 127 | (useMask ? 0x80 : 0); + header[2] = (message_size >> 56) & 0xff; + header[3] = (message_size >> 48) & 0xff; + header[4] = (message_size >> 40) & 0xff; + header[5] = (message_size >> 32) & 0xff; + header[6] = (message_size >> 24) & 0xff; + header[7] = (message_size >> 16) & 0xff; + header[8] = (message_size >> 8) & 0xff; + header[9] = (message_size >> 0) & 0xff; + if (useMask) { + header[10] = masking_key[0]; + header[11] = masking_key[1]; + header[12] = masking_key[2]; + header[13] = masking_key[3]; + } + } + // N.B. - txbuf will keep growing until it can be transmitted over the socket: + txbuf.insert(txbuf.end(), header.begin(), header.end()); + txbuf.insert(txbuf.end(), message_begin, message_end); + if (useMask) { + size_t message_offset = txbuf.size() - message_size; + for (size_t i = 0; i != message_size; ++i) { + txbuf[message_offset + i] ^= masking_key[i&0x3]; + } + } + } + + void close() { + if(readyState == CLOSING || readyState == CLOSED) { return; } + readyState = CLOSING; + uint8_t closeFrame[6] = {0x88, 0x80, 0x00, 0x00, 0x00, 0x00}; // last 4 bytes are a masking key + std::vector header(closeFrame, closeFrame+6); + txbuf.insert(txbuf.end(), header.begin(), header.end()); + } + +}; + + +easywsclient::WebSocket::pointer from_url(const std::string& url, bool useMask, const std::string& origin) { + char host[512]; + int port; + char path[512]; + if (url.size() >= 512) { + fprintf(stderr, "ERROR: url size limit exceeded: %s\n", url.c_str()); + return NULL; + } + if (origin.size() >= 200) { + fprintf(stderr, "ERROR: origin size limit exceeded: %s\n", origin.c_str()); + return NULL; + } + if (false) { } + else if (sscanf(url.c_str(), "ws://%[^:/]:%d/%s", host, &port, path) == 3) { + } + else if (sscanf(url.c_str(), "ws://%[^:/]/%s", host, path) == 2) { + port = 80; + } + else if (sscanf(url.c_str(), "ws://%[^:/]:%d", host, &port) == 2) { + path[0] = '\0'; + } + else if (sscanf(url.c_str(), "ws://%[^:/]", host) == 1) { + port = 80; + path[0] = '\0'; + } + else { + fprintf(stderr, "ERROR: Could not parse WebSocket url: %s\n", url.c_str()); + return NULL; + } + //fprintf(stderr, "easywsclient: connecting: host=%s port=%d path=/%s\n", host, port, path); + socket_t sockfd = hostname_connect(host, port); + if (sockfd == INVALID_SOCKET) { + fprintf(stderr, "Unable to connect to %s:%d\n", host, port); + return NULL; + } + { + // XXX: this should be done non-blocking, + char line[1024]; + int status; + int i; + snprintf(line, 1024, "GET /%s HTTP/1.1\r\n", path); ::send(sockfd, line, strlen(line), 0); + if (port == 80) { + snprintf(line, 1024, "Host: %s\r\n", host); ::send(sockfd, line, strlen(line), 0); + } + else { + snprintf(line, 1024, "Host: %s:%d\r\n", host, port); ::send(sockfd, line, strlen(line), 0); + } + snprintf(line, 1024, "Upgrade: websocket\r\n"); ::send(sockfd, line, strlen(line), 0); + snprintf(line, 1024, "Connection: Upgrade\r\n"); ::send(sockfd, line, strlen(line), 0); + if (!origin.empty()) { + snprintf(line, 1024, "Origin: %s\r\n", origin.c_str()); ::send(sockfd, line, strlen(line), 0); + } + snprintf(line, 1024, "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"); ::send(sockfd, line, strlen(line), 0); + snprintf(line, 1024, "Sec-WebSocket-Version: 13\r\n"); ::send(sockfd, line, strlen(line), 0); + snprintf(line, 1024, "\r\n"); ::send(sockfd, line, strlen(line), 0); + for (i = 0; i < 2 || (i < 1023 && line[i-2] != '\r' && line[i-1] != '\n'); ++i) { if (recv(sockfd, line+i, 1, 0) == 0) { return NULL; } } + line[i] = 0; + if (i == 1023) { fprintf(stderr, "ERROR: Got invalid status line connecting to: %s\n", url.c_str()); return NULL; } + if (sscanf(line, "HTTP/1.1 %d", &status) != 1 || status != 101) { fprintf(stderr, "ERROR: Got bad status connecting to %s: %s", url.c_str(), line); return NULL; } + // TODO: verify response headers, + while (true) { + for (i = 0; i < 2 || (i < 1023 && line[i-2] != '\r' && line[i-1] != '\n'); ++i) { if (recv(sockfd, line+i, 1, 0) == 0) { return NULL; } } + if (line[0] == '\r' && line[1] == '\n') { break; } + } + } + int flag = 1; + setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)); // Disable Nagle's algorithm +#ifdef _WIN32 + u_long on = 1; + ioctlsocket(sockfd, FIONBIO, &on); +#else + fcntl(sockfd, F_SETFL, O_NONBLOCK); +#endif + //fprintf(stderr, "Connected to: %s\n", url.c_str()); + return easywsclient::WebSocket::pointer(new _RealWebSocket(sockfd, useMask)); +} + +} // end of module-only namespace + + + +namespace easywsclient { + +WebSocket::pointer WebSocket::create_dummy() { + static pointer dummy = pointer(new _DummyWebSocket); + return dummy; +} + + +WebSocket::pointer WebSocket::from_url(const std::string& url, const std::string& origin) { + return ::from_url(url, true, origin); +} + +WebSocket::pointer WebSocket::from_url_no_mask(const std::string& url, const std::string& origin) { + return ::from_url(url, false, origin); +} + + +} // namespace easywsclient diff --git a/src/spice2x/external/easywsclient/easywsclient.hpp b/src/spice2x/external/easywsclient/easywsclient.hpp index 0cad04b..df60500 100644 --- a/src/spice2x/external/easywsclient/easywsclient.hpp +++ b/src/spice2x/external/easywsclient/easywsclient.hpp @@ -1,73 +1,73 @@ -#ifndef EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD -#define EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD - -// This code comes from: -// https://github.com/dhbaird/easywsclient -// -// To get the latest version: -// wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.hpp -// wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.cpp - -#include -#include -#include - -namespace easywsclient { - -struct Callback_Imp { virtual void operator()(const std::string& message) = 0; }; -struct BytesCallback_Imp { virtual void operator()(const std::vector& message) = 0; }; - -class WebSocket { - public: - typedef WebSocket * pointer; - typedef enum readyStateValues { CLOSING, CLOSED, CONNECTING, OPEN } readyStateValues; - - // Factories: - static pointer create_dummy(); - static pointer from_url(const std::string& url, const std::string& origin = std::string()); - static pointer from_url_no_mask(const std::string& url, const std::string& origin = std::string()); - - // Interfaces: - virtual ~WebSocket() { } - virtual void poll(int timeout = 0) = 0; // timeout in milliseconds - virtual void send(const std::string& message) = 0; - virtual void sendBinary(const std::string& message) = 0; - virtual void sendBinary(const std::vector& message) = 0; - virtual void sendPing() = 0; - virtual void close() = 0; - virtual readyStateValues getReadyState() const = 0; - - template - void dispatch(Callable callable) - // For callbacks that accept a string argument. - { // N.B. this is compatible with both C++11 lambdas, functors and C function pointers - struct _Callback : public Callback_Imp { - Callable& callable; - _Callback(Callable& callable) : callable(callable) { } - void operator()(const std::string& message) { callable(message); } - }; - _Callback callback(callable); - _dispatch(callback); - } - - template - void dispatchBinary(Callable callable) - // For callbacks that accept a std::vector argument. - { // N.B. this is compatible with both C++11 lambdas, functors and C function pointers - struct _Callback : public BytesCallback_Imp { - Callable& callable; - _Callback(Callable& callable) : callable(callable) { } - void operator()(const std::vector& message) { callable(message); } - }; - _Callback callback(callable); - _dispatchBinary(callback); - } - - protected: - virtual void _dispatch(Callback_Imp& callable) = 0; - virtual void _dispatchBinary(BytesCallback_Imp& callable) = 0; -}; - -} // namespace easywsclient - -#endif /* EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD */ +#ifndef EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD +#define EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD + +// This code comes from: +// https://github.com/dhbaird/easywsclient +// +// To get the latest version: +// wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.hpp +// wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.cpp + +#include +#include +#include + +namespace easywsclient { + +struct Callback_Imp { virtual void operator()(const std::string& message) = 0; }; +struct BytesCallback_Imp { virtual void operator()(const std::vector& message) = 0; }; + +class WebSocket { + public: + typedef WebSocket * pointer; + typedef enum readyStateValues { CLOSING, CLOSED, CONNECTING, OPEN } readyStateValues; + + // Factories: + static pointer create_dummy(); + static pointer from_url(const std::string& url, const std::string& origin = std::string()); + static pointer from_url_no_mask(const std::string& url, const std::string& origin = std::string()); + + // Interfaces: + virtual ~WebSocket() { } + virtual void poll(int timeout = 0) = 0; // timeout in milliseconds + virtual void send(const std::string& message) = 0; + virtual void sendBinary(const std::string& message) = 0; + virtual void sendBinary(const std::vector& message) = 0; + virtual void sendPing() = 0; + virtual void close() = 0; + virtual readyStateValues getReadyState() const = 0; + + template + void dispatch(Callable callable) + // For callbacks that accept a string argument. + { // N.B. this is compatible with both C++11 lambdas, functors and C function pointers + struct _Callback : public Callback_Imp { + Callable& callable; + _Callback(Callable& callable) : callable(callable) { } + void operator()(const std::string& message) { callable(message); } + }; + _Callback callback(callable); + _dispatch(callback); + } + + template + void dispatchBinary(Callable callable) + // For callbacks that accept a std::vector argument. + { // N.B. this is compatible with both C++11 lambdas, functors and C function pointers + struct _Callback : public BytesCallback_Imp { + Callable& callable; + _Callback(Callable& callable) : callable(callable) { } + void operator()(const std::vector& message) { callable(message); } + }; + _Callback callback(callable); + _dispatchBinary(callback); + } + + protected: + virtual void _dispatch(Callback_Imp& callable) = 0; + virtual void _dispatchBinary(BytesCallback_Imp& callable) = 0; +}; + +} // namespace easywsclient + +#endif /* EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD */ diff --git a/src/spice2x/external/imgui/imgui_memory_editor.h b/src/spice2x/external/imgui/imgui_memory_editor.h index 8819ef3..57c4e52 100644 --- a/src/spice2x/external/imgui/imgui_memory_editor.h +++ b/src/spice2x/external/imgui/imgui_memory_editor.h @@ -1,836 +1,836 @@ -// Mini memory editor for Dear ImGui (to embed in your game/tools) -// Get latest version at http://www.github.com/ocornut/imgui_club -// Licensed under The MIT License (MIT) - -// Right-click anywhere to access the Options menu! -// You can adjust the keyboard repeat delay/rate in ImGuiIO. -// The code assume a mono-space font for simplicity! -// If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before calling this. -// -// Usage: -// // Create a window and draw memory editor inside it: -// static MemoryEditor mem_edit_1; -// static char data[0x10000]; -// size_t data_size = 0x10000; -// mem_edit_1.DrawWindow("Memory Editor", data, data_size); -// -// Usage: -// // If you already have a window, use DrawContents() instead: -// static MemoryEditor mem_edit_2; -// ImGui::Begin("MyWindow") -// mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this); -// ImGui::End(); -// -// Changelog: -// - v0.10: initial version -// - v0.23 (2017/08/17): added to github. fixed right-arrow triggering a byte write. -// - v0.24 (2018/06/02): changed DragInt("Rows" to use a %d data format (which is desirable since imgui 1.61). -// - v0.25 (2018/07/11): fixed wording: all occurrences of "Rows" renamed to "Columns". -// - v0.26 (2018/08/02): fixed clicking on hex region -// - v0.30 (2018/08/02): added data preview for common data types -// - v0.31 (2018/10/10): added OptUpperCaseHex option to select lower/upper casing display [@samhocevar] -// - v0.32 (2018/10/10): changed signatures to use void* instead of unsigned char* -// - v0.33 (2018/10/10): added OptShowOptions option to hide all the interactive option setting. -// - v0.34 (2019/05/07): binary preview now applies endianness setting [@nicolasnoble] -// - v0.35 (2020/01/29): using ImGuiDataType available since Dear ImGui 1.69. -// - v0.36 (2020/05/05): minor tweaks, minor refactor. -// - v0.40 (2020/10/04): fix misuse of ImGuiListClipper API, broke with Dear ImGui 1.79. made cursor position appears on left-side of edit box. option popup appears on mouse release. fix MSVC warnings where _CRT_SECURE_NO_WARNINGS wasn't working in recent versions. -// - v0.41 (2020/10/05): fix when using with keyboard/gamepad navigation enabled. -// - v0.42 (2020/10/14): fix for . character in ASCII view always being greyed out. -// - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel] -// - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width. -// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper, relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size. -// - v0.51 (2024/02/22): fix for layout change in 1.89 when using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#34) -// - v0.52 (2024/03/08): removed unnecessary GetKeyIndex() calls, they are a no-op since 1.87. -// - v0.53 (2024/05/27): fixed right-click popup from not appearing when using DrawContents(). warning fixes. (#35) -// - v0.54 (2024/07/29): allow ReadOnly mode to still select and preview data. (#46) [@DeltaGW2]) -// - v0.55 (2024/08/19): added BgColorFn to allow setting background colors independently from highlighted selection. (#27) [@StrikerX3] -// added MouseHoveredAddr public readable field. (#47, #27) [@StrikerX3] -// fixed a data preview crash with 1.91.0 WIP. fixed contiguous highlight color when using data preview. -// *BREAKING* added UserData field passed to all optional function handlers: ReadFn, WriteFn, HighlightFn, BgColorFn. (#50) [@silverweed] -// - v0.56 (2024/11/04): fixed MouseHovered, MouseHoveredAddr not being set when hovering a byte being edited. (#54) -// - v0.57 (2025/03/26): fixed warnings. using ImGui's ImSXX/ImUXX types instead of e.g. int32_t/uint32_t. (#56) -// - v0.58 (2025/03/31): fixed extraneous footer spacing (added in 0.51) breaking vertical auto-resize. (#53) -// - v0.59 (2025/04/08): fixed GotoAddrAndHighlight() not working if OptShowOptions is disabled. -// -// TODO: -// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day. -// - PageUp/PageDown are not supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own... -// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame. -// - Using InputText() is awkward and maybe overkill here, consider implementing something custom. - -#pragma once - -#include // sprintf, scanf -#include // uint8_t, etc. - -#if defined(_MSC_VER) && !defined(snprintf) -#define ImSnprintf _snprintf -#else -#define ImSnprintf snprintf -#endif -#if defined(_MSC_VER) && !defined(__clang__) -#define _PRISizeT "I" -#else -#define _PRISizeT "z" -#endif - -#if defined(_MSC_VER) || defined(_UCRT) -#pragma warning (push) -#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe. -#endif - -struct MemoryEditor -{ - enum DataFormat - { - DataFormat_Bin = 0, - DataFormat_Dec = 1, - DataFormat_Hex = 2, - DataFormat_COUNT - }; - - // Settings - bool Open; // = true // set to false when DrawWindow() was closed. ignore if not using DrawWindow(). - bool ReadOnly; // = false // disable any editing. - int Cols; // = 16 // number of columns to display. - bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them. - bool OptShowDataPreview; // = false // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes. - bool OptShowHexII; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X". - bool OptShowAscii; // = true // display ASCII representation on the right side. - bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color. - bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff". - int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols. - int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr). - float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add custom widgets - ImU32 HighlightColor; // // background color of highlighted bytes. - - // Function handlers - ImU8 (*ReadFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to read bytes. - void (*WriteFn)(ImU8* mem, size_t off, ImU8 d, void* user_data); // = 0 // optional handler to write bytes. - bool (*HighlightFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return Highlight property (to support non-contiguous highlighting). - ImU32 (*BgColorFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return custom background color of individual bytes. - void* UserData; // = NULL // user data forwarded to the function handlers - - // Public read-only data - bool MouseHovered; // set when mouse is hovering a value. - size_t MouseHoveredAddr; // the address currently being hovered if MouseHovered is set. - - // [Internal State] - bool ContentsWidthChanged; - size_t DataPreviewAddr; - size_t DataEditingAddr; - bool DataEditingTakeFocus; - char DataInputBuf[32]; - char AddrInputBuf[32]; - size_t GotoAddr; - size_t HighlightMin, HighlightMax; - int PreviewEndianness; - ImGuiDataType PreviewDataType; - - MemoryEditor() - { - // Settings - Open = true; - ReadOnly = false; - Cols = 16; - OptShowOptions = true; - OptShowDataPreview = false; - OptShowHexII = false; - OptShowAscii = true; - OptGreyOutZeroes = true; - OptUpperCaseHex = true; - OptMidColsCount = 8; - OptAddrDigitsCount = 0; - OptFooterExtraHeight = 0.0f; - HighlightColor = IM_COL32(255, 255, 255, 50); - ReadFn = nullptr; - WriteFn = nullptr; - HighlightFn = nullptr; - BgColorFn = nullptr; - UserData = nullptr; - - // State/Internals - ContentsWidthChanged = false; - DataPreviewAddr = DataEditingAddr = (size_t)-1; - DataEditingTakeFocus = false; - memset(DataInputBuf, 0, sizeof(DataInputBuf)); - memset(AddrInputBuf, 0, sizeof(AddrInputBuf)); - GotoAddr = (size_t)-1; - MouseHovered = false; - MouseHoveredAddr = 0; - HighlightMin = HighlightMax = (size_t)-1; - PreviewEndianness = 0; - PreviewDataType = ImGuiDataType_S32; - } - - void GotoAddrAndHighlight(size_t addr_min, size_t addr_max) - { - GotoAddr = addr_min; - HighlightMin = addr_min; - HighlightMax = addr_max; - } - - struct Sizes - { - int AddrDigitsCount; // Number of digits required to represent maximum address. - float LineHeight; // Height of each line (no spacing). - float GlyphWidth; // Glyph width (assume mono-space). - float HexCellWidth; // Width of a hex edit cell ~2.5f * GlypHWidth. - float SpacingBetweenMidCols; // Spacing between each columns section (OptMidColsCount). - float OffsetHexMinX; - float OffsetHexMaxX; - float OffsetAsciiMinX; - float OffsetAsciiMaxX; - float WindowWidth; // Ideal window width. - - Sizes() { memset(this, 0, sizeof(*this)); } - }; - - void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr) - { - ImGuiStyle& style = ImGui::GetStyle(); - s.AddrDigitsCount = OptAddrDigitsCount; - if (s.AddrDigitsCount == 0) - for (size_t n = base_display_addr + mem_size - 1; n > 0; n >>= 4) - s.AddrDigitsCount++; - s.LineHeight = ImGui::GetTextLineHeight(); - s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space - s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to easily catch clicks everywhere - s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing - s.OffsetHexMinX = (s.AddrDigitsCount + 2) * s.GlyphWidth; - s.OffsetHexMaxX = s.OffsetHexMinX + (s.HexCellWidth * Cols); - s.OffsetAsciiMinX = s.OffsetAsciiMaxX = s.OffsetHexMaxX; - if (OptShowAscii) - { - s.OffsetAsciiMinX = s.OffsetHexMaxX + s.GlyphWidth * 1; - if (OptMidColsCount > 0) - s.OffsetAsciiMinX += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols; - s.OffsetAsciiMaxX = s.OffsetAsciiMinX + Cols * s.GlyphWidth; - } - s.WindowWidth = s.OffsetAsciiMaxX + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth; - } - - // Standalone Memory Editor window - void DrawWindow(const char* title, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000) - { - Sizes s; - CalcSizes(s, mem_size, base_display_addr); - ImGui::SetNextWindowSize(ImVec2(s.WindowWidth, s.WindowWidth * 0.60f), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX)); - - Open = true; - if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar)) - { - DrawContents(mem_data, mem_size, base_display_addr); - if (ContentsWidthChanged) - { - CalcSizes(s, mem_size, base_display_addr); - ImGui::SetWindowSize(ImVec2(s.WindowWidth, ImGui::GetWindowSize().y)); - } - } - ImGui::End(); - } - - // Memory Editor contents only - void DrawContents(void* mem_data_void, size_t mem_size, size_t base_display_addr = 0x0000) - { - if (Cols < 1) - Cols = 1; - - ImU8* mem_data = (ImU8*)mem_data_void; - Sizes s; - CalcSizes(s, mem_size, base_display_addr); - ImGuiStyle& style = ImGui::GetStyle(); - - const ImVec2 contents_pos_start = ImGui::GetCursorScreenPos(); - - // We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window. - // This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move. - const float height_separator = style.ItemSpacing.y; - float footer_height = OptFooterExtraHeight; - if (OptShowOptions) - footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1; - if (OptShowDataPreview) - footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3; - ImGui::BeginChild("##scrolling", ImVec2(-FLT_MIN, -footer_height), ImGuiChildFlags_None, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - - // We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function. - const ImVec2 avail_size = ImGui::GetContentRegionAvail(); - const int line_total_count = (int)((mem_size + Cols - 1) / Cols); - ImGuiListClipper clipper; - clipper.Begin(line_total_count, s.LineHeight); - - bool data_next = false; - - if (DataEditingAddr >= mem_size) - DataEditingAddr = (size_t)-1; - if (DataPreviewAddr >= mem_size) - DataPreviewAddr = (size_t)-1; - - size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0; - - size_t data_editing_addr_next = (size_t)-1; - if (DataEditingAddr != (size_t)-1) - { - // Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered) - if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; } - else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols){ data_editing_addr_next = DataEditingAddr + Cols; } - else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; } - else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; } - } - - // Draw vertical separator - ImVec2 window_pos = ImGui::GetWindowPos(); - if (OptShowAscii) - draw_list->AddLine(ImVec2(window_pos.x + s.OffsetAsciiMinX - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.OffsetAsciiMinX - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border)); - - const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text); - const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text; - - const char* format_address = OptUpperCaseHex ? "%0*" _PRISizeT "X: " : "%0*" _PRISizeT "x: "; - const char* format_data = OptUpperCaseHex ? "%0*" _PRISizeT "X" : "%0*" _PRISizeT "x"; - const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x"; - const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x "; - - MouseHovered = false; - MouseHoveredAddr = 0; - - while (clipper.Step()) - for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines - { - size_t addr = (size_t)line_i * Cols; - ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr); - - // Draw Hexadecimal - for (int n = 0; n < Cols && addr < mem_size; n++, addr++) - { - float byte_pos_x = s.OffsetHexMinX + s.HexCellWidth * n; - if (OptMidColsCount > 0) - byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols; - ImGui::SameLine(byte_pos_x); - - // Draw highlight or custom background color - const bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax); - const bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, UserData)); - const bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size); - - ImU32 bg_color = 0; - bool is_next_byte_highlighted = false; - if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview) - { - is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1, UserData)) || (addr + 1 < DataPreviewAddr + preview_data_type_size)); - bg_color = HighlightColor; - } - else if (BgColorFn != nullptr) - { - is_next_byte_highlighted = (addr + 1 < mem_size) && ((BgColorFn(mem_data, addr + 1, UserData) & IM_COL32_A_MASK) != 0); - bg_color = BgColorFn(mem_data, addr, UserData); - } - if (bg_color != 0) - { - float bg_width = s.GlyphWidth * 2; - if (is_next_byte_highlighted || (n + 1 == Cols)) - { - bg_width = s.HexCellWidth; - if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0) - bg_width += s.SpacingBetweenMidCols; - } - ImVec2 pos = ImGui::GetCursorScreenPos(); - draw_list->AddRectFilled(pos, ImVec2(pos.x + bg_width, pos.y + s.LineHeight), bg_color); - } - - if (DataEditingAddr == addr) - { - // Display text input on current byte - bool data_write = false; - ImGui::PushID((void*)addr); - if (DataEditingTakeFocus) - { - ImGui::SetKeyboardFocusHere(0); - ImSnprintf(AddrInputBuf, 32, format_data, s.AddrDigitsCount, base_display_addr + addr); - ImSnprintf(DataInputBuf, 32, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]); - } - struct InputTextUserData - { - // FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here. - static int Callback(ImGuiInputTextCallbackData* data) - { - InputTextUserData* user_data = (InputTextUserData*)data->UserData; - if (!data->HasSelection()) - user_data->CursorPos = data->CursorPos; -#if IMGUI_VERSION_NUM < 19102 - if (data->Flags & ImGuiInputTextFlags_ReadOnly) - return 0; -#endif - if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen) - { - // When not editing a byte, always refresh its InputText content pulled from underlying memory data - // (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there) - data->DeleteChars(0, data->BufTextLen); - data->InsertChars(0, user_data->CurrentBufOverwrite); - data->SelectionStart = 0; - data->SelectionEnd = 2; - data->CursorPos = 0; - } - return 0; - } - char CurrentBufOverwrite[3]; // Input - int CursorPos; // Output - }; - InputTextUserData input_text_user_data; - input_text_user_data.CursorPos = -1; - ImSnprintf(input_text_user_data.CurrentBufOverwrite, 3, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways; - if (ReadOnly) - flags |= ImGuiInputTextFlags_ReadOnly; - flags |= ImGuiInputTextFlags_AlwaysOverwrite; // was ImGuiInputTextFlags_AlwaysInsertMode - ImGui::SetNextItemWidth(s.GlyphWidth * 2); - if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, InputTextUserData::Callback, &input_text_user_data)) - data_write = data_next = true; - else if (!DataEditingTakeFocus && !ImGui::IsItemActive()) - DataEditingAddr = data_editing_addr_next = (size_t)-1; - DataEditingTakeFocus = false; - if (input_text_user_data.CursorPos >= 2) - data_write = data_next = true; - if (data_editing_addr_next != (size_t)-1) - data_write = data_next = false; - unsigned int data_input_value = 0; - if (!ReadOnly && data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1) - { - if (WriteFn) - WriteFn(mem_data, addr, (ImU8)data_input_value, UserData); - else - mem_data[addr] = (ImU8)data_input_value; - } - if (ImGui::IsItemHovered()) - { - MouseHovered = true; - MouseHoveredAddr = addr; - } - ImGui::PopID(); - } - else - { - // NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on. - ImU8 b = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]; - - if (OptShowHexII) - { - if ((b >= 32 && b < 128)) - ImGui::Text(".%c ", b); - else if (b == 0xFF && OptGreyOutZeroes) - ImGui::TextDisabled("## "); - else if (b == 0x00) - ImGui::Text(" "); - else - ImGui::Text(format_byte_space, b); - } - else - { - if (b == 0 && OptGreyOutZeroes) - ImGui::TextDisabled("00 "); - else - ImGui::Text(format_byte_space, b); - } - if (ImGui::IsItemHovered()) - { - MouseHovered = true; - MouseHoveredAddr = addr; - if (ImGui::IsMouseClicked(0)) - { - DataEditingTakeFocus = true; - data_editing_addr_next = addr; - } - } - } - } - - if (OptShowAscii) - { - // Draw ASCII values - ImGui::SameLine(s.OffsetAsciiMinX); - ImVec2 pos = ImGui::GetCursorScreenPos(); - addr = (size_t)line_i * Cols; - - const float mouse_off_x = ImGui::GetIO().MousePos.x - pos.x; - const size_t mouse_addr = (mouse_off_x >= 0.0f && mouse_off_x < s.OffsetAsciiMaxX - s.OffsetAsciiMinX) ? addr + (size_t)(mouse_off_x / s.GlyphWidth) : (size_t)-1; - - ImGui::PushID(line_i); - if (ImGui::InvisibleButton("ascii", ImVec2(s.OffsetAsciiMaxX - s.OffsetAsciiMinX, s.LineHeight))) - { - DataEditingAddr = DataPreviewAddr = mouse_addr; - DataEditingTakeFocus = true; - } - if (ImGui::IsItemHovered()) - { - MouseHovered = true; - MouseHoveredAddr = mouse_addr; - } - ImGui::PopID(); - for (int n = 0; n < Cols && addr < mem_size; n++, addr++) - { - if (addr == DataEditingAddr) - { - draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg)); - draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg)); - } - else if (BgColorFn) - { - draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), BgColorFn(mem_data, addr, UserData)); - } - unsigned char c = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]; - char display_c = (c < 32 || c >= 128) ? '.' : c; - draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1); - pos.x += s.GlyphWidth; - } - } - } - ImGui::PopStyleVar(2); - const float child_width = ImGui::GetWindowSize().x; - ImGui::EndChild(); - - // Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child) - ImVec2 backup_pos = ImGui::GetCursorScreenPos(); - ImGui::SetCursorPosX(s.WindowWidth); - ImGui::Dummy(ImVec2(0.0f, 0.0f)); - ImGui::SetCursorScreenPos(backup_pos); - - if (data_next && DataEditingAddr + 1 < mem_size) - { - DataEditingAddr = DataPreviewAddr = DataEditingAddr + 1; - DataEditingTakeFocus = true; - } - else if (data_editing_addr_next != (size_t)-1) - { - DataEditingAddr = DataPreviewAddr = data_editing_addr_next; - DataEditingTakeFocus = true; - } - - const bool lock_show_data_preview = OptShowDataPreview; - if (OptShowOptions) - { - ImGui::Separator(); - DrawOptionsLine(s, mem_data, mem_size, base_display_addr); - } - - if (lock_show_data_preview) - { - ImGui::Separator(); - DrawPreviewLine(s, mem_data, mem_size, base_display_addr); - } - - if (GotoAddr != (size_t)-1) - { - if (GotoAddr < mem_size) - { - ImGui::BeginChild("##scrolling"); - ImGui::SetScrollY((GotoAddr / Cols) * ImGui::GetTextLineHeight() - avail_size.y * 0.5f); - ImGui::EndChild(); - DataEditingAddr = DataPreviewAddr = GotoAddr; - DataEditingTakeFocus = true; - } - GotoAddr = (size_t)-1; - } - - const ImVec2 contents_pos_end(contents_pos_start.x + child_width, ImGui::GetCursorScreenPos().y); - //ImGui::GetForegroundDrawList()->AddRect(contents_pos_start, contents_pos_end, IM_COL32(255, 0, 0, 255)); - if (OptShowOptions) - if (ImGui::IsMouseHoveringRect(contents_pos_start, contents_pos_end)) - if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right)) - ImGui::OpenPopup("OptionsPopup"); - - if (ImGui::BeginPopup("OptionsPopup")) - { - ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f); - if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; } - ImGui::Checkbox("Show Data Preview", &OptShowDataPreview); - ImGui::Checkbox("Show HexII", &OptShowHexII); - if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; } - ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes); - ImGui::Checkbox("Uppercase Hex", &OptUpperCaseHex); - - ImGui::EndPopup(); - } - } - - void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr) - { - IM_UNUSED(mem_data); - ImGuiStyle& style = ImGui::GetStyle(); - const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x"; - - // Options menu - if (ImGui::Button("Options")) - ImGui::OpenPopup("OptionsPopup"); - - ImGui::SameLine(); - ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1); - ImGui::SameLine(); - ImGui::SetNextItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth + style.FramePadding.x * 2.0f); - if (ImGui::InputText("##addr", AddrInputBuf, IM_ARRAYSIZE(AddrInputBuf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) - { - size_t goto_addr; - if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1) - { - GotoAddr = goto_addr - base_display_addr; - HighlightMin = HighlightMax = (size_t)-1; - } - } - - //if (MouseHovered) - //{ - // ImGui::SameLine(); - // ImGui::Text("Hovered: %p", MouseHoveredAddr); - //} - } - - void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr) - { - IM_UNUSED(base_display_addr); - ImU8* mem_data = (ImU8*)mem_data_void; - ImGuiStyle& style = ImGui::GetStyle(); - ImGui::AlignTextToFramePadding(); - ImGui::Text("Preview as:"); - ImGui::SameLine(); - ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x); - - static const ImGuiDataType supported_data_types[] = { ImGuiDataType_S8, ImGuiDataType_U8, ImGuiDataType_S16, ImGuiDataType_U16, ImGuiDataType_S32, ImGuiDataType_U32, ImGuiDataType_S64, ImGuiDataType_U64, ImGuiDataType_Float, ImGuiDataType_Double }; - if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest)) - { - for (int n = 0; n < IM_ARRAYSIZE(supported_data_types); n++) - { - ImGuiDataType data_type = supported_data_types[n]; - if (ImGui::Selectable(DataTypeGetDesc(data_type), PreviewDataType == data_type)) - PreviewDataType = data_type; - } - ImGui::EndCombo(); - } - ImGui::SameLine(); - ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x); - ImGui::Combo("##combo_endianness", &PreviewEndianness, "LE\0BE\0\0"); - - char buf[128] = ""; - float x = s.GlyphWidth * 6.0f; - bool has_value = DataPreviewAddr != (size_t)-1; - if (has_value) - DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf)); - ImGui::Text("Dec"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A"); - if (has_value) - DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf)); - ImGui::Text("Hex"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A"); - if (has_value) - DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf)); - buf[IM_ARRAYSIZE(buf) - 1] = 0; - ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A"); - } - - // Utilities for Data Preview (since we don't access imgui_internal.h) - // FIXME: This technically depends on ImGuiDataType order. - const char* DataTypeGetDesc(ImGuiDataType data_type) const - { - const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" }; - IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(descs)); - return descs[data_type]; - } - - size_t DataTypeGetSize(ImGuiDataType data_type) const - { - const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) }; - IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(sizes)); - return sizes[data_type]; - } - - const char* DataFormatGetDesc(DataFormat data_format) const - { - const char* descs[] = { "Bin", "Dec", "Hex" }; - IM_ASSERT(data_format >= 0 && data_format < DataFormat_COUNT); - return descs[data_format]; - } - - bool IsBigEndian() const - { - ImU16 x = 1; - char c[2]; - memcpy(c, &x, 2); - return c[0] != 0; - } - - static void* EndiannessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian) - { - if (is_little_endian) - { - ImU8* dst = (ImU8*)_dst; - ImU8* src = (ImU8*)_src + s - 1; - for (int i = 0, n = (int)s; i < n; ++i) - memcpy(dst++, src--, 1); - return _dst; - } - else - { - return memcpy(_dst, _src, s); - } - } - - static void* EndiannessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian) - { - if (is_little_endian) - { - return memcpy(_dst, _src, s); - } - else - { - ImU8* dst = (ImU8*)_dst; - ImU8* src = (ImU8*)_src + s - 1; - for (int i = 0, n = (int)s; i < n; ++i) - memcpy(dst++, src--, 1); - return _dst; - } - } - - void* EndiannessCopy(void* dst, void* src, size_t size) const - { - static void* (*fp)(void*, void*, size_t, int) = nullptr; - if (fp == nullptr) - fp = IsBigEndian() ? EndiannessCopyBigEndian : EndiannessCopyLittleEndian; - return fp(dst, src, size, PreviewEndianness); - } - - const char* FormatBinary(const ImU8* buf, int width) const - { - IM_ASSERT(width <= 64); - size_t out_n = 0; - static char out_buf[64 + 8 + 1]; - int n = width / 8; - for (int j = n - 1; j >= 0; --j) - { - for (int i = 0; i < 8; ++i) - out_buf[out_n++] = (buf[j] & (1 << (7 - i))) ? '1' : '0'; - out_buf[out_n++] = ' '; - } - IM_ASSERT(out_n < IM_ARRAYSIZE(out_buf)); - out_buf[out_n] = 0; - return out_buf; - } - - // [Internal] - void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const - { - ImU8 buf[8]; - size_t elem_size = DataTypeGetSize(data_type); - size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size; - if (ReadFn) - for (int i = 0, n = (int)size; i < n; ++i) - buf[i] = ReadFn(mem_data, addr + i, UserData); - else - memcpy(buf, mem_data + addr, size); - - if (data_format == DataFormat_Bin) - { - ImU8 binbuf[8]; - EndiannessCopy(binbuf, buf, size); - ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8)); - return; - } - - out_buf[0] = 0; - switch (data_type) - { - case ImGuiDataType_S8: - { - ImS8 data = 0; - EndiannessCopy(&data, buf, size); - if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", data); return; } - if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0xFF); return; } - break; - } - case ImGuiDataType_U8: - { - ImU8 data = 0; - EndiannessCopy(&data, buf, size); - if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", data); return; } - if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0XFF); return; } - break; - } - case ImGuiDataType_S16: - { - ImS16 data = 0; - EndiannessCopy(&data, buf, size); - if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", data); return; } - if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; } - break; - } - case ImGuiDataType_U16: - { - ImU16 data = 0; - EndiannessCopy(&data, buf, size); - if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", data); return; } - if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; } - break; - } - case ImGuiDataType_S32: - { - ImS32 data = 0; - EndiannessCopy(&data, buf, size); - if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", data); return; } - if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; } - break; - } - case ImGuiDataType_U32: - { - ImU32 data = 0; - EndiannessCopy(&data, buf, size); - if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", data); return; } - if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; } - break; - } - case ImGuiDataType_S64: - { - ImS64 data = 0; - EndiannessCopy(&data, buf, size); - if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)data); return; } - if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; } - break; - } - case ImGuiDataType_U64: - { - ImU64 data = 0; - EndiannessCopy(&data, buf, size); - if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)data); return; } - if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; } - break; - } - case ImGuiDataType_Float: - { - float data = 0.0f; - EndiannessCopy(&data, buf, size); - if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; } - if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; } - break; - } - case ImGuiDataType_Double: - { - double data = 0.0; - EndiannessCopy(&data, buf, size); - if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; } - if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; } - break; - } - default: - case ImGuiDataType_COUNT: - break; - } // Switch - IM_ASSERT(0); // Shouldn't reach - } -}; - -#undef _PRISizeT -#undef ImSnprintf - -#ifdef _MSC_VER -#pragma warning (pop) +// Mini memory editor for Dear ImGui (to embed in your game/tools) +// Get latest version at http://www.github.com/ocornut/imgui_club +// Licensed under The MIT License (MIT) + +// Right-click anywhere to access the Options menu! +// You can adjust the keyboard repeat delay/rate in ImGuiIO. +// The code assume a mono-space font for simplicity! +// If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before calling this. +// +// Usage: +// // Create a window and draw memory editor inside it: +// static MemoryEditor mem_edit_1; +// static char data[0x10000]; +// size_t data_size = 0x10000; +// mem_edit_1.DrawWindow("Memory Editor", data, data_size); +// +// Usage: +// // If you already have a window, use DrawContents() instead: +// static MemoryEditor mem_edit_2; +// ImGui::Begin("MyWindow") +// mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this); +// ImGui::End(); +// +// Changelog: +// - v0.10: initial version +// - v0.23 (2017/08/17): added to github. fixed right-arrow triggering a byte write. +// - v0.24 (2018/06/02): changed DragInt("Rows" to use a %d data format (which is desirable since imgui 1.61). +// - v0.25 (2018/07/11): fixed wording: all occurrences of "Rows" renamed to "Columns". +// - v0.26 (2018/08/02): fixed clicking on hex region +// - v0.30 (2018/08/02): added data preview for common data types +// - v0.31 (2018/10/10): added OptUpperCaseHex option to select lower/upper casing display [@samhocevar] +// - v0.32 (2018/10/10): changed signatures to use void* instead of unsigned char* +// - v0.33 (2018/10/10): added OptShowOptions option to hide all the interactive option setting. +// - v0.34 (2019/05/07): binary preview now applies endianness setting [@nicolasnoble] +// - v0.35 (2020/01/29): using ImGuiDataType available since Dear ImGui 1.69. +// - v0.36 (2020/05/05): minor tweaks, minor refactor. +// - v0.40 (2020/10/04): fix misuse of ImGuiListClipper API, broke with Dear ImGui 1.79. made cursor position appears on left-side of edit box. option popup appears on mouse release. fix MSVC warnings where _CRT_SECURE_NO_WARNINGS wasn't working in recent versions. +// - v0.41 (2020/10/05): fix when using with keyboard/gamepad navigation enabled. +// - v0.42 (2020/10/14): fix for . character in ASCII view always being greyed out. +// - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel] +// - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width. +// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper, relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size. +// - v0.51 (2024/02/22): fix for layout change in 1.89 when using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#34) +// - v0.52 (2024/03/08): removed unnecessary GetKeyIndex() calls, they are a no-op since 1.87. +// - v0.53 (2024/05/27): fixed right-click popup from not appearing when using DrawContents(). warning fixes. (#35) +// - v0.54 (2024/07/29): allow ReadOnly mode to still select and preview data. (#46) [@DeltaGW2]) +// - v0.55 (2024/08/19): added BgColorFn to allow setting background colors independently from highlighted selection. (#27) [@StrikerX3] +// added MouseHoveredAddr public readable field. (#47, #27) [@StrikerX3] +// fixed a data preview crash with 1.91.0 WIP. fixed contiguous highlight color when using data preview. +// *BREAKING* added UserData field passed to all optional function handlers: ReadFn, WriteFn, HighlightFn, BgColorFn. (#50) [@silverweed] +// - v0.56 (2024/11/04): fixed MouseHovered, MouseHoveredAddr not being set when hovering a byte being edited. (#54) +// - v0.57 (2025/03/26): fixed warnings. using ImGui's ImSXX/ImUXX types instead of e.g. int32_t/uint32_t. (#56) +// - v0.58 (2025/03/31): fixed extraneous footer spacing (added in 0.51) breaking vertical auto-resize. (#53) +// - v0.59 (2025/04/08): fixed GotoAddrAndHighlight() not working if OptShowOptions is disabled. +// +// TODO: +// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day. +// - PageUp/PageDown are not supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own... +// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame. +// - Using InputText() is awkward and maybe overkill here, consider implementing something custom. + +#pragma once + +#include // sprintf, scanf +#include // uint8_t, etc. + +#if defined(_MSC_VER) && !defined(snprintf) +#define ImSnprintf _snprintf +#else +#define ImSnprintf snprintf +#endif +#if defined(_MSC_VER) && !defined(__clang__) +#define _PRISizeT "I" +#else +#define _PRISizeT "z" +#endif + +#if defined(_MSC_VER) || defined(_UCRT) +#pragma warning (push) +#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe. +#endif + +struct MemoryEditor +{ + enum DataFormat + { + DataFormat_Bin = 0, + DataFormat_Dec = 1, + DataFormat_Hex = 2, + DataFormat_COUNT + }; + + // Settings + bool Open; // = true // set to false when DrawWindow() was closed. ignore if not using DrawWindow(). + bool ReadOnly; // = false // disable any editing. + int Cols; // = 16 // number of columns to display. + bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them. + bool OptShowDataPreview; // = false // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes. + bool OptShowHexII; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X". + bool OptShowAscii; // = true // display ASCII representation on the right side. + bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color. + bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff". + int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols. + int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr). + float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add custom widgets + ImU32 HighlightColor; // // background color of highlighted bytes. + + // Function handlers + ImU8 (*ReadFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to read bytes. + void (*WriteFn)(ImU8* mem, size_t off, ImU8 d, void* user_data); // = 0 // optional handler to write bytes. + bool (*HighlightFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return Highlight property (to support non-contiguous highlighting). + ImU32 (*BgColorFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return custom background color of individual bytes. + void* UserData; // = NULL // user data forwarded to the function handlers + + // Public read-only data + bool MouseHovered; // set when mouse is hovering a value. + size_t MouseHoveredAddr; // the address currently being hovered if MouseHovered is set. + + // [Internal State] + bool ContentsWidthChanged; + size_t DataPreviewAddr; + size_t DataEditingAddr; + bool DataEditingTakeFocus; + char DataInputBuf[32]; + char AddrInputBuf[32]; + size_t GotoAddr; + size_t HighlightMin, HighlightMax; + int PreviewEndianness; + ImGuiDataType PreviewDataType; + + MemoryEditor() + { + // Settings + Open = true; + ReadOnly = false; + Cols = 16; + OptShowOptions = true; + OptShowDataPreview = false; + OptShowHexII = false; + OptShowAscii = true; + OptGreyOutZeroes = true; + OptUpperCaseHex = true; + OptMidColsCount = 8; + OptAddrDigitsCount = 0; + OptFooterExtraHeight = 0.0f; + HighlightColor = IM_COL32(255, 255, 255, 50); + ReadFn = nullptr; + WriteFn = nullptr; + HighlightFn = nullptr; + BgColorFn = nullptr; + UserData = nullptr; + + // State/Internals + ContentsWidthChanged = false; + DataPreviewAddr = DataEditingAddr = (size_t)-1; + DataEditingTakeFocus = false; + memset(DataInputBuf, 0, sizeof(DataInputBuf)); + memset(AddrInputBuf, 0, sizeof(AddrInputBuf)); + GotoAddr = (size_t)-1; + MouseHovered = false; + MouseHoveredAddr = 0; + HighlightMin = HighlightMax = (size_t)-1; + PreviewEndianness = 0; + PreviewDataType = ImGuiDataType_S32; + } + + void GotoAddrAndHighlight(size_t addr_min, size_t addr_max) + { + GotoAddr = addr_min; + HighlightMin = addr_min; + HighlightMax = addr_max; + } + + struct Sizes + { + int AddrDigitsCount; // Number of digits required to represent maximum address. + float LineHeight; // Height of each line (no spacing). + float GlyphWidth; // Glyph width (assume mono-space). + float HexCellWidth; // Width of a hex edit cell ~2.5f * GlypHWidth. + float SpacingBetweenMidCols; // Spacing between each columns section (OptMidColsCount). + float OffsetHexMinX; + float OffsetHexMaxX; + float OffsetAsciiMinX; + float OffsetAsciiMaxX; + float WindowWidth; // Ideal window width. + + Sizes() { memset(this, 0, sizeof(*this)); } + }; + + void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr) + { + ImGuiStyle& style = ImGui::GetStyle(); + s.AddrDigitsCount = OptAddrDigitsCount; + if (s.AddrDigitsCount == 0) + for (size_t n = base_display_addr + mem_size - 1; n > 0; n >>= 4) + s.AddrDigitsCount++; + s.LineHeight = ImGui::GetTextLineHeight(); + s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space + s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to easily catch clicks everywhere + s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing + s.OffsetHexMinX = (s.AddrDigitsCount + 2) * s.GlyphWidth; + s.OffsetHexMaxX = s.OffsetHexMinX + (s.HexCellWidth * Cols); + s.OffsetAsciiMinX = s.OffsetAsciiMaxX = s.OffsetHexMaxX; + if (OptShowAscii) + { + s.OffsetAsciiMinX = s.OffsetHexMaxX + s.GlyphWidth * 1; + if (OptMidColsCount > 0) + s.OffsetAsciiMinX += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols; + s.OffsetAsciiMaxX = s.OffsetAsciiMinX + Cols * s.GlyphWidth; + } + s.WindowWidth = s.OffsetAsciiMaxX + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth; + } + + // Standalone Memory Editor window + void DrawWindow(const char* title, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000) + { + Sizes s; + CalcSizes(s, mem_size, base_display_addr); + ImGui::SetNextWindowSize(ImVec2(s.WindowWidth, s.WindowWidth * 0.60f), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX)); + + Open = true; + if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar)) + { + DrawContents(mem_data, mem_size, base_display_addr); + if (ContentsWidthChanged) + { + CalcSizes(s, mem_size, base_display_addr); + ImGui::SetWindowSize(ImVec2(s.WindowWidth, ImGui::GetWindowSize().y)); + } + } + ImGui::End(); + } + + // Memory Editor contents only + void DrawContents(void* mem_data_void, size_t mem_size, size_t base_display_addr = 0x0000) + { + if (Cols < 1) + Cols = 1; + + ImU8* mem_data = (ImU8*)mem_data_void; + Sizes s; + CalcSizes(s, mem_size, base_display_addr); + ImGuiStyle& style = ImGui::GetStyle(); + + const ImVec2 contents_pos_start = ImGui::GetCursorScreenPos(); + + // We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window. + // This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move. + const float height_separator = style.ItemSpacing.y; + float footer_height = OptFooterExtraHeight; + if (OptShowOptions) + footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1; + if (OptShowDataPreview) + footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3; + ImGui::BeginChild("##scrolling", ImVec2(-FLT_MIN, -footer_height), ImGuiChildFlags_None, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + + // We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function. + const ImVec2 avail_size = ImGui::GetContentRegionAvail(); + const int line_total_count = (int)((mem_size + Cols - 1) / Cols); + ImGuiListClipper clipper; + clipper.Begin(line_total_count, s.LineHeight); + + bool data_next = false; + + if (DataEditingAddr >= mem_size) + DataEditingAddr = (size_t)-1; + if (DataPreviewAddr >= mem_size) + DataPreviewAddr = (size_t)-1; + + size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0; + + size_t data_editing_addr_next = (size_t)-1; + if (DataEditingAddr != (size_t)-1) + { + // Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered) + if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; } + else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols){ data_editing_addr_next = DataEditingAddr + Cols; } + else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; } + else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; } + } + + // Draw vertical separator + ImVec2 window_pos = ImGui::GetWindowPos(); + if (OptShowAscii) + draw_list->AddLine(ImVec2(window_pos.x + s.OffsetAsciiMinX - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.OffsetAsciiMinX - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border)); + + const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text); + const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text; + + const char* format_address = OptUpperCaseHex ? "%0*" _PRISizeT "X: " : "%0*" _PRISizeT "x: "; + const char* format_data = OptUpperCaseHex ? "%0*" _PRISizeT "X" : "%0*" _PRISizeT "x"; + const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x"; + const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x "; + + MouseHovered = false; + MouseHoveredAddr = 0; + + while (clipper.Step()) + for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines + { + size_t addr = (size_t)line_i * Cols; + ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr); + + // Draw Hexadecimal + for (int n = 0; n < Cols && addr < mem_size; n++, addr++) + { + float byte_pos_x = s.OffsetHexMinX + s.HexCellWidth * n; + if (OptMidColsCount > 0) + byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols; + ImGui::SameLine(byte_pos_x); + + // Draw highlight or custom background color + const bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax); + const bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, UserData)); + const bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size); + + ImU32 bg_color = 0; + bool is_next_byte_highlighted = false; + if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview) + { + is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1, UserData)) || (addr + 1 < DataPreviewAddr + preview_data_type_size)); + bg_color = HighlightColor; + } + else if (BgColorFn != nullptr) + { + is_next_byte_highlighted = (addr + 1 < mem_size) && ((BgColorFn(mem_data, addr + 1, UserData) & IM_COL32_A_MASK) != 0); + bg_color = BgColorFn(mem_data, addr, UserData); + } + if (bg_color != 0) + { + float bg_width = s.GlyphWidth * 2; + if (is_next_byte_highlighted || (n + 1 == Cols)) + { + bg_width = s.HexCellWidth; + if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0) + bg_width += s.SpacingBetweenMidCols; + } + ImVec2 pos = ImGui::GetCursorScreenPos(); + draw_list->AddRectFilled(pos, ImVec2(pos.x + bg_width, pos.y + s.LineHeight), bg_color); + } + + if (DataEditingAddr == addr) + { + // Display text input on current byte + bool data_write = false; + ImGui::PushID((void*)addr); + if (DataEditingTakeFocus) + { + ImGui::SetKeyboardFocusHere(0); + ImSnprintf(AddrInputBuf, 32, format_data, s.AddrDigitsCount, base_display_addr + addr); + ImSnprintf(DataInputBuf, 32, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]); + } + struct InputTextUserData + { + // FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here. + static int Callback(ImGuiInputTextCallbackData* data) + { + InputTextUserData* user_data = (InputTextUserData*)data->UserData; + if (!data->HasSelection()) + user_data->CursorPos = data->CursorPos; +#if IMGUI_VERSION_NUM < 19102 + if (data->Flags & ImGuiInputTextFlags_ReadOnly) + return 0; +#endif + if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen) + { + // When not editing a byte, always refresh its InputText content pulled from underlying memory data + // (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there) + data->DeleteChars(0, data->BufTextLen); + data->InsertChars(0, user_data->CurrentBufOverwrite); + data->SelectionStart = 0; + data->SelectionEnd = 2; + data->CursorPos = 0; + } + return 0; + } + char CurrentBufOverwrite[3]; // Input + int CursorPos; // Output + }; + InputTextUserData input_text_user_data; + input_text_user_data.CursorPos = -1; + ImSnprintf(input_text_user_data.CurrentBufOverwrite, 3, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]); + ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways; + if (ReadOnly) + flags |= ImGuiInputTextFlags_ReadOnly; + flags |= ImGuiInputTextFlags_AlwaysOverwrite; // was ImGuiInputTextFlags_AlwaysInsertMode + ImGui::SetNextItemWidth(s.GlyphWidth * 2); + if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, InputTextUserData::Callback, &input_text_user_data)) + data_write = data_next = true; + else if (!DataEditingTakeFocus && !ImGui::IsItemActive()) + DataEditingAddr = data_editing_addr_next = (size_t)-1; + DataEditingTakeFocus = false; + if (input_text_user_data.CursorPos >= 2) + data_write = data_next = true; + if (data_editing_addr_next != (size_t)-1) + data_write = data_next = false; + unsigned int data_input_value = 0; + if (!ReadOnly && data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1) + { + if (WriteFn) + WriteFn(mem_data, addr, (ImU8)data_input_value, UserData); + else + mem_data[addr] = (ImU8)data_input_value; + } + if (ImGui::IsItemHovered()) + { + MouseHovered = true; + MouseHoveredAddr = addr; + } + ImGui::PopID(); + } + else + { + // NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on. + ImU8 b = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]; + + if (OptShowHexII) + { + if ((b >= 32 && b < 128)) + ImGui::Text(".%c ", b); + else if (b == 0xFF && OptGreyOutZeroes) + ImGui::TextDisabled("## "); + else if (b == 0x00) + ImGui::Text(" "); + else + ImGui::Text(format_byte_space, b); + } + else + { + if (b == 0 && OptGreyOutZeroes) + ImGui::TextDisabled("00 "); + else + ImGui::Text(format_byte_space, b); + } + if (ImGui::IsItemHovered()) + { + MouseHovered = true; + MouseHoveredAddr = addr; + if (ImGui::IsMouseClicked(0)) + { + DataEditingTakeFocus = true; + data_editing_addr_next = addr; + } + } + } + } + + if (OptShowAscii) + { + // Draw ASCII values + ImGui::SameLine(s.OffsetAsciiMinX); + ImVec2 pos = ImGui::GetCursorScreenPos(); + addr = (size_t)line_i * Cols; + + const float mouse_off_x = ImGui::GetIO().MousePos.x - pos.x; + const size_t mouse_addr = (mouse_off_x >= 0.0f && mouse_off_x < s.OffsetAsciiMaxX - s.OffsetAsciiMinX) ? addr + (size_t)(mouse_off_x / s.GlyphWidth) : (size_t)-1; + + ImGui::PushID(line_i); + if (ImGui::InvisibleButton("ascii", ImVec2(s.OffsetAsciiMaxX - s.OffsetAsciiMinX, s.LineHeight))) + { + DataEditingAddr = DataPreviewAddr = mouse_addr; + DataEditingTakeFocus = true; + } + if (ImGui::IsItemHovered()) + { + MouseHovered = true; + MouseHoveredAddr = mouse_addr; + } + ImGui::PopID(); + for (int n = 0; n < Cols && addr < mem_size; n++, addr++) + { + if (addr == DataEditingAddr) + { + draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg)); + draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg)); + } + else if (BgColorFn) + { + draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), BgColorFn(mem_data, addr, UserData)); + } + unsigned char c = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]; + char display_c = (c < 32 || c >= 128) ? '.' : c; + draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1); + pos.x += s.GlyphWidth; + } + } + } + ImGui::PopStyleVar(2); + const float child_width = ImGui::GetWindowSize().x; + ImGui::EndChild(); + + // Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child) + ImVec2 backup_pos = ImGui::GetCursorScreenPos(); + ImGui::SetCursorPosX(s.WindowWidth); + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + ImGui::SetCursorScreenPos(backup_pos); + + if (data_next && DataEditingAddr + 1 < mem_size) + { + DataEditingAddr = DataPreviewAddr = DataEditingAddr + 1; + DataEditingTakeFocus = true; + } + else if (data_editing_addr_next != (size_t)-1) + { + DataEditingAddr = DataPreviewAddr = data_editing_addr_next; + DataEditingTakeFocus = true; + } + + const bool lock_show_data_preview = OptShowDataPreview; + if (OptShowOptions) + { + ImGui::Separator(); + DrawOptionsLine(s, mem_data, mem_size, base_display_addr); + } + + if (lock_show_data_preview) + { + ImGui::Separator(); + DrawPreviewLine(s, mem_data, mem_size, base_display_addr); + } + + if (GotoAddr != (size_t)-1) + { + if (GotoAddr < mem_size) + { + ImGui::BeginChild("##scrolling"); + ImGui::SetScrollY((GotoAddr / Cols) * ImGui::GetTextLineHeight() - avail_size.y * 0.5f); + ImGui::EndChild(); + DataEditingAddr = DataPreviewAddr = GotoAddr; + DataEditingTakeFocus = true; + } + GotoAddr = (size_t)-1; + } + + const ImVec2 contents_pos_end(contents_pos_start.x + child_width, ImGui::GetCursorScreenPos().y); + //ImGui::GetForegroundDrawList()->AddRect(contents_pos_start, contents_pos_end, IM_COL32(255, 0, 0, 255)); + if (OptShowOptions) + if (ImGui::IsMouseHoveringRect(contents_pos_start, contents_pos_end)) + if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right)) + ImGui::OpenPopup("OptionsPopup"); + + if (ImGui::BeginPopup("OptionsPopup")) + { + ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f); + if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; } + ImGui::Checkbox("Show Data Preview", &OptShowDataPreview); + ImGui::Checkbox("Show HexII", &OptShowHexII); + if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; } + ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes); + ImGui::Checkbox("Uppercase Hex", &OptUpperCaseHex); + + ImGui::EndPopup(); + } + } + + void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr) + { + IM_UNUSED(mem_data); + ImGuiStyle& style = ImGui::GetStyle(); + const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X" : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x"; + + // Options menu + if (ImGui::Button("Options")) + ImGui::OpenPopup("OptionsPopup"); + + ImGui::SameLine(); + ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1); + ImGui::SameLine(); + ImGui::SetNextItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth + style.FramePadding.x * 2.0f); + if (ImGui::InputText("##addr", AddrInputBuf, IM_ARRAYSIZE(AddrInputBuf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) + { + size_t goto_addr; + if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1) + { + GotoAddr = goto_addr - base_display_addr; + HighlightMin = HighlightMax = (size_t)-1; + } + } + + //if (MouseHovered) + //{ + // ImGui::SameLine(); + // ImGui::Text("Hovered: %p", MouseHoveredAddr); + //} + } + + void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr) + { + IM_UNUSED(base_display_addr); + ImU8* mem_data = (ImU8*)mem_data_void; + ImGuiStyle& style = ImGui::GetStyle(); + ImGui::AlignTextToFramePadding(); + ImGui::Text("Preview as:"); + ImGui::SameLine(); + ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x); + + static const ImGuiDataType supported_data_types[] = { ImGuiDataType_S8, ImGuiDataType_U8, ImGuiDataType_S16, ImGuiDataType_U16, ImGuiDataType_S32, ImGuiDataType_U32, ImGuiDataType_S64, ImGuiDataType_U64, ImGuiDataType_Float, ImGuiDataType_Double }; + if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest)) + { + for (int n = 0; n < IM_ARRAYSIZE(supported_data_types); n++) + { + ImGuiDataType data_type = supported_data_types[n]; + if (ImGui::Selectable(DataTypeGetDesc(data_type), PreviewDataType == data_type)) + PreviewDataType = data_type; + } + ImGui::EndCombo(); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x); + ImGui::Combo("##combo_endianness", &PreviewEndianness, "LE\0BE\0\0"); + + char buf[128] = ""; + float x = s.GlyphWidth * 6.0f; + bool has_value = DataPreviewAddr != (size_t)-1; + if (has_value) + DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf)); + ImGui::Text("Dec"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A"); + if (has_value) + DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf)); + ImGui::Text("Hex"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A"); + if (has_value) + DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf)); + buf[IM_ARRAYSIZE(buf) - 1] = 0; + ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A"); + } + + // Utilities for Data Preview (since we don't access imgui_internal.h) + // FIXME: This technically depends on ImGuiDataType order. + const char* DataTypeGetDesc(ImGuiDataType data_type) const + { + const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" }; + IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(descs)); + return descs[data_type]; + } + + size_t DataTypeGetSize(ImGuiDataType data_type) const + { + const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) }; + IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(sizes)); + return sizes[data_type]; + } + + const char* DataFormatGetDesc(DataFormat data_format) const + { + const char* descs[] = { "Bin", "Dec", "Hex" }; + IM_ASSERT(data_format >= 0 && data_format < DataFormat_COUNT); + return descs[data_format]; + } + + bool IsBigEndian() const + { + ImU16 x = 1; + char c[2]; + memcpy(c, &x, 2); + return c[0] != 0; + } + + static void* EndiannessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian) + { + if (is_little_endian) + { + ImU8* dst = (ImU8*)_dst; + ImU8* src = (ImU8*)_src + s - 1; + for (int i = 0, n = (int)s; i < n; ++i) + memcpy(dst++, src--, 1); + return _dst; + } + else + { + return memcpy(_dst, _src, s); + } + } + + static void* EndiannessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian) + { + if (is_little_endian) + { + return memcpy(_dst, _src, s); + } + else + { + ImU8* dst = (ImU8*)_dst; + ImU8* src = (ImU8*)_src + s - 1; + for (int i = 0, n = (int)s; i < n; ++i) + memcpy(dst++, src--, 1); + return _dst; + } + } + + void* EndiannessCopy(void* dst, void* src, size_t size) const + { + static void* (*fp)(void*, void*, size_t, int) = nullptr; + if (fp == nullptr) + fp = IsBigEndian() ? EndiannessCopyBigEndian : EndiannessCopyLittleEndian; + return fp(dst, src, size, PreviewEndianness); + } + + const char* FormatBinary(const ImU8* buf, int width) const + { + IM_ASSERT(width <= 64); + size_t out_n = 0; + static char out_buf[64 + 8 + 1]; + int n = width / 8; + for (int j = n - 1; j >= 0; --j) + { + for (int i = 0; i < 8; ++i) + out_buf[out_n++] = (buf[j] & (1 << (7 - i))) ? '1' : '0'; + out_buf[out_n++] = ' '; + } + IM_ASSERT(out_n < IM_ARRAYSIZE(out_buf)); + out_buf[out_n] = 0; + return out_buf; + } + + // [Internal] + void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const + { + ImU8 buf[8]; + size_t elem_size = DataTypeGetSize(data_type); + size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size; + if (ReadFn) + for (int i = 0, n = (int)size; i < n; ++i) + buf[i] = ReadFn(mem_data, addr + i, UserData); + else + memcpy(buf, mem_data + addr, size); + + if (data_format == DataFormat_Bin) + { + ImU8 binbuf[8]; + EndiannessCopy(binbuf, buf, size); + ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8)); + return; + } + + out_buf[0] = 0; + switch (data_type) + { + case ImGuiDataType_S8: + { + ImS8 data = 0; + EndiannessCopy(&data, buf, size); + if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", data); return; } + if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0xFF); return; } + break; + } + case ImGuiDataType_U8: + { + ImU8 data = 0; + EndiannessCopy(&data, buf, size); + if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", data); return; } + if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0XFF); return; } + break; + } + case ImGuiDataType_S16: + { + ImS16 data = 0; + EndiannessCopy(&data, buf, size); + if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", data); return; } + if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; } + break; + } + case ImGuiDataType_U16: + { + ImU16 data = 0; + EndiannessCopy(&data, buf, size); + if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", data); return; } + if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; } + break; + } + case ImGuiDataType_S32: + { + ImS32 data = 0; + EndiannessCopy(&data, buf, size); + if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", data); return; } + if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; } + break; + } + case ImGuiDataType_U32: + { + ImU32 data = 0; + EndiannessCopy(&data, buf, size); + if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", data); return; } + if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; } + break; + } + case ImGuiDataType_S64: + { + ImS64 data = 0; + EndiannessCopy(&data, buf, size); + if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)data); return; } + if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; } + break; + } + case ImGuiDataType_U64: + { + ImU64 data = 0; + EndiannessCopy(&data, buf, size); + if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)data); return; } + if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; } + break; + } + case ImGuiDataType_Float: + { + float data = 0.0f; + EndiannessCopy(&data, buf, size); + if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; } + if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; } + break; + } + case ImGuiDataType_Double: + { + double data = 0.0; + EndiannessCopy(&data, buf, size); + if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; } + if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; } + break; + } + default: + case ImGuiDataType_COUNT: + break; + } // Switch + IM_ASSERT(0); // Shouldn't reach + } +}; + +#undef _PRISizeT +#undef ImSnprintf + +#ifdef _MSC_VER +#pragma warning (pop) #endif \ No newline at end of file diff --git a/src/spice2x/games/gitadora/asio.cpp b/src/spice2x/games/gitadora/asio.cpp index ee06fb7..5b70566 100644 --- a/src/spice2x/games/gitadora/asio.cpp +++ b/src/spice2x/games/gitadora/asio.cpp @@ -1,171 +1,171 @@ -#include "asio.h" - -#include -#include - -#include "avs/game.h" -#include "gitadora.h" -#include "util/detour.h" -#include "util/logging.h" - -namespace games::gitadora { - - // Redirects the game's hard-coded "XONAR" ASIO driver lookup to the - // driver name in ASIO_DRIVER by intercepting registry calls to - // HKLM\SOFTWARE\ASIO. Sentinel HKEY values mark the redirected handles - // so we can recognise them on subsequent reg* calls. - - static const HKEY PARENT_ASIO_REG_HANDLE = reinterpret_cast(0x4001); - static const HKEY DEVICE_ASIO_REG_HANDLE = reinterpret_cast(0x4002); - static const char *FAKE_ASIO_DEVICE_NAME = "XONAR"; - - static decltype(RegCloseKey) *RegCloseKey_orig = nullptr; - static decltype(RegEnumKeyA) *RegEnumKeyA_orig = nullptr; - static decltype(RegOpenKeyA) *RegOpenKeyA_orig = nullptr; - static decltype(RegOpenKeyExA) *RegOpenKeyExA_orig = nullptr; - static decltype(RegQueryValueExA) *RegQueryValueExA_orig = nullptr; - - static HKEY real_asio_reg_handle = nullptr; - static HKEY real_asio_device_reg_handle = nullptr; - - static LONG WINAPI RegOpenKeyExA_hook(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, - PHKEY phkResult) - { - if (ASIO_DRIVER.has_value() && - lpSubKey != nullptr && - phkResult != nullptr && - hKey == PARENT_ASIO_REG_HANDLE && - _stricmp(lpSubKey, FAKE_ASIO_DEVICE_NAME) == 0) { - - *phkResult = DEVICE_ASIO_REG_HANDLE; - - log_info("gitadora::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value()); - const auto result = RegOpenKeyExA_orig( - real_asio_reg_handle, - ASIO_DRIVER.value().c_str(), - ulOptions, - samDesired, - &real_asio_device_reg_handle); - - if (result != ERROR_SUCCESS) { - log_warning( - "gitadora::asio", - "failed to open registry subkey '{}', error=0x{:x}", - ASIO_DRIVER.value(), result); - log_warning( - "gitadora::asio", - "due to improper ASIO setting, audio init will fail"); - } - - return result; - } - - return RegOpenKeyExA_orig(hKey, lpSubKey, ulOptions, samDesired, phkResult); - } - - static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) { - if (ASIO_DRIVER.has_value() && - lpSubKey != nullptr && - phkResult != nullptr && - hKey == HKEY_LOCAL_MACHINE && - _stricmp(lpSubKey, "software\\asio") == 0) - { - *phkResult = PARENT_ASIO_REG_HANDLE; - return RegOpenKeyA_orig(hKey, lpSubKey, &real_asio_reg_handle); - } - - return RegOpenKeyA_orig(hKey, lpSubKey, phkResult); - } - - static LONG WINAPI RegEnumKeyA_hook(HKEY hKey, DWORD dwIndex, LPSTR lpName, DWORD cchName) { - if (hKey == PARENT_ASIO_REG_HANDLE && ASIO_DRIVER.has_value()) { - if (dwIndex == 0) { - // forward to real handle just to verify the key exists; we - // overwrite the name with our fake driver string regardless - auto ret = RegEnumKeyA_orig(real_asio_reg_handle, dwIndex, lpName, cchName); - if (ret == ERROR_SUCCESS && lpName != nullptr && cchName > 0) { - log_info("gitadora::asio", "stubbing '{}' with '{}'", lpName, FAKE_ASIO_DEVICE_NAME); - strncpy(lpName, FAKE_ASIO_DEVICE_NAME, cchName); - lpName[cchName - 1] = '\0'; - } - return ret; - } else { - return ERROR_NO_MORE_ITEMS; - } - } - - return RegEnumKeyA_orig(hKey, dwIndex, lpName, cchName); - } - - static LONG WINAPI RegQueryValueExA_hook(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, - LPBYTE lpData, LPDWORD lpcbData) - { - HKEY target = hKey; - - if (ASIO_DRIVER.has_value() && - lpValueName != nullptr && - lpData != nullptr && - lpcbData != nullptr && - hKey == DEVICE_ASIO_REG_HANDLE) { - if (_stricmp(lpValueName, "Description") == 0) { - // engine may verify the driver name after open; ensure it still - // sees something containing "XONAR" so the substring check passes - const size_t len = strlen(FAKE_ASIO_DEVICE_NAME) + 1; - if (*lpcbData < len) { - *lpcbData = static_cast(len); - return ERROR_MORE_DATA; - } - memcpy(lpData, FAKE_ASIO_DEVICE_NAME, len); - *lpcbData = static_cast(len); - if (lpType != nullptr) { - *lpType = REG_SZ; - } - return ERROR_SUCCESS; - } - - // for everything else (CLSID etc.) defer to the real driver subkey - target = real_asio_device_reg_handle; - } - - return RegQueryValueExA_orig(target, lpValueName, lpReserved, lpType, lpData, lpcbData); - } - - static LONG WINAPI RegCloseKey_hook(HKEY hKey) { - if (hKey == PARENT_ASIO_REG_HANDLE) { - if (real_asio_reg_handle != nullptr) { - RegCloseKey_orig(real_asio_reg_handle); - real_asio_reg_handle = nullptr; - } - return ERROR_SUCCESS; - } - - if (hKey == DEVICE_ASIO_REG_HANDLE) { - if (real_asio_device_reg_handle != nullptr) { - RegCloseKey_orig(real_asio_device_reg_handle); - real_asio_device_reg_handle = nullptr; - } - return ERROR_SUCCESS; - } - - return RegCloseKey_orig(hKey); - } - - void asio_hook_init() { - if (!ASIO_DRIVER.has_value()) { - return; - } - - log_info("gitadora::asio", "installing ASIO driver redirect: XONAR -> {}", ASIO_DRIVER.value()); - - RegCloseKey_orig = detour::iat_try( - "RegCloseKey", RegCloseKey_hook, avs::game::DLL_INSTANCE); - RegEnumKeyA_orig = detour::iat_try( - "RegEnumKeyA", RegEnumKeyA_hook, avs::game::DLL_INSTANCE); - RegOpenKeyA_orig = detour::iat_try( - "RegOpenKeyA", RegOpenKeyA_hook, avs::game::DLL_INSTANCE); - RegOpenKeyExA_orig = detour::iat_try( - "RegOpenKeyExA", RegOpenKeyExA_hook, avs::game::DLL_INSTANCE); - RegQueryValueExA_orig = detour::iat_try( - "RegQueryValueExA", RegQueryValueExA_hook, avs::game::DLL_INSTANCE); - } -} +#include "asio.h" + +#include +#include + +#include "avs/game.h" +#include "gitadora.h" +#include "util/detour.h" +#include "util/logging.h" + +namespace games::gitadora { + + // Redirects the game's hard-coded "XONAR" ASIO driver lookup to the + // driver name in ASIO_DRIVER by intercepting registry calls to + // HKLM\SOFTWARE\ASIO. Sentinel HKEY values mark the redirected handles + // so we can recognise them on subsequent reg* calls. + + static const HKEY PARENT_ASIO_REG_HANDLE = reinterpret_cast(0x4001); + static const HKEY DEVICE_ASIO_REG_HANDLE = reinterpret_cast(0x4002); + static const char *FAKE_ASIO_DEVICE_NAME = "XONAR"; + + static decltype(RegCloseKey) *RegCloseKey_orig = nullptr; + static decltype(RegEnumKeyA) *RegEnumKeyA_orig = nullptr; + static decltype(RegOpenKeyA) *RegOpenKeyA_orig = nullptr; + static decltype(RegOpenKeyExA) *RegOpenKeyExA_orig = nullptr; + static decltype(RegQueryValueExA) *RegQueryValueExA_orig = nullptr; + + static HKEY real_asio_reg_handle = nullptr; + static HKEY real_asio_device_reg_handle = nullptr; + + static LONG WINAPI RegOpenKeyExA_hook(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, + PHKEY phkResult) + { + if (ASIO_DRIVER.has_value() && + lpSubKey != nullptr && + phkResult != nullptr && + hKey == PARENT_ASIO_REG_HANDLE && + _stricmp(lpSubKey, FAKE_ASIO_DEVICE_NAME) == 0) { + + *phkResult = DEVICE_ASIO_REG_HANDLE; + + log_info("gitadora::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value()); + const auto result = RegOpenKeyExA_orig( + real_asio_reg_handle, + ASIO_DRIVER.value().c_str(), + ulOptions, + samDesired, + &real_asio_device_reg_handle); + + if (result != ERROR_SUCCESS) { + log_warning( + "gitadora::asio", + "failed to open registry subkey '{}', error=0x{:x}", + ASIO_DRIVER.value(), result); + log_warning( + "gitadora::asio", + "due to improper ASIO setting, audio init will fail"); + } + + return result; + } + + return RegOpenKeyExA_orig(hKey, lpSubKey, ulOptions, samDesired, phkResult); + } + + static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) { + if (ASIO_DRIVER.has_value() && + lpSubKey != nullptr && + phkResult != nullptr && + hKey == HKEY_LOCAL_MACHINE && + _stricmp(lpSubKey, "software\\asio") == 0) + { + *phkResult = PARENT_ASIO_REG_HANDLE; + return RegOpenKeyA_orig(hKey, lpSubKey, &real_asio_reg_handle); + } + + return RegOpenKeyA_orig(hKey, lpSubKey, phkResult); + } + + static LONG WINAPI RegEnumKeyA_hook(HKEY hKey, DWORD dwIndex, LPSTR lpName, DWORD cchName) { + if (hKey == PARENT_ASIO_REG_HANDLE && ASIO_DRIVER.has_value()) { + if (dwIndex == 0) { + // forward to real handle just to verify the key exists; we + // overwrite the name with our fake driver string regardless + auto ret = RegEnumKeyA_orig(real_asio_reg_handle, dwIndex, lpName, cchName); + if (ret == ERROR_SUCCESS && lpName != nullptr && cchName > 0) { + log_info("gitadora::asio", "stubbing '{}' with '{}'", lpName, FAKE_ASIO_DEVICE_NAME); + strncpy(lpName, FAKE_ASIO_DEVICE_NAME, cchName); + lpName[cchName - 1] = '\0'; + } + return ret; + } else { + return ERROR_NO_MORE_ITEMS; + } + } + + return RegEnumKeyA_orig(hKey, dwIndex, lpName, cchName); + } + + static LONG WINAPI RegQueryValueExA_hook(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, + LPBYTE lpData, LPDWORD lpcbData) + { + HKEY target = hKey; + + if (ASIO_DRIVER.has_value() && + lpValueName != nullptr && + lpData != nullptr && + lpcbData != nullptr && + hKey == DEVICE_ASIO_REG_HANDLE) { + if (_stricmp(lpValueName, "Description") == 0) { + // engine may verify the driver name after open; ensure it still + // sees something containing "XONAR" so the substring check passes + const size_t len = strlen(FAKE_ASIO_DEVICE_NAME) + 1; + if (*lpcbData < len) { + *lpcbData = static_cast(len); + return ERROR_MORE_DATA; + } + memcpy(lpData, FAKE_ASIO_DEVICE_NAME, len); + *lpcbData = static_cast(len); + if (lpType != nullptr) { + *lpType = REG_SZ; + } + return ERROR_SUCCESS; + } + + // for everything else (CLSID etc.) defer to the real driver subkey + target = real_asio_device_reg_handle; + } + + return RegQueryValueExA_orig(target, lpValueName, lpReserved, lpType, lpData, lpcbData); + } + + static LONG WINAPI RegCloseKey_hook(HKEY hKey) { + if (hKey == PARENT_ASIO_REG_HANDLE) { + if (real_asio_reg_handle != nullptr) { + RegCloseKey_orig(real_asio_reg_handle); + real_asio_reg_handle = nullptr; + } + return ERROR_SUCCESS; + } + + if (hKey == DEVICE_ASIO_REG_HANDLE) { + if (real_asio_device_reg_handle != nullptr) { + RegCloseKey_orig(real_asio_device_reg_handle); + real_asio_device_reg_handle = nullptr; + } + return ERROR_SUCCESS; + } + + return RegCloseKey_orig(hKey); + } + + void asio_hook_init() { + if (!ASIO_DRIVER.has_value()) { + return; + } + + log_info("gitadora::asio", "installing ASIO driver redirect: XONAR -> {}", ASIO_DRIVER.value()); + + RegCloseKey_orig = detour::iat_try( + "RegCloseKey", RegCloseKey_hook, avs::game::DLL_INSTANCE); + RegEnumKeyA_orig = detour::iat_try( + "RegEnumKeyA", RegEnumKeyA_hook, avs::game::DLL_INSTANCE); + RegOpenKeyA_orig = detour::iat_try( + "RegOpenKeyA", RegOpenKeyA_hook, avs::game::DLL_INSTANCE); + RegOpenKeyExA_orig = detour::iat_try( + "RegOpenKeyExA", RegOpenKeyExA_hook, avs::game::DLL_INSTANCE); + RegQueryValueExA_orig = detour::iat_try( + "RegQueryValueExA", RegQueryValueExA_hook, avs::game::DLL_INSTANCE); + } +} diff --git a/src/spice2x/games/gitadora/asio.h b/src/spice2x/games/gitadora/asio.h index f4b5685..d43ce94 100644 --- a/src/spice2x/games/gitadora/asio.h +++ b/src/spice2x/games/gitadora/asio.h @@ -1,12 +1,12 @@ -#pragma once - -namespace games::gitadora { - - // installs IAT registry hooks in gfdm.dll that redirect the game's - // ASIO driver lookup (hard-coded "XONAR" substring) to a user-chosen - // driver name read from games::gitadora::ASIO_DRIVER. - // - // safe to call unconditionally; if ASIO_DRIVER is unset the hooks - // forward every call straight through to advapi32. - void asio_hook_init(); -} +#pragma once + +namespace games::gitadora { + + // installs IAT registry hooks in gfdm.dll that redirect the game's + // ASIO driver lookup (hard-coded "XONAR" substring) to a user-chosen + // driver name read from games::gitadora::ASIO_DRIVER. + // + // safe to call unconditionally; if ASIO_DRIVER is unset the hooks + // forward every call straight through to advapi32. + void asio_hook_init(); +} diff --git a/src/spice2x/games/iidx/mf_wrappers.cpp b/src/spice2x/games/iidx/mf_wrappers.cpp index 068573b..0097da5 100644 --- a/src/spice2x/games/iidx/mf_wrappers.cpp +++ b/src/spice2x/games/iidx/mf_wrappers.cpp @@ -1,132 +1,132 @@ -#include "mf_wrappers.h" -#include "util/libutils.h" -#include "util/logging.h" - -namespace games::iidx { - - static bool INITIALIZED = false; - - static HMODULE mf_dll = nullptr; - static HMODULE mfreadwrite_dll = nullptr; - static HMODULE mfplat_dll = nullptr; - - typedef HRESULT (__stdcall * MFCreateAttributes_t)( - _Out_ IMFAttributes** ppMFAttributes, - _In_ UINT32 cInitialSize - ); - - typedef HRESULT (__stdcall * MFCreateMediaType_t)( - _Out_ IMFMediaType** ppMFType - ); - - typedef HRESULT (__stdcall * MFEnumDeviceSources_t)( - _In_ IMFAttributes* pAttributes, - _Outptr_result_buffer_(*pcSourceActivate) IMFActivate*** pppSourceActivate, - _Out_ UINT32* pcSourceActivate - ); - - typedef HRESULT (__stdcall * MFCreateSourceReaderFromMediaSource_t)( - _In_ IMFMediaSource *pMediaSource, - _In_opt_ IMFAttributes *pAttributes, - _Out_ IMFSourceReader **ppSourceReader - ); - - typedef HRESULT (__stdcall * MFGetService_t)( - IUnknown* punkObject, - REFGUID guidService, - REFIID riid, - _Outptr_ LPVOID* ppvObject - ); - - static MFCreateAttributes_t MFCreateAttributes = nullptr; - static MFCreateMediaType_t MFCreateMediaType = nullptr; - static MFEnumDeviceSources_t MFEnumDeviceSources = nullptr; - static MFCreateSourceReaderFromMediaSource_t MFCreateSourceReaderFromMediaSource = nullptr; - static MFGetService_t MFGetService = nullptr; - - void init_mf_library() { - - // why was all of this needed? - // - // when iidx camhook was initially implemented, we linked to mf.lib, mfreadwrite.lib, and mfplat.lib - // this made Unity-based really unhappy, causing them to skip over the logic that loads mf library - // causing videos to not play ("Initializing Microsoft Media Foundation failed." in the cmd prompt) - // - // as a result, the static linking to mf libs were removed, and we are now doing the mess that is this file - - if (INITIALIZED) { - return; - } - INITIALIZED = true; - - log_misc("mf_wrappers", "creating delay-loaded wrappers for MF routines - BEGIN"); - - mf_dll = libutils::load_library("mf.dll", true); - mfreadwrite_dll = libutils::load_library("mfreadwrite.dll", true); - mfplat_dll = libutils::load_library("mfplat.dll", true); - - MFCreateAttributes = (MFCreateAttributes_t) - libutils::get_proc(mfplat_dll, "MFCreateAttributes"); - if (!MFCreateAttributes) { - log_fatal("mf_wrappers", "MFCreateAttributes failed to hook"); - } - - MFCreateMediaType = (MFCreateMediaType_t) - libutils::get_proc(mfplat_dll, "MFCreateMediaType"); - if (!MFCreateMediaType) { - log_fatal("mf_wrappers", "MFCreateMediaType failed to hook"); - } - - MFEnumDeviceSources = (MFEnumDeviceSources_t) - libutils::get_proc(mf_dll, "MFEnumDeviceSources"); - if (!MFEnumDeviceSources) { - log_fatal("mf_wrappers", "MFEnumDeviceSources failed to hook"); - } - - MFCreateSourceReaderFromMediaSource = (MFCreateSourceReaderFromMediaSource_t) - libutils::get_proc(mfreadwrite_dll, "MFCreateSourceReaderFromMediaSource"); - if (!MFCreateSourceReaderFromMediaSource) { - log_fatal("mf_wrappers", "MFCreateSourceReaderFromMediaSource failed to hook"); - } - - MFGetService = (MFGetService_t)libutils::get_proc(mf_dll, "MFGetService"); - if (!MFGetService) { - log_fatal("mf_wrappers", "MFGetService failed to hook"); - } - - log_misc("mf_wrappers", "creating delay-loaded wrappers for MF routines - DONE"); - } - - HRESULT WrappedMFCreateAttributes ( - _Out_ IMFAttributes** ppMFAttributes, - _In_ UINT32 cInitialSize) { - return MFCreateAttributes(ppMFAttributes, cInitialSize); - } - - HRESULT WrappedMFCreateMediaType ( - _Out_ IMFMediaType** ppMFType) { - return MFCreateMediaType(ppMFType); - } - - HRESULT WrappedMFEnumDeviceSources ( - _In_ IMFAttributes* pAttributes, - _Outptr_result_buffer_(*pcSourceActivate) IMFActivate*** pppSourceActivate, - _Out_ UINT32* pcSourceActivate) { - return MFEnumDeviceSources(pAttributes, pppSourceActivate, pcSourceActivate); - } - - HRESULT WrappedMFCreateSourceReaderFromMediaSource ( - _In_ IMFMediaSource *pMediaSource, - _In_opt_ IMFAttributes *pAttributes, - _Out_ IMFSourceReader **ppSourceReader) { - return MFCreateSourceReaderFromMediaSource(pMediaSource, pAttributes, ppSourceReader); - } - - HRESULT WrappedMFGetService ( - IUnknown* punkObject, - REFGUID guidService, - REFIID riid, - _Outptr_ LPVOID* ppvObject) { - return MFGetService(punkObject, guidService, riid, ppvObject); - } +#include "mf_wrappers.h" +#include "util/libutils.h" +#include "util/logging.h" + +namespace games::iidx { + + static bool INITIALIZED = false; + + static HMODULE mf_dll = nullptr; + static HMODULE mfreadwrite_dll = nullptr; + static HMODULE mfplat_dll = nullptr; + + typedef HRESULT (__stdcall * MFCreateAttributes_t)( + _Out_ IMFAttributes** ppMFAttributes, + _In_ UINT32 cInitialSize + ); + + typedef HRESULT (__stdcall * MFCreateMediaType_t)( + _Out_ IMFMediaType** ppMFType + ); + + typedef HRESULT (__stdcall * MFEnumDeviceSources_t)( + _In_ IMFAttributes* pAttributes, + _Outptr_result_buffer_(*pcSourceActivate) IMFActivate*** pppSourceActivate, + _Out_ UINT32* pcSourceActivate + ); + + typedef HRESULT (__stdcall * MFCreateSourceReaderFromMediaSource_t)( + _In_ IMFMediaSource *pMediaSource, + _In_opt_ IMFAttributes *pAttributes, + _Out_ IMFSourceReader **ppSourceReader + ); + + typedef HRESULT (__stdcall * MFGetService_t)( + IUnknown* punkObject, + REFGUID guidService, + REFIID riid, + _Outptr_ LPVOID* ppvObject + ); + + static MFCreateAttributes_t MFCreateAttributes = nullptr; + static MFCreateMediaType_t MFCreateMediaType = nullptr; + static MFEnumDeviceSources_t MFEnumDeviceSources = nullptr; + static MFCreateSourceReaderFromMediaSource_t MFCreateSourceReaderFromMediaSource = nullptr; + static MFGetService_t MFGetService = nullptr; + + void init_mf_library() { + + // why was all of this needed? + // + // when iidx camhook was initially implemented, we linked to mf.lib, mfreadwrite.lib, and mfplat.lib + // this made Unity-based really unhappy, causing them to skip over the logic that loads mf library + // causing videos to not play ("Initializing Microsoft Media Foundation failed." in the cmd prompt) + // + // as a result, the static linking to mf libs were removed, and we are now doing the mess that is this file + + if (INITIALIZED) { + return; + } + INITIALIZED = true; + + log_misc("mf_wrappers", "creating delay-loaded wrappers for MF routines - BEGIN"); + + mf_dll = libutils::load_library("mf.dll", true); + mfreadwrite_dll = libutils::load_library("mfreadwrite.dll", true); + mfplat_dll = libutils::load_library("mfplat.dll", true); + + MFCreateAttributes = (MFCreateAttributes_t) + libutils::get_proc(mfplat_dll, "MFCreateAttributes"); + if (!MFCreateAttributes) { + log_fatal("mf_wrappers", "MFCreateAttributes failed to hook"); + } + + MFCreateMediaType = (MFCreateMediaType_t) + libutils::get_proc(mfplat_dll, "MFCreateMediaType"); + if (!MFCreateMediaType) { + log_fatal("mf_wrappers", "MFCreateMediaType failed to hook"); + } + + MFEnumDeviceSources = (MFEnumDeviceSources_t) + libutils::get_proc(mf_dll, "MFEnumDeviceSources"); + if (!MFEnumDeviceSources) { + log_fatal("mf_wrappers", "MFEnumDeviceSources failed to hook"); + } + + MFCreateSourceReaderFromMediaSource = (MFCreateSourceReaderFromMediaSource_t) + libutils::get_proc(mfreadwrite_dll, "MFCreateSourceReaderFromMediaSource"); + if (!MFCreateSourceReaderFromMediaSource) { + log_fatal("mf_wrappers", "MFCreateSourceReaderFromMediaSource failed to hook"); + } + + MFGetService = (MFGetService_t)libutils::get_proc(mf_dll, "MFGetService"); + if (!MFGetService) { + log_fatal("mf_wrappers", "MFGetService failed to hook"); + } + + log_misc("mf_wrappers", "creating delay-loaded wrappers for MF routines - DONE"); + } + + HRESULT WrappedMFCreateAttributes ( + _Out_ IMFAttributes** ppMFAttributes, + _In_ UINT32 cInitialSize) { + return MFCreateAttributes(ppMFAttributes, cInitialSize); + } + + HRESULT WrappedMFCreateMediaType ( + _Out_ IMFMediaType** ppMFType) { + return MFCreateMediaType(ppMFType); + } + + HRESULT WrappedMFEnumDeviceSources ( + _In_ IMFAttributes* pAttributes, + _Outptr_result_buffer_(*pcSourceActivate) IMFActivate*** pppSourceActivate, + _Out_ UINT32* pcSourceActivate) { + return MFEnumDeviceSources(pAttributes, pppSourceActivate, pcSourceActivate); + } + + HRESULT WrappedMFCreateSourceReaderFromMediaSource ( + _In_ IMFMediaSource *pMediaSource, + _In_opt_ IMFAttributes *pAttributes, + _Out_ IMFSourceReader **ppSourceReader) { + return MFCreateSourceReaderFromMediaSource(pMediaSource, pAttributes, ppSourceReader); + } + + HRESULT WrappedMFGetService ( + IUnknown* punkObject, + REFGUID guidService, + REFIID riid, + _Outptr_ LPVOID* ppvObject) { + return MFGetService(punkObject, guidService, riid, ppvObject); + } } \ No newline at end of file diff --git a/src/spice2x/games/iidx/mf_wrappers.h b/src/spice2x/games/iidx/mf_wrappers.h index 1dba852..b574709 100644 --- a/src/spice2x/games/iidx/mf_wrappers.h +++ b/src/spice2x/games/iidx/mf_wrappers.h @@ -1,33 +1,33 @@ -#include -#include -#include -#include - -#pragma once - -namespace games::iidx { - void init_mf_library(); - - HRESULT WrappedMFCreateAttributes ( - _Out_ IMFAttributes** ppMFAttributes, - _In_ UINT32 cInitialSize); - - HRESULT WrappedMFCreateMediaType ( - _Out_ IMFMediaType** ppMFType); - - HRESULT WrappedMFEnumDeviceSources ( - _In_ IMFAttributes* pAttributes, - _Outptr_result_buffer_(*pcSourceActivate) IMFActivate*** pppSourceActivate, - _Out_ UINT32* pcSourceActivate); - - HRESULT WrappedMFCreateSourceReaderFromMediaSource ( - _In_ IMFMediaSource *pMediaSource, - _In_opt_ IMFAttributes *pAttributes, - _Out_ IMFSourceReader **ppSourceReader); - - HRESULT WrappedMFGetService ( - IUnknown* punkObject, - REFGUID guidService, - REFIID riid, - _Outptr_ LPVOID* ppvObject); +#include +#include +#include +#include + +#pragma once + +namespace games::iidx { + void init_mf_library(); + + HRESULT WrappedMFCreateAttributes ( + _Out_ IMFAttributes** ppMFAttributes, + _In_ UINT32 cInitialSize); + + HRESULT WrappedMFCreateMediaType ( + _Out_ IMFMediaType** ppMFType); + + HRESULT WrappedMFEnumDeviceSources ( + _In_ IMFAttributes* pAttributes, + _Outptr_result_buffer_(*pcSourceActivate) IMFActivate*** pppSourceActivate, + _Out_ UINT32* pcSourceActivate); + + HRESULT WrappedMFCreateSourceReaderFromMediaSource ( + _In_ IMFMediaSource *pMediaSource, + _In_opt_ IMFAttributes *pAttributes, + _Out_ IMFSourceReader **ppSourceReader); + + HRESULT WrappedMFGetService ( + IUnknown* punkObject, + REFGUID guidService, + REFIID riid, + _Outptr_ LPVOID* ppvObject); } \ No newline at end of file diff --git a/src/spice2x/games/nost/touch_mode.cpp b/src/spice2x/games/nost/touch_mode.cpp index dd918a3..907efd1 100644 --- a/src/spice2x/games/nost/touch_mode.cpp +++ b/src/spice2x/games/nost/touch_mode.cpp @@ -1,233 +1,233 @@ -#include "touch_mode.h" - -#include -#include -#include - -#include "touch/native/nativetouchhook.h" -#include "util/logging.h" - -namespace games::nost::touch_mode { - - // native contact positions feed piano input directly. native events reach the game - // in nav mode and are suppressed in piano mode. mode-button contacts are always - // suppressed and change that routing after release. - static constexpr LONG PIANO_LEFT_GAP = 11; - static constexpr LONG PIANO_RIGHT_GAP = 10; - static constexpr uint32_t PIANO_KEY_COUNT = 28; - - struct TouchGeometry { - HWND window = nullptr; - RECT mode_button {}; - LONG client_width = 0; - LONG client_height = 0; - - bool valid() const { - return window != nullptr && client_width > 0 && client_height > 0; - } - }; - - struct NativeContact { - POINT position {}; - - // position contains game-client coordinates - bool client_position_valid = false; - - // down began on the mode switch button; remains true through up - bool mode_button = false; - }; - - static std::atomic_bool accept_events { false }; - static std::atomic current_mode_state { Mode::Nav }; - static std::mutex state_mutex; - static TouchGeometry touch_geometry; - - // native contacts are kept by ID so each contact contributes exactly one position - static std::unordered_map active_contacts; - - // a hardware button release requests one change after all contacts are released - static bool mode_change_pending = false; - - static void reset_state_locked() { - current_mode_state.store(Mode::Nav, std::memory_order_release); - touch_geometry = {}; - active_contacts.clear(); - mode_change_pending = false; - } - - // hardware contacts arrive in screen coordinates - static bool native_touch_in_button(const nativetouch::NativeTouchEvent &event) { - if (!touch_geometry.valid()) { - return false; - } - - POINT position { event.x, event.y }; - if (!ScreenToClient(touch_geometry.window, &position)) { - return false; - } - return PtInRect(&touch_geometry.mode_button, position) != FALSE; - } - - static bool update_touch_state(const nativetouch::NativeTouchEvent &event) { - std::lock_guard lock(state_mutex); - - // first, process down / move events - if (event.down || event.move) { - auto contact = active_contacts.try_emplace(event.id).first; - - // keep track of IDs that began as a down on the mode switch button - if (event.down) { - contact->second.mode_button = native_touch_in_button(event); - } - - // check for valid position - POINT position { event.x, event.y }; - if (touch_geometry.window != nullptr && - ScreenToClient(touch_geometry.window, &position)) { - contact->second.position = position; - contact->second.client_position_valid = true; - } - } - - const auto contact = active_contacts.find(event.id); - const bool mode_button_contact = contact != active_contacts.end() && - contact->second.mode_button; - - // process up events - if (event.up) { - active_contacts.erase(event.id); - - // if a contact that began down event on the mode switch button has - // been released, a mode switch is now pending - if (mode_button_contact) { - mode_change_pending = true; - } - - // apply the change on the final hardware up. switching earlier would - // split another contact's down and up events across different modes - if (mode_change_pending && active_contacts.empty()) { - mode_change_pending = false; - const auto next_mode = current_mode() == Mode::Nav ? Mode::Piano : Mode::Nav; - current_mode_state.store(next_mode, std::memory_order_release); - } - } - return mode_button_contact; - } - - // install the Nostalgia-specific native touch interception - void enable() { - if (accept_events.exchange(true, std::memory_order_acq_rel)) { - return; - } - - { - std::lock_guard lock(state_mutex); - reset_state_locked(); - } - - nativetouch::set_input_filter(filter_native_touch); - log_info("nost::touch", "enabled"); - } - - void disable() { - if (!accept_events.exchange(false, std::memory_order_acq_rel)) { - return; - } - - nativetouch::set_input_filter(nullptr); - - std::lock_guard lock(state_mutex); - reset_state_locked(); - } - - bool enabled() { - return accept_events.load(std::memory_order_acquire); - } - - Mode current_mode() { - return current_mode_state.load(std::memory_order_acquire); - } - - // publish the rendered overlay button rectangle in game-client pixels - void publish_button_bounds(HWND window, const RECT &client_bounds) { - TouchGeometry next {}; - RECT client_rect {}; - if (window != nullptr && GetClientRect(window, &client_rect) && - client_rect.right > 0 && client_rect.bottom > 0) { - next.window = window; - next.mode_button = client_bounds; - next.client_width = client_rect.right; - next.client_height = client_rect.bottom; - } - - std::lock_guard lock(state_mutex); - touch_geometry = next; - } - - // return the active 28-key piano bitfield for the PANB input update - uint32_t piano_key_state() { - if (!enabled() || current_mode() != Mode::Piano) { - return 0; - } - - std::lock_guard lock(state_mutex); - if (current_mode() != Mode::Piano || !touch_geometry.valid()) { - return 0; - } - - uint32_t state = 0; - for (const auto &contact : active_contacts) { - // invalid position or mode-button contact; ignore these contacts - if (!contact.second.client_position_valid || contact.second.mode_button) { - continue; - } - - const auto &position = contact.second.position; - - // outside the client area or on the mode button; ignore these contacts - if (position.x < 0 || position.x >= touch_geometry.client_width || - position.y < 0 || position.y >= touch_geometry.client_height || - PtInRect(&touch_geometry.mode_button, position)) { - continue; - } - - // divide the inset width evenly; touches in either side gap clamp to - // the nearest outer key so the physical screen edges remain playable - const auto piano_width = - touch_geometry.client_width - PIANO_LEFT_GAP - PIANO_RIGHT_GAP; - uint32_t key = 0; - if (position.x >= touch_geometry.client_width - PIANO_RIGHT_GAP) { - key = PIANO_KEY_COUNT - 1; - } else if (position.x >= PIANO_LEFT_GAP && piano_width > 0) { - key = static_cast( - (position.x - PIANO_LEFT_GAP) * PIANO_KEY_COUNT / piano_width); - } - state |= UINT32_C(1) << key; - } - - return state; - } - - // update native contacts and report whether this event should be hidden from the game - bool filter_native_touch(const nativetouch::NativeTouchEvent &event) { - // synthetic events are outside this hardware-only feature - if (!enabled() || event.synthetic) { - // false leaves the event visible to the game - return false; - } - - // snapshot routing before an up event can commit a pending mode switch - const bool piano_mode_before_update = current_mode() == Mode::Piano; - - // update the contact lifetime and commit any pending switch when safe - const bool mode_button_contact = update_touch_state(event); - - // hide every event in a contact that began on the mode switch button - if (mode_button_contact) { - return true; - } - - // piano mode consumes hardware events; nav mode forwards them to the game - return piano_mode_before_update; - } -} +#include "touch_mode.h" + +#include +#include +#include + +#include "touch/native/nativetouchhook.h" +#include "util/logging.h" + +namespace games::nost::touch_mode { + + // native contact positions feed piano input directly. native events reach the game + // in nav mode and are suppressed in piano mode. mode-button contacts are always + // suppressed and change that routing after release. + static constexpr LONG PIANO_LEFT_GAP = 11; + static constexpr LONG PIANO_RIGHT_GAP = 10; + static constexpr uint32_t PIANO_KEY_COUNT = 28; + + struct TouchGeometry { + HWND window = nullptr; + RECT mode_button {}; + LONG client_width = 0; + LONG client_height = 0; + + bool valid() const { + return window != nullptr && client_width > 0 && client_height > 0; + } + }; + + struct NativeContact { + POINT position {}; + + // position contains game-client coordinates + bool client_position_valid = false; + + // down began on the mode switch button; remains true through up + bool mode_button = false; + }; + + static std::atomic_bool accept_events { false }; + static std::atomic current_mode_state { Mode::Nav }; + static std::mutex state_mutex; + static TouchGeometry touch_geometry; + + // native contacts are kept by ID so each contact contributes exactly one position + static std::unordered_map active_contacts; + + // a hardware button release requests one change after all contacts are released + static bool mode_change_pending = false; + + static void reset_state_locked() { + current_mode_state.store(Mode::Nav, std::memory_order_release); + touch_geometry = {}; + active_contacts.clear(); + mode_change_pending = false; + } + + // hardware contacts arrive in screen coordinates + static bool native_touch_in_button(const nativetouch::NativeTouchEvent &event) { + if (!touch_geometry.valid()) { + return false; + } + + POINT position { event.x, event.y }; + if (!ScreenToClient(touch_geometry.window, &position)) { + return false; + } + return PtInRect(&touch_geometry.mode_button, position) != FALSE; + } + + static bool update_touch_state(const nativetouch::NativeTouchEvent &event) { + std::lock_guard lock(state_mutex); + + // first, process down / move events + if (event.down || event.move) { + auto contact = active_contacts.try_emplace(event.id).first; + + // keep track of IDs that began as a down on the mode switch button + if (event.down) { + contact->second.mode_button = native_touch_in_button(event); + } + + // check for valid position + POINT position { event.x, event.y }; + if (touch_geometry.window != nullptr && + ScreenToClient(touch_geometry.window, &position)) { + contact->second.position = position; + contact->second.client_position_valid = true; + } + } + + const auto contact = active_contacts.find(event.id); + const bool mode_button_contact = contact != active_contacts.end() && + contact->second.mode_button; + + // process up events + if (event.up) { + active_contacts.erase(event.id); + + // if a contact that began down event on the mode switch button has + // been released, a mode switch is now pending + if (mode_button_contact) { + mode_change_pending = true; + } + + // apply the change on the final hardware up. switching earlier would + // split another contact's down and up events across different modes + if (mode_change_pending && active_contacts.empty()) { + mode_change_pending = false; + const auto next_mode = current_mode() == Mode::Nav ? Mode::Piano : Mode::Nav; + current_mode_state.store(next_mode, std::memory_order_release); + } + } + return mode_button_contact; + } + + // install the Nostalgia-specific native touch interception + void enable() { + if (accept_events.exchange(true, std::memory_order_acq_rel)) { + return; + } + + { + std::lock_guard lock(state_mutex); + reset_state_locked(); + } + + nativetouch::set_input_filter(filter_native_touch); + log_info("nost::touch", "enabled"); + } + + void disable() { + if (!accept_events.exchange(false, std::memory_order_acq_rel)) { + return; + } + + nativetouch::set_input_filter(nullptr); + + std::lock_guard lock(state_mutex); + reset_state_locked(); + } + + bool enabled() { + return accept_events.load(std::memory_order_acquire); + } + + Mode current_mode() { + return current_mode_state.load(std::memory_order_acquire); + } + + // publish the rendered overlay button rectangle in game-client pixels + void publish_button_bounds(HWND window, const RECT &client_bounds) { + TouchGeometry next {}; + RECT client_rect {}; + if (window != nullptr && GetClientRect(window, &client_rect) && + client_rect.right > 0 && client_rect.bottom > 0) { + next.window = window; + next.mode_button = client_bounds; + next.client_width = client_rect.right; + next.client_height = client_rect.bottom; + } + + std::lock_guard lock(state_mutex); + touch_geometry = next; + } + + // return the active 28-key piano bitfield for the PANB input update + uint32_t piano_key_state() { + if (!enabled() || current_mode() != Mode::Piano) { + return 0; + } + + std::lock_guard lock(state_mutex); + if (current_mode() != Mode::Piano || !touch_geometry.valid()) { + return 0; + } + + uint32_t state = 0; + for (const auto &contact : active_contacts) { + // invalid position or mode-button contact; ignore these contacts + if (!contact.second.client_position_valid || contact.second.mode_button) { + continue; + } + + const auto &position = contact.second.position; + + // outside the client area or on the mode button; ignore these contacts + if (position.x < 0 || position.x >= touch_geometry.client_width || + position.y < 0 || position.y >= touch_geometry.client_height || + PtInRect(&touch_geometry.mode_button, position)) { + continue; + } + + // divide the inset width evenly; touches in either side gap clamp to + // the nearest outer key so the physical screen edges remain playable + const auto piano_width = + touch_geometry.client_width - PIANO_LEFT_GAP - PIANO_RIGHT_GAP; + uint32_t key = 0; + if (position.x >= touch_geometry.client_width - PIANO_RIGHT_GAP) { + key = PIANO_KEY_COUNT - 1; + } else if (position.x >= PIANO_LEFT_GAP && piano_width > 0) { + key = static_cast( + (position.x - PIANO_LEFT_GAP) * PIANO_KEY_COUNT / piano_width); + } + state |= UINT32_C(1) << key; + } + + return state; + } + + // update native contacts and report whether this event should be hidden from the game + bool filter_native_touch(const nativetouch::NativeTouchEvent &event) { + // synthetic events are outside this hardware-only feature + if (!enabled() || event.synthetic) { + // false leaves the event visible to the game + return false; + } + + // snapshot routing before an up event can commit a pending mode switch + const bool piano_mode_before_update = current_mode() == Mode::Piano; + + // update the contact lifetime and commit any pending switch when safe + const bool mode_button_contact = update_touch_state(event); + + // hide every event in a contact that began on the mode switch button + if (mode_button_contact) { + return true; + } + + // piano mode consumes hardware events; nav mode forwards them to the game + return piano_mode_before_update; + } +} diff --git a/src/spice2x/games/nost/touch_mode.h b/src/spice2x/games/nost/touch_mode.h index 7ef19fb..c478e13 100644 --- a/src/spice2x/games/nost/touch_mode.h +++ b/src/spice2x/games/nost/touch_mode.h @@ -1,27 +1,27 @@ -#pragma once - -#include -#include - -#include "touch/native/nativetouchhook.h" - -namespace games::nost::touch_mode { - - // nav mode forwards contacts to the game; piano mode converts them into piano keys - enum class Mode { - Nav, - Piano, - }; - - void enable(); - void disable(); - bool enabled(); - - Mode current_mode(); - - void publish_button_bounds(HWND window, const RECT &client_bounds); - - uint32_t piano_key_state(); - - bool filter_native_touch(const nativetouch::NativeTouchEvent &event); -} +#pragma once + +#include +#include + +#include "touch/native/nativetouchhook.h" + +namespace games::nost::touch_mode { + + // nav mode forwards contacts to the game; piano mode converts them into piano keys + enum class Mode { + Nav, + Piano, + }; + + void enable(); + void disable(); + bool enabled(); + + Mode current_mode(); + + void publish_button_bounds(HWND window, const RECT &client_bounds); + + uint32_t piano_key_state(); + + bool filter_native_touch(const nativetouch::NativeTouchEvent &event); +} diff --git a/src/spice2x/games/rb/touch_debug.cpp b/src/spice2x/games/rb/touch_debug.cpp index 8419377..7a4b2f7 100644 --- a/src/spice2x/games/rb/touch_debug.cpp +++ b/src/spice2x/games/rb/touch_debug.cpp @@ -1,143 +1,143 @@ -#include "touch_debug.h" - -#include -#include -#include -#include - -#include "external/imgui/imgui.h" -#include "games/rb/rb.h" -#include "games/rb/touch_defs.h" - -namespace games::rb { - - struct TouchDebugState { - std::array packet {}; - bool is_landscape = false; - }; - - std::atomic_bool TOUCH_DEBUG_OVERLAY = false; - static std::atomic_bool TOUCH_ACTIVE = false; - static std::mutex TOUCH_DEBUG_STATE_M; - static TouchDebugState TOUCH_DEBUG_STATE; - - static float touch_scale_factor() { - return TOUCH_SCALING / (float) TOUCH_SCALE_DEFAULT; - } - - static void clear_touch_debug_state() { - std::lock_guard lock(TOUCH_DEBUG_STATE_M); - TOUCH_DEBUG_STATE = {}; - } - - static TouchDebugState get_touch_debug_state() { - std::lock_guard lock(TOUCH_DEBUG_STATE_M); - return TOUCH_DEBUG_STATE; - } - - static bool packet_bit_active( - const std::array &packet, int bit) { - return (packet[TOUCH_PACKET_DATA_OFFSET + bit / 8] & (1u << (bit % 8))) != 0; - } - - static int sensor_center(int sensor, int sensor_count, int extent) { - return ((sensor * 2 + 1) * extent) / (sensor_count * 2); - } - - static float sensor_span_position(int sensor, int sensor_count, int extent) { - return sensor * (extent - 1) / (float) (sensor_count - 1); - } - - bool touch_debug_overlay_enabled() { - return TOUCH_DEBUG_OVERLAY && TOUCH_ACTIVE.load(std::memory_order_acquire); - } - - void touch_draw_debug_overlay() { - if (!touch_debug_overlay_enabled()) { - return; - } - - const auto &io = ImGui::GetIO(); - int width = static_cast(io.DisplaySize.x); - int height = static_cast(io.DisplaySize.y); - if (width <= 0 || height <= 0) { - return; - } - - const float scale_factor = touch_scale_factor(); - const float left = width * (1.f - scale_factor) / 2.f; - const float top = height * (1.f - scale_factor) / 2.f; - const float right = width - left; - const float bottom = height - top; - - TouchDebugState state = get_touch_debug_state(); - ImDrawList *draw_list = ImGui::GetBackgroundDrawList(); - auto draw_line = [&](float x1, float y1, float x2, float y2) { - draw_list->AddLine( - ImVec2(x1, y1), ImVec2(x2, y2), - IM_COL32(0, 255, 64, 255), 2.f); - }; - - // show the valid input area when touch scaling restricts it - if (TOUCH_SCALING != TOUCH_SCALE_DEFAULT) { - draw_list->AddRect( - ImVec2(left, top), ImVec2(right, bottom), - IM_COL32(255, 255, 255, 255), 0.f, 0, 2.f); - } - - // spread the usable X sensors 2..45 from edge to edge - for (int sensor = X_SENSOR_FIRST_ACTIVE; sensor <= X_SENSOR_LAST_ACTIVE; sensor++) { - if (!packet_bit_active(state.packet, X_SENSOR_FIRST_BIT + sensor)) { - continue; - } - - float position = sensor_span_position( - sensor - X_SENSOR_FIRST_ACTIVE, X_SENSOR_ACTIVE_COUNT, - state.is_landscape ? height : width); - if (state.is_landscape) { - float y = top + position * scale_factor; - draw_line(left, y, right, y); - } else { - float x = left + position * scale_factor; - draw_line(x, top, x, bottom); - } - } - - for (int sensor = 0; sensor < Y_SENSOR_COUNT; sensor++) { - if (!packet_bit_active(state.packet, Y_SENSOR_FIRST_BIT - sensor)) { - continue; - } - - int position = sensor_center( - sensor, Y_SENSOR_COUNT, - state.is_landscape ? width : height); - if (state.is_landscape) { - float x = right - position * scale_factor; - draw_line(x, top, x, bottom); - } else { - float y = top + position * scale_factor; - draw_line(left, y, right, y); - } - } - } - - void touch_debug_attach() { - clear_touch_debug_state(); - TOUCH_ACTIVE.store(true, std::memory_order_release); - } - - void touch_debug_detach() { - TOUCH_ACTIVE.store(false, std::memory_order_release); - clear_touch_debug_state(); - } - - void touch_debug_publish(const unsigned char *data, bool is_landscape) { - if (!TOUCH_DEBUG_OVERLAY) { - return; - } - - std::lock_guard lock(TOUCH_DEBUG_STATE_M); - memcpy(TOUCH_DEBUG_STATE.packet.data(), data, TOUCH_PACKET_SIZE); - TOUCH_DEBUG_STATE.is_landscape = is_landscape; - } -} +#include "touch_debug.h" + +#include +#include +#include +#include + +#include "external/imgui/imgui.h" +#include "games/rb/rb.h" +#include "games/rb/touch_defs.h" + +namespace games::rb { + + struct TouchDebugState { + std::array packet {}; + bool is_landscape = false; + }; + + std::atomic_bool TOUCH_DEBUG_OVERLAY = false; + static std::atomic_bool TOUCH_ACTIVE = false; + static std::mutex TOUCH_DEBUG_STATE_M; + static TouchDebugState TOUCH_DEBUG_STATE; + + static float touch_scale_factor() { + return TOUCH_SCALING / (float) TOUCH_SCALE_DEFAULT; + } + + static void clear_touch_debug_state() { + std::lock_guard lock(TOUCH_DEBUG_STATE_M); + TOUCH_DEBUG_STATE = {}; + } + + static TouchDebugState get_touch_debug_state() { + std::lock_guard lock(TOUCH_DEBUG_STATE_M); + return TOUCH_DEBUG_STATE; + } + + static bool packet_bit_active( + const std::array &packet, int bit) { + return (packet[TOUCH_PACKET_DATA_OFFSET + bit / 8] & (1u << (bit % 8))) != 0; + } + + static int sensor_center(int sensor, int sensor_count, int extent) { + return ((sensor * 2 + 1) * extent) / (sensor_count * 2); + } + + static float sensor_span_position(int sensor, int sensor_count, int extent) { + return sensor * (extent - 1) / (float) (sensor_count - 1); + } + + bool touch_debug_overlay_enabled() { + return TOUCH_DEBUG_OVERLAY && TOUCH_ACTIVE.load(std::memory_order_acquire); + } + + void touch_draw_debug_overlay() { + if (!touch_debug_overlay_enabled()) { + return; + } + + const auto &io = ImGui::GetIO(); + int width = static_cast(io.DisplaySize.x); + int height = static_cast(io.DisplaySize.y); + if (width <= 0 || height <= 0) { + return; + } + + const float scale_factor = touch_scale_factor(); + const float left = width * (1.f - scale_factor) / 2.f; + const float top = height * (1.f - scale_factor) / 2.f; + const float right = width - left; + const float bottom = height - top; + + TouchDebugState state = get_touch_debug_state(); + ImDrawList *draw_list = ImGui::GetBackgroundDrawList(); + auto draw_line = [&](float x1, float y1, float x2, float y2) { + draw_list->AddLine( + ImVec2(x1, y1), ImVec2(x2, y2), + IM_COL32(0, 255, 64, 255), 2.f); + }; + + // show the valid input area when touch scaling restricts it + if (TOUCH_SCALING != TOUCH_SCALE_DEFAULT) { + draw_list->AddRect( + ImVec2(left, top), ImVec2(right, bottom), + IM_COL32(255, 255, 255, 255), 0.f, 0, 2.f); + } + + // spread the usable X sensors 2..45 from edge to edge + for (int sensor = X_SENSOR_FIRST_ACTIVE; sensor <= X_SENSOR_LAST_ACTIVE; sensor++) { + if (!packet_bit_active(state.packet, X_SENSOR_FIRST_BIT + sensor)) { + continue; + } + + float position = sensor_span_position( + sensor - X_SENSOR_FIRST_ACTIVE, X_SENSOR_ACTIVE_COUNT, + state.is_landscape ? height : width); + if (state.is_landscape) { + float y = top + position * scale_factor; + draw_line(left, y, right, y); + } else { + float x = left + position * scale_factor; + draw_line(x, top, x, bottom); + } + } + + for (int sensor = 0; sensor < Y_SENSOR_COUNT; sensor++) { + if (!packet_bit_active(state.packet, Y_SENSOR_FIRST_BIT - sensor)) { + continue; + } + + int position = sensor_center( + sensor, Y_SENSOR_COUNT, + state.is_landscape ? width : height); + if (state.is_landscape) { + float x = right - position * scale_factor; + draw_line(x, top, x, bottom); + } else { + float y = top + position * scale_factor; + draw_line(left, y, right, y); + } + } + } + + void touch_debug_attach() { + clear_touch_debug_state(); + TOUCH_ACTIVE.store(true, std::memory_order_release); + } + + void touch_debug_detach() { + TOUCH_ACTIVE.store(false, std::memory_order_release); + clear_touch_debug_state(); + } + + void touch_debug_publish(const unsigned char *data, bool is_landscape) { + if (!TOUCH_DEBUG_OVERLAY) { + return; + } + + std::lock_guard lock(TOUCH_DEBUG_STATE_M); + memcpy(TOUCH_DEBUG_STATE.packet.data(), data, TOUCH_PACKET_SIZE); + TOUCH_DEBUG_STATE.is_landscape = is_landscape; + } +} diff --git a/src/spice2x/games/rb/touch_debug.h b/src/spice2x/games/rb/touch_debug.h index 1abd14d..75516da 100644 --- a/src/spice2x/games/rb/touch_debug.h +++ b/src/spice2x/games/rb/touch_debug.h @@ -1,14 +1,14 @@ -#pragma once - -#include - -namespace games::rb { - extern std::atomic_bool TOUCH_DEBUG_OVERLAY; - - bool touch_debug_overlay_enabled(); - void touch_draw_debug_overlay(); - - void touch_debug_attach(); - void touch_debug_detach(); - void touch_debug_publish(const unsigned char *data, bool is_landscape); -} +#pragma once + +#include + +namespace games::rb { + extern std::atomic_bool TOUCH_DEBUG_OVERLAY; + + bool touch_debug_overlay_enabled(); + void touch_draw_debug_overlay(); + + void touch_debug_attach(); + void touch_debug_detach(); + void touch_debug_publish(const unsigned char *data, bool is_landscape); +} diff --git a/src/spice2x/games/rb/touch_defs.h b/src/spice2x/games/rb/touch_defs.h index f0c2474..b5de3e8 100644 --- a/src/spice2x/games/rb/touch_defs.h +++ b/src/spice2x/games/rb/touch_defs.h @@ -1,17 +1,17 @@ -#pragma once - -namespace games::rb { - inline constexpr int TOUCH_SCALE_DEFAULT = 1000; - inline constexpr int TOUCH_PACKET_SIZE = 20; - inline constexpr int TOUCH_PACKET_DATA_OFFSET = 3; - - inline constexpr int X_SENSOR_COUNT = 48; - inline constexpr int X_SENSOR_FIRST_ACTIVE = 2; - inline constexpr int X_SENSOR_LAST_ACTIVE = 45; - inline constexpr int X_SENSOR_ACTIVE_COUNT = - X_SENSOR_LAST_ACTIVE - X_SENSOR_FIRST_ACTIVE + 1; - inline constexpr int X_SENSOR_FIRST_BIT = 88; - - inline constexpr int Y_SENSOR_COUNT = 76; - inline constexpr int Y_SENSOR_FIRST_BIT = 75; -} +#pragma once + +namespace games::rb { + inline constexpr int TOUCH_SCALE_DEFAULT = 1000; + inline constexpr int TOUCH_PACKET_SIZE = 20; + inline constexpr int TOUCH_PACKET_DATA_OFFSET = 3; + + inline constexpr int X_SENSOR_COUNT = 48; + inline constexpr int X_SENSOR_FIRST_ACTIVE = 2; + inline constexpr int X_SENSOR_LAST_ACTIVE = 45; + inline constexpr int X_SENSOR_ACTIVE_COUNT = + X_SENSOR_LAST_ACTIVE - X_SENSOR_FIRST_ACTIVE + 1; + inline constexpr int X_SENSOR_FIRST_BIT = 88; + + inline constexpr int Y_SENSOR_COUNT = 76; + inline constexpr int Y_SENSOR_FIRST_BIT = 75; +} diff --git a/src/spice2x/games/sdvx/sdvx_live2d.cpp b/src/spice2x/games/sdvx/sdvx_live2d.cpp index 2d810f0..fdbfc94 100644 --- a/src/spice2x/games/sdvx/sdvx_live2d.cpp +++ b/src/spice2x/games/sdvx/sdvx_live2d.cpp @@ -1,72 +1,72 @@ -#include "sdvx_live2d.h" - -// only the Live2D-capable SDVX versions are 64-bit, so the whole feature is -// compiled out of 32-bit builds. -#ifdef SPICE64 - -#include - -#include "hooks/graphics/graphics.h" -#include "launcher/logger.h" -#include "util/logging.h" - -namespace games::sdvx { - - // Live2D in-game scene detection (for the -sdvxnolive2d "ingame" option). - // - // the game logs scene transitions as "I:Attach: in " / "I:Detach: in - // ". several scenes correspond to in-song gameplay (with the heavy - // Live2D rendering); we watch those log lines and keep the shared flag - // the d3d9 backend reads up to date. the hook never alters the log output - // (always returns false). - static bool live2d_scene_log_hook( - void *user, const std::string &data, logger::Style style, std::string &out) { - // any of these scenes counts as in-song gameplay (different play modes) - static const char *const gameplay_scenes[] = { - "in ALTERNATIVE_GAME_SCENE", - "in MEGAMIX_GAME_SCENE", - "in MEGAMIX_BATTLE", - "in BATTLE_GAME_SCENE", - "in AUTOMATION_GAME_SCENE", - "in ARENA_GAME_SCENE", - }; - bool in_gameplay_scene = false; - for (const auto *scene : gameplay_scenes) { - if (data.find(scene) != std::string::npos) { - in_gameplay_scene = true; - break; - } - } - if (!in_gameplay_scene) { - return false; - } - // note: log messages here must NOT contain any matched scene token, else - // this hook would re-enter itself when the message is pushed. - if (data.find("I:Attach: in ") != std::string::npos) { - if (!GRAPHICS_SDVX_LIVE2D_IN_GAMEPLAY.exchange(true, std::memory_order_relaxed)) { - log_info("sdvx", "Live2D skip: entering gameplay"); - } - } else if (data.find("I:Detach: in ") != std::string::npos) { - if (GRAPHICS_SDVX_LIVE2D_IN_GAMEPLAY.exchange(false, std::memory_order_relaxed)) { - log_info("sdvx", "Live2D skip: leaving gameplay"); - } - } - return false; - } - - void live2d_scene_detection_init() { - static bool installed = false; - if (installed) { - return; - } - installed = true; - // the logger's hook list is a persistent static, so registering here is - // safe even though this runs before logger::start(). we intentionally do - // NOT log a confirmation now: at this point the log file isn't open yet - // and the message would be dropped. the entering/leaving-gameplay lines - // above provide runtime confirmation once the logger is running. - logger::hook_add(live2d_scene_log_hook, nullptr); - } -} - -#endif // SPICE64 +#include "sdvx_live2d.h" + +// only the Live2D-capable SDVX versions are 64-bit, so the whole feature is +// compiled out of 32-bit builds. +#ifdef SPICE64 + +#include + +#include "hooks/graphics/graphics.h" +#include "launcher/logger.h" +#include "util/logging.h" + +namespace games::sdvx { + + // Live2D in-game scene detection (for the -sdvxnolive2d "ingame" option). + // + // the game logs scene transitions as "I:Attach: in " / "I:Detach: in + // ". several scenes correspond to in-song gameplay (with the heavy + // Live2D rendering); we watch those log lines and keep the shared flag + // the d3d9 backend reads up to date. the hook never alters the log output + // (always returns false). + static bool live2d_scene_log_hook( + void *user, const std::string &data, logger::Style style, std::string &out) { + // any of these scenes counts as in-song gameplay (different play modes) + static const char *const gameplay_scenes[] = { + "in ALTERNATIVE_GAME_SCENE", + "in MEGAMIX_GAME_SCENE", + "in MEGAMIX_BATTLE", + "in BATTLE_GAME_SCENE", + "in AUTOMATION_GAME_SCENE", + "in ARENA_GAME_SCENE", + }; + bool in_gameplay_scene = false; + for (const auto *scene : gameplay_scenes) { + if (data.find(scene) != std::string::npos) { + in_gameplay_scene = true; + break; + } + } + if (!in_gameplay_scene) { + return false; + } + // note: log messages here must NOT contain any matched scene token, else + // this hook would re-enter itself when the message is pushed. + if (data.find("I:Attach: in ") != std::string::npos) { + if (!GRAPHICS_SDVX_LIVE2D_IN_GAMEPLAY.exchange(true, std::memory_order_relaxed)) { + log_info("sdvx", "Live2D skip: entering gameplay"); + } + } else if (data.find("I:Detach: in ") != std::string::npos) { + if (GRAPHICS_SDVX_LIVE2D_IN_GAMEPLAY.exchange(false, std::memory_order_relaxed)) { + log_info("sdvx", "Live2D skip: leaving gameplay"); + } + } + return false; + } + + void live2d_scene_detection_init() { + static bool installed = false; + if (installed) { + return; + } + installed = true; + // the logger's hook list is a persistent static, so registering here is + // safe even though this runs before logger::start(). we intentionally do + // NOT log a confirmation now: at this point the log file isn't open yet + // and the message would be dropped. the entering/leaving-gameplay lines + // above provide runtime confirmation once the logger is running. + logger::hook_add(live2d_scene_log_hook, nullptr); + } +} + +#endif // SPICE64 diff --git a/src/spice2x/games/sdvx/sdvx_live2d.h b/src/spice2x/games/sdvx/sdvx_live2d.h index 5255806..e3bcaca 100644 --- a/src/spice2x/games/sdvx/sdvx_live2d.h +++ b/src/spice2x/games/sdvx/sdvx_live2d.h @@ -1,14 +1,14 @@ -#pragma once - -namespace games::sdvx { - -#ifdef SPICE64 - // installs the Live2D in-game scene-detection log hook used by the - // -sdvxnolive2d "ingame" option. does not require the SDVX game module to - // be attached, so it can be enabled purely from the launcher option. - // only the Live2D-capable SDVX versions are 64-bit, so this is compiled out - // of 32-bit builds. - void live2d_scene_detection_init(); -#endif - -} +#pragma once + +namespace games::sdvx { + +#ifdef SPICE64 + // installs the Live2D in-game scene-detection log hook used by the + // -sdvxnolive2d "ingame" option. does not require the SDVX game module to + // be attached, so it can be enabled purely from the launcher option. + // only the Live2D-capable SDVX versions are 64-bit, so this is compiled out + // of 32-bit builds. + void live2d_scene_detection_init(); +#endif + +} diff --git a/src/spice2x/hooks/audio/asio_driver_scan.cpp b/src/spice2x/hooks/audio/asio_driver_scan.cpp index 57e1a85..265c17e 100644 --- a/src/spice2x/hooks/audio/asio_driver_scan.cpp +++ b/src/spice2x/hooks/audio/asio_driver_scan.cpp @@ -1,84 +1,84 @@ -#include "asio_driver_scan.h" - -#include - -#include - -#include "util/utils.h" - -namespace hooks::audio { - - static constexpr char ASIO_REG_PATH[] = "software\\asio"; - static constexpr char ASIO_REG_DESC[] = "description"; - - // enumerate a single registry view, appending to entries while merging - // duplicates discovered in another view. Drivers are matched by name (not - // CLSID): the game's ASIO loader selects drivers by name, and some vendors - // register the same CLSID under different 32-bit/64-bit names (e.g. "XONAR - // SOUND CARD" vs "XONAR SOUND CARD(64)"), which are distinct user choices. - static void scan_view( - REGSAM wow64_flag, - bool is_64bit, - std::vector &entries) { - - HKEY hkEnum = nullptr; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, ASIO_REG_PATH, 0, - KEY_READ | wow64_flag, &hkEnum) != ERROR_SUCCESS) { - return; - } - - char key_name[256]; - for (DWORD index = 0; - RegEnumKeyA(hkEnum, index, key_name, sizeof(key_name)) == ERROR_SUCCESS; - index++) { - - // read description (display name), fall back to the key name. - // RegOpenKeyExA + RegQueryValueExA is used instead of RegGetValueA - // because the latter is unavailable on Windows XP. - char desc[256] = { 0 }; - DWORD size = sizeof(desc); - std::string name = key_name; - HKEY hkDriver = nullptr; - if (RegOpenKeyExA(hkEnum, key_name, 0, - KEY_QUERY_VALUE | wow64_flag, &hkDriver) == ERROR_SUCCESS) { - DWORD type = 0; - if (RegQueryValueExA(hkDriver, - ASIO_REG_DESC, - nullptr, - &type, - reinterpret_cast(desc), - &size) == ERROR_SUCCESS - && type == REG_SZ && desc[0]) { - // ensure null termination - desc[sizeof(desc) - 1] = '\0'; - name = desc; - } - RegCloseKey(hkDriver); - } - - // merge with an existing entry from the other view (match by name) - const std::string name_lower = strtolower(name); - auto it = std::find_if(entries.begin(), entries.end(), [&](const auto &e) { - return strtolower(e.name) == name_lower; - }); - if (it == entries.end()) { - entries.push_back({ name }); - it = entries.end() - 1; - } - it->found_32bit |= !is_64bit; - it->found_64bit |= is_64bit; - } - - RegCloseKey(hkEnum); - } - - std::vector scan_asio_drivers() { - std::vector entries; - - // 64-bit view first so it wins ordering when present in both - scan_view(KEY_WOW64_64KEY, true, entries); - scan_view(KEY_WOW64_32KEY, false, entries); - - return entries; - } -} +#include "asio_driver_scan.h" + +#include + +#include + +#include "util/utils.h" + +namespace hooks::audio { + + static constexpr char ASIO_REG_PATH[] = "software\\asio"; + static constexpr char ASIO_REG_DESC[] = "description"; + + // enumerate a single registry view, appending to entries while merging + // duplicates discovered in another view. Drivers are matched by name (not + // CLSID): the game's ASIO loader selects drivers by name, and some vendors + // register the same CLSID under different 32-bit/64-bit names (e.g. "XONAR + // SOUND CARD" vs "XONAR SOUND CARD(64)"), which are distinct user choices. + static void scan_view( + REGSAM wow64_flag, + bool is_64bit, + std::vector &entries) { + + HKEY hkEnum = nullptr; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, ASIO_REG_PATH, 0, + KEY_READ | wow64_flag, &hkEnum) != ERROR_SUCCESS) { + return; + } + + char key_name[256]; + for (DWORD index = 0; + RegEnumKeyA(hkEnum, index, key_name, sizeof(key_name)) == ERROR_SUCCESS; + index++) { + + // read description (display name), fall back to the key name. + // RegOpenKeyExA + RegQueryValueExA is used instead of RegGetValueA + // because the latter is unavailable on Windows XP. + char desc[256] = { 0 }; + DWORD size = sizeof(desc); + std::string name = key_name; + HKEY hkDriver = nullptr; + if (RegOpenKeyExA(hkEnum, key_name, 0, + KEY_QUERY_VALUE | wow64_flag, &hkDriver) == ERROR_SUCCESS) { + DWORD type = 0; + if (RegQueryValueExA(hkDriver, + ASIO_REG_DESC, + nullptr, + &type, + reinterpret_cast(desc), + &size) == ERROR_SUCCESS + && type == REG_SZ && desc[0]) { + // ensure null termination + desc[sizeof(desc) - 1] = '\0'; + name = desc; + } + RegCloseKey(hkDriver); + } + + // merge with an existing entry from the other view (match by name) + const std::string name_lower = strtolower(name); + auto it = std::find_if(entries.begin(), entries.end(), [&](const auto &e) { + return strtolower(e.name) == name_lower; + }); + if (it == entries.end()) { + entries.push_back({ name }); + it = entries.end() - 1; + } + it->found_32bit |= !is_64bit; + it->found_64bit |= is_64bit; + } + + RegCloseKey(hkEnum); + } + + std::vector scan_asio_drivers() { + std::vector entries; + + // 64-bit view first so it wins ordering when present in both + scan_view(KEY_WOW64_64KEY, true, entries); + scan_view(KEY_WOW64_32KEY, false, entries); + + return entries; + } +} diff --git a/src/spice2x/hooks/audio/asio_driver_scan.h b/src/spice2x/hooks/audio/asio_driver_scan.h index 25a0c47..2f72123 100644 --- a/src/spice2x/hooks/audio/asio_driver_scan.h +++ b/src/spice2x/hooks/audio/asio_driver_scan.h @@ -1,15 +1,15 @@ -#pragma once - -#include -#include - -namespace hooks::audio { - - struct AsioDriverScanEntry { - std::string name; - bool found_32bit = false; - bool found_64bit = false; - }; - - std::vector scan_asio_drivers(); -} +#pragma once + +#include +#include + +namespace hooks::audio { + + struct AsioDriverScanEntry { + std::string name; + bool found_32bit = false; + bool found_64bit = false; + }; + + std::vector scan_asio_drivers(); +} diff --git a/src/spice2x/hooks/audio/asio_proxy.cpp b/src/spice2x/hooks/audio/asio_proxy.cpp index 8cfbaf3..e57cffb 100644 --- a/src/spice2x/hooks/audio/asio_proxy.cpp +++ b/src/spice2x/hooks/audio/asio_proxy.cpp @@ -1,1067 +1,1067 @@ -#include "asio_proxy.h" - -#include -#include -#include -#include -#include -#include - -#include "external/asio/asiolist.h" -#include "hooks/audio/audio.h" -#include "util/logging.h" -#include "util/utils.h" - -namespace { - - // readable name for an ASIO sample type (e.g. "ASIOSTInt32LSB"), falling back to the - // numeric value for unknown types - const char *asio_sample_type_name(AsioSampleType type) { - switch (type) { - case ASIOSTInt16MSB: return "ASIOSTInt16MSB"; - case ASIOSTInt24MSB: return "ASIOSTInt24MSB"; - case ASIOSTInt32MSB: return "ASIOSTInt32MSB"; - case ASIOSTFloat32MSB: return "ASIOSTFloat32MSB"; - case ASIOSTFloat64MSB: return "ASIOSTFloat64MSB"; - case ASIOSTInt32MSB16: return "ASIOSTInt32MSB16"; - case ASIOSTInt32MSB18: return "ASIOSTInt32MSB18"; - case ASIOSTInt32MSB20: return "ASIOSTInt32MSB20"; - case ASIOSTInt32MSB24: return "ASIOSTInt32MSB24"; - case ASIOSTInt16LSB: return "ASIOSTInt16LSB"; - case ASIOSTInt24LSB: return "ASIOSTInt24LSB"; - case ASIOSTInt32LSB: return "ASIOSTInt32LSB"; - case ASIOSTFloat32LSB: return "ASIOSTFloat32LSB"; - case ASIOSTFloat64LSB: return "ASIOSTFloat64LSB"; - case ASIOSTInt32LSB16: return "ASIOSTInt32LSB16"; - case ASIOSTInt32LSB18: return "ASIOSTInt32LSB18"; - case ASIOSTInt32LSB20: return "ASIOSTInt32LSB20"; - case ASIOSTInt32LSB24: return "ASIOSTInt32LSB24"; - default: return "unknown"; - } - } - - // bytes occupied by one sample of the given ASIO type, or 0 for formats we cannot size - // (used to compute the byte length of a planar channel buffer for raw copies) - int asio_sample_bytes(AsioSampleType type) { - switch (type) { - case ASIOSTInt16LSB: - case ASIOSTInt16MSB: - return 2; - case ASIOSTInt24LSB: - case ASIOSTInt24MSB: - return 3; - case ASIOSTInt32LSB: - case ASIOSTInt32MSB: - case ASIOSTInt32LSB16: - case ASIOSTInt32LSB18: - case ASIOSTInt32LSB20: - case ASIOSTInt32LSB24: - case ASIOSTInt32MSB16: - case ASIOSTInt32MSB18: - case ASIOSTInt32MSB20: - case ASIOSTInt32MSB24: - case ASIOSTFloat32LSB: - case ASIOSTFloat32MSB: - return 4; - case ASIOSTFloat64LSB: - case ASIOSTFloat64MSB: - return 8; - default: - return 0; - } - } - - // readable name for a stereo downmix selection, used in our logs - const char *stereo_downmix_name(WrappedAsio::StereoDownmix mode) { - switch (mode) { - case WrappedAsio::StereoDownmix::None: return "none"; - case WrappedAsio::StereoDownmix::Front: return "front"; - case WrappedAsio::StereoDownmix::Center: return "center"; - case WrappedAsio::StereoDownmix::Rear: return "rear"; - case WrappedAsio::StereoDownmix::Side: return "side"; - default: return "unknown"; - } - } - - // duration in milliseconds of a buffer of the given frame count at a sample rate, or a - // negative sentinel when the frame count or sample rate is unusable - double frames_to_ms(long frames, AsioSampleRate sample_rate) { - if (frames < 0 || sample_rate <= 0.0) { - return -1.0; - } - return (frames * 1000.0) / sample_rate; - } - - // scales one planar ASIO output buffer (frames samples of the given type) by gain in - // place, clamping integer formats so a boost saturates instead of wrapping. unsupported - // formats are left untouched. runs on the driver's realtime thread, so no allocation, - // locking or logging here - void apply_gain_planar(void *buffer, long frames, AsioSampleType type, float gain) { - if (buffer == nullptr || frames <= 0) { - return; - } - switch (type) { - case ASIOSTFloat32LSB: { - auto p = static_cast(buffer); - for (long i = 0; i < frames; i++) { - p[i] = std::clamp(p[i] * gain, -1.0f, 1.0f); - } - break; - } - case ASIOSTFloat64LSB: { - auto p = static_cast(buffer); - for (long i = 0; i < frames; i++) { - p[i] = std::clamp(p[i] * static_cast(gain), -1.0, 1.0); - } - break; - } - case ASIOSTInt16LSB: { - auto p = static_cast(buffer); - for (long i = 0; i < frames; i++) { - p[i] = static_cast( - std::clamp(std::lround(p[i] * gain), -32768L, 32767L)); - } - break; - } - case ASIOSTInt24LSB: { - // packed 24-bit little-endian, 3 bytes per sample - auto bytes = static_cast(buffer); - for (long i = 0; i < frames; i++) { - uint8_t *s = bytes + i * 3; - int32_t v = s[0] | (s[1] << 8) | (s[2] << 16); - if (v & 0x800000) { - v |= ~0xFFFFFF; // sign extend - } - int64_t scaled = std::clamp( - std::llround(static_cast(v) * gain), -8388608, 8388607); - s[0] = scaled & 0xFF; - s[1] = (scaled >> 8) & 0xFF; - s[2] = (scaled >> 16) & 0xFF; - } - break; - } - case ASIOSTInt32LSB: { - auto p = static_cast(buffer); - for (long i = 0; i < frames; i++) { - p[i] = static_cast(std::clamp( - std::llround(static_cast(p[i]) * gain), INT32_MIN, INT32_MAX)); - } - break; - } - default: - // unsupported format (MSB, aligned 32-bit, DSD): leave untouched - break; - } - } - - // one wrapper per CLSID, kept alive for the process lifetime. ASIO drivers are - // single-instance, and some hardware drivers (e.g. Neva Uno) crash if their COM object - // is destroyed and re-created within a process - which the host triggers by leaking and - // re-instantiating the driver during startup probing. so we build the real driver and - // its wrapper once, hold a reference so it survives the host's Release calls, and hand - // the same wrapper back for every later CoCreate. as a result the real driver is created - // and initialized exactly once - std::mutex g_wrappers_mutex; - std::vector> g_wrappers; - - WrappedAsio *find_wrapper(REFCLSID clsid) { - std::lock_guard lock(g_wrappers_mutex); - for (auto &entry : g_wrappers) { - if (IsEqualCLSID(entry.first, clsid)) { - return entry.second; - } - } - - return nullptr; - } - - void store_wrapper(REFCLSID clsid, WrappedAsio *wrapper) { - std::lock_guard lock(g_wrappers_mutex); - g_wrappers.emplace_back(clsid, wrapper); - } - - // ASIO drivers registered on this system (CLSID + registry name), scanned once - const std::vector> ®istered_asio_drivers() { - static const std::vector> drivers = [] { - std::vector> result; - AsioDriverList driver_list; - for (const auto &driver : driver_list.driver_list) { - result.emplace_back(driver.clsid, driver.name); - log_info( - "audio::wrappedasio", - "registered ASIO driver: name='{}', clsid={}", - driver.name, - guid2s(driver.clsid)); - } - - log_info("audio::wrappedasio", "discovered {} registered ASIO driver(s)", result.size()); - return result; - }(); - - return drivers; - } - - std::string registered_asio_name(REFCLSID clsid) { - for (const auto &driver : registered_asio_drivers()) { - if (IsEqualCLSID(driver.first, clsid)) { - return driver.second; - } - } - - return guid2s(clsid); - } -} - -namespace hooks::audio::asio { - - bool is_asio_creation(REFCLSID rclsid, REFIID riid) { - - // ASIO hosts request the driver using its own CLSID as the interface id - if (!IsEqualGUID(rclsid, riid)) { - return false; - } - - for (const auto &driver : registered_asio_drivers()) { - if (IsEqualCLSID(driver.first, rclsid)) { - return true; - } - } - - return false; - } -} - -#pragma region IUnknown -WrappedAsio::StereoDownmix WrappedAsio::STEREO_DOWNMIX = WrappedAsio::StereoDownmix::None; -std::atomic WrappedAsio::active_instance {nullptr}; - -WrappedAsio::StereoDownmix WrappedAsio::name_to_stereo_downmix(const char *name) { - if (_stricmp(name, "front") == 0) { - return StereoDownmix::Front; - } else if (_stricmp(name, "center") == 0) { - return StereoDownmix::Center; - } else if (_stricmp(name, "rear") == 0) { - return StereoDownmix::Rear; - } else if (_stricmp(name, "side") == 0) { - return StereoDownmix::Side; - } - - return StereoDownmix::None; -} - -WrappedAsio::~WrappedAsio() { - this->detach_post_process(); - - // never runs mid-run: wrap() pins the wrapper so the refcount stays above zero until - // release_all_wrappers() drops the pin at shutdown - the only point this can fire, and - // only once the host has released its own references. tears down the real driver - this->pReal->Release(); - - log_info("audio::wrappedasio", "destroying wrapped ASIO driver, clsid={}", guid2s(this->clsid)); -} - -HRESULT STDMETHODCALLTYPE WrappedAsio::QueryInterface(REFIID riid, void **ppv) { - if (ppv == nullptr) { - return E_POINTER; - } - - // ASIO hosts query for the driver using its own CLSID as the IID - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, this->clsid)) { - this->AddRef(); - *ppv = static_cast(this); - - return S_OK; - } - - // the host is asking for some other interface; forward to the real driver. a failure - // here is a common reason a host discards a driver and retries - const HRESULT ret = this->pReal->QueryInterface(riid, ppv); - if (SUCCEEDED(ret)) { - log_info("audio::wrappedasio", "QueryInterface({}) -> forwarded to real driver", guid2s(riid)); - } else { - log_info( - "audio::wrappedasio", - "QueryInterface({}) -> not supported by driver, hr={:#x}", - guid2s(riid), - static_cast(ret)); - } - - return ret; -} - -ULONG STDMETHODCALLTYPE WrappedAsio::AddRef() { - // decoupled from the real driver: we count host references on the wrapper and hold a - // single reference on pReal for our lifetime. this neutralizes a host bug (iidx32+) - // that takes a duplicate AddRef with no matching Release, which would leak the driver - return ++this->ref_count; -} - -ULONG STDMETHODCALLTYPE WrappedAsio::Release() { - const ULONG refs = --this->ref_count; - if (refs == 0) { - delete this; - } - - return refs; -} -#pragma endregion - -#pragma region IAsio -AsioBool __thiscall WrappedAsio::init(void *sys_handle) { - // the real driver is single-instance and kept alive; initialize it exactly once. the - // host re-calls init() on each CoCreate during probing, but re-initializing a live - // driver crashes some hardware drivers, so once we have a live driver we report success - if (this->initialized) { - log_misc("audio::wrappedasio", "init skipped, '{}' already initialized", this->driver_name); - return AsioTrue; - } - - const AsioBool result = this->pReal->init(sys_handle); - if (result == AsioTrue) { - this->initialized = true; - log_info( - "audio::wrappedasio", - "init succeeded for '{}' (driver version {})", - this->driver_name, - this->pReal->get_driver_version()); - } else { - char message[128] = {}; - this->pReal->get_error_message(message); - log_warning("audio::wrappedasio", "init failed: {}", message); - } - - return result; -} - -void __thiscall WrappedAsio::get_driver_name(char *name) { - this->pReal->get_driver_name(name); -} - -long __thiscall WrappedAsio::get_driver_version() { - return this->pReal->get_driver_version(); -} - -void __thiscall WrappedAsio::get_error_message(char *string) { - this->pReal->get_error_message(string); -} - -AsioError __thiscall WrappedAsio::start() { - const AsioError result = this->pReal->start(); - if (result == ASE_OK) { - this->started = true; - log_info( - "audio::wrappedasio", - "start succeeded, ASIO stream is now running on '{}'", - this->driver_name); - } else { - log_warning("audio::wrappedasio", "start failed, err={}", static_cast(result)); - } - - return result; -} - -AsioError __thiscall WrappedAsio::stop() { - const AsioError result = this->pReal->stop(); - if (result == ASE_OK) { - this->started = false; - log_info("audio::wrappedasio", "stop succeeded, ASIO stream on '{}' halted", this->driver_name); - } else { - log_warning("audio::wrappedasio", "stop failed, err={}", static_cast(result)); - } - - return result; -} - -AsioError __thiscall WrappedAsio::get_channels(long *num_input_channels, long *num_output_channels) { - const AsioError result = this->pReal->get_channels(num_input_channels, num_output_channels); - if (result != ASE_OK) { - log_warning("audio::wrappedasio", "get_channels failed, err={}", static_cast(result)); - return result; - } - - if (force_two_channels() - && num_output_channels != nullptr - && *num_output_channels < FORCED_OUTPUT_CHANNELS) - { - // the device has fewer outputs than the game hardcodes; report the count it - // expects so it proceeds to create_buffers, where we forward only the real - // front pair and discard the rest - log_info( - "audio::wrappedasio", - "reporting output channel count as {} (device has {}) for forced two-channel", - FORCED_OUTPUT_CHANNELS, - *num_output_channels); - *num_output_channels = FORCED_OUTPUT_CHANNELS; - } - - log_info( - "audio::wrappedasio", - "get_channels -> in={}, out={}", - num_input_channels ? *num_input_channels : -1, - num_output_channels ? *num_output_channels : -1); - - return result; -} - -AsioError __thiscall WrappedAsio::get_latencies(long *input_latency, long *output_latency) { - const AsioError result = this->pReal->get_latencies(input_latency, output_latency); - if (result == ASE_OK) { - // include millisecond equivalents alongside the frame counts for readability - AsioSampleRate sample_rate = 0.0; - this->pReal->get_sample_rate(&sample_rate); - const long in_frames = input_latency ? *input_latency : -1; - const long out_frames = output_latency ? *output_latency : -1; - log_info( - "audio::wrappedasio", - "get_latencies -> in={} frames ({:.2f} ms), out={} frames ({:.2f} ms)", - in_frames, - frames_to_ms(in_frames, sample_rate), - out_frames, - frames_to_ms(out_frames, sample_rate)); - } else { - log_warning("audio::wrappedasio", "get_latencies failed, err={}", static_cast(result)); - } - - return result; -} - -AsioError __thiscall WrappedAsio::get_buffer_size( - long *min_size, - long *max_size, - long *preferred_size, - long *granularity) -{ - const AsioError result = this->pReal->get_buffer_size(min_size, max_size, preferred_size, granularity); - if (result != ASE_OK) { - log_warning("audio::wrappedasio", "get_buffer_size failed, err={}", static_cast(result)); - return result; - } - - // include millisecond equivalents alongside the frame counts for readability - AsioSampleRate sample_rate = 0.0; - this->pReal->get_sample_rate(&sample_rate); - const long min_frames = min_size ? *min_size : -1; - const long max_frames = max_size ? *max_size : -1; - const long preferred_frames = preferred_size ? *preferred_size : -1; - log_info( - "audio::wrappedasio", - "get_buffer_size -> min={} frames ({:.2f} ms), max={} frames ({:.2f} ms), " - "preferred={} frames ({:.2f} ms), granularity={}", - min_frames, - frames_to_ms(min_frames, sample_rate), - max_frames, - frames_to_ms(max_frames, sample_rate), - preferred_frames, - frames_to_ms(preferred_frames, sample_rate), - granularity ? *granularity : -1); - - return result; -} - -AsioError __thiscall WrappedAsio::can_sample_rate(AsioSampleRate sample_rate) { - const AsioError result = this->pReal->can_sample_rate(sample_rate); - if (result == ASE_OK) { - log_misc("audio::wrappedasio", "can_sample_rate({} Hz) -> supported", sample_rate); - } else { - log_misc( - "audio::wrappedasio", - "can_sample_rate({} Hz) -> not supported, err={}", - sample_rate, - static_cast(result)); - } - - return result; -} - -AsioError __thiscall WrappedAsio::get_sample_rate(AsioSampleRate *sample_rate) { - const AsioError result = this->pReal->get_sample_rate(sample_rate); - if (result == ASE_OK) { - log_misc("audio::wrappedasio", "get_sample_rate -> {} Hz", sample_rate ? *sample_rate : 0.0); - } else { - log_warning("audio::wrappedasio", "get_sample_rate failed, err={}", static_cast(result)); - } - - return result; -} - -AsioError __thiscall WrappedAsio::set_sample_rate(AsioSampleRate sample_rate) { - const AsioError result = this->pReal->set_sample_rate(sample_rate); - if (result == ASE_OK) { - log_info("audio::wrappedasio", "set_sample_rate({} Hz) succeeded", sample_rate); - } else { - log_warning( - "audio::wrappedasio", - "set_sample_rate({} Hz) failed, err={}", - sample_rate, - static_cast(result)); - } - - return result; -} - -AsioError __thiscall WrappedAsio::get_clock_sources(ASIOClockSource *clocks, long *num_sources) { - return this->pReal->get_clock_sources(clocks, num_sources); -} - -AsioError __thiscall WrappedAsio::set_clock_source(long reference) { - return this->pReal->set_clock_source(reference); -} - -AsioError __thiscall WrappedAsio::get_sample_position(ASIOSamples *s_pos, ASIOTimeStamp *t_stamp) { - return this->pReal->get_sample_position(s_pos, t_stamp); -} - -AsioError __thiscall WrappedAsio::get_channel_info(AsioChannelInfo *info) { - // forced two-channel: the game probes all output channels it thinks exist, but the - // device only has the real front pair. fabricate a plausible entry for the channels - // beyond the device without touching the real driver - they are discarded in - // create_buffers anyway - long real_in = 0, real_out = 0; - if (force_two_channels() - && info != nullptr - && info->is_input == AsioFalse - && this->pReal->get_channels(&real_in, &real_out) == ASE_OK - && info->channel >= real_out) - { - const long channel = info->channel; - - // report the real device's output sample format rather than a fixed type: when - // stereo downmix is active the game writes these dummy channels in this format and - // we raw-copy the selected pair onto the device's front channels, so the formats - // must match or the copy produces static. the guard above already proved the device - // has output channels, so query channel 0's format directly (avoiding a redundant - // get_channels) and fall back to Int32LSB if that query fails - AsioChannelInfo real_ci {}; - real_ci.channel = 0; - real_ci.is_input = AsioFalse; - AsioSampleType fake_type = ASIOSTInt32LSB; - if (this->pReal->get_channel_info(&real_ci) == ASE_OK) { - fake_type = real_ci.type; - } - - info->is_active = AsioTrue; - info->channel_group = 0; - info->type = fake_type; - snprintf(info->name, sizeof(info->name), "Fake ASIO OUT %ld", channel); - log_info( - "audio::wrappedasio", - "get_channel_info(channel={}, dir=output) -> fake channel, type={} ({})", - channel, - asio_sample_type_name(info->type), - static_cast(info->type)); - - return ASE_OK; - } - - const AsioError result = this->pReal->get_channel_info(info); - if (result == ASE_OK && info != nullptr) { - log_info( - "audio::wrappedasio", - "get_channel_info(channel={}, dir={}) -> active={}, group={}, type={} ({}), name='{}'", - info->channel, - info->is_input == AsioTrue ? "input" : "output", - info->is_active == AsioTrue, - info->channel_group, - asio_sample_type_name(info->type), - static_cast(info->type), - info->name); - } else if (result != ASE_OK) { - log_warning("audio::wrappedasio", "get_channel_info failed, err={}", static_cast(result)); - } - - return result; -} - -AsioCallbacks *WrappedAsio::install_proxy_callbacks(AsioCallbacks *game_callbacks) { - const float gain = hooks::audio::VOLUME_BOOST; - - // start from a clean slate; a previous buffer set may have left state behind - this->volume_channels.clear(); - this->volume_active = false; - this->downmix_active = false; - - const bool want_volume = (gain != 1.0f); - - // front is the device's own pair, so selecting it (or None) means no copy is needed - const bool want_downmix = (STEREO_DOWNMIX != StereoDownmix::None - && STEREO_DOWNMIX != StereoDownmix::Front); - - // no post-processing configured (or no callbacks to wrap): pass the game's callbacks - // straight through and do zero realtime work, exactly as before - if ((!want_volume && !want_downmix) || game_callbacks == nullptr) { - return game_callbacks; - } - - if (want_volume) { - this->volume_active = true; - this->volume_gain = gain; - } - - // the trampolines reach the game's buffer_switch through this copy, regardless of which - // effect is active - this->game_callbacks = *game_callbacks; - - // wrap only the buffer-switch callbacks, where the audio data lives and we rework it. - // the other two carry no data we touch, so forward the game's own pointers unchanged - - // the driver expects them non-null and the game already owns their context - this->proxy_callbacks = {}; - this->proxy_callbacks.buffer_switch = &WrappedAsio::proxy_buffer_switch; - this->proxy_callbacks.sample_rate_did_change = game_callbacks->sample_rate_did_change; - this->proxy_callbacks.asio_message = game_callbacks->asio_message; - this->proxy_callbacks.buffer_switch_time_info = - game_callbacks->buffer_switch_time_info ? &WrappedAsio::proxy_buffer_switch_time_info : nullptr; - - return &this->proxy_callbacks; -} - -AsioSampleType WrappedAsio::device_output_sample_type() { - long real_in = 0, real_out = 0; - if (this->pReal->get_channels(&real_in, &real_out) != ASE_OK || real_out <= 0) { - return ASIOSTLastEntry; - } - - AsioChannelInfo ci {}; - ci.channel = 0; - ci.is_input = AsioFalse; - if (this->pReal->get_channel_info(&ci) != ASE_OK) { - return ASIOSTLastEntry; - } - return ci.type; -} - -void WrappedAsio::record_volume_output_channel(const AsioBufferInfo &info) { - if (info.is_input != AsioFalse) { - return; - } - - // ask the real driver for this channel's sample format so the realtime path knows how - // to scale it; fall back to a sentinel that apply_gain_planar leaves untouched - AsioChannelInfo ci {}; - ci.channel = info.channel_num; - ci.is_input = AsioFalse; - AsioSampleType type = ASIOSTLastEntry; - if (this->pReal->get_channel_info(&ci) == ASE_OK) { - type = ci.type; - } - - VolumeOutputChannel ch; - ch.buffers[0] = info.buffers[0]; - ch.buffers[1] = info.buffers[1]; - ch.type = type; - this->volume_channels.push_back(ch); -} - -void WrappedAsio::record_downmix_channels( - AsioBufferInfo *buffer_infos, long num_channels, long buffer_size) -{ - // map the selected pair to source channel indices (0-indexed, standard 7.1 layout); - // None and Front need no copy - long src_left = 0, src_right = 0; - switch (STEREO_DOWNMIX) { - case StereoDownmix::Center: src_left = 2; src_right = 2; break; - case StereoDownmix::Rear: src_left = 4; src_right = 5; break; - case StereoDownmix::Side: src_left = 6; src_right = 7; break; - default: return; - } - - // find the double-buffer pair for a given output channel among those the game created, - // or nullptr if the device does not expose it - auto find_output = [&](long channel) -> void ** { - for (long i = 0; i < num_channels; i++) { - AsioBufferInfo &bi = buffer_infos[i]; - if (bi.is_input == AsioFalse && bi.channel_num == channel) { - return bi.buffers; - } - } - return nullptr; - }; - - // destinations are device channels 0/1; sources are the selected pair - void **dst0 = find_output(0); - void **dst1 = find_output(1); - void **src_l = find_output(src_left); - void **src_r = find_output(src_right); - if (dst0 == nullptr || dst1 == nullptr || src_l == nullptr || src_r == nullptr) { - log_warning( - "audio::wrappedasio", - "stereo downmix disabled: device is missing the front pair or source channels " - "{}/{} (game created {} channel(s))", - src_left, - src_right, - num_channels); - return; - } - - // all device output channels share one sample format; query it to size the copy - const AsioSampleType type = this->device_output_sample_type(); - const int sample_bytes = asio_sample_bytes(type); - if (sample_bytes <= 0) { - log_warning( - "audio::wrappedasio", - "stereo downmix disabled: unsupported sample format {} ({})", - asio_sample_type_name(type), - static_cast(type)); - return; - } - - this->downmix_copies[0] = {{dst0[0], dst0[1]}, {src_l[0], src_l[1]}}; - this->downmix_copies[1] = {{dst1[0], dst1[1]}, {src_r[0], src_r[1]}}; - this->downmix_bytes = static_cast(buffer_size) * sample_bytes; - this->downmix_active = true; - - log_info( - "audio::wrappedasio", - "stereo downmix active: pair={} (src {}/{} -> device 0/1), {} frames, {} byte(s)/sample", - stereo_downmix_name(STEREO_DOWNMIX), - src_left, - src_right, - buffer_size, - sample_bytes); -} - -void WrappedAsio::publish_post_process(long buffer_size) { - if (!this->volume_active && !this->downmix_active) { - return; - } - - // everything the realtime thread reads is now in place; make ourselves reachable - this->volume_buffer_size = buffer_size; - WrappedAsio::active_instance.store(this, std::memory_order_release); - - if (this->volume_active) { - log_info( - "audio::wrappedasio", - "volume boost active: gain={}, scaling {} output channel(s)", - this->volume_gain, - this->volume_channels.size()); - } -} - -void WrappedAsio::detach_post_process() { - // stop our realtime trampolines from reaching this wrapper, but only if we are the - // currently published instance - WrappedAsio *expected = this; - WrappedAsio::active_instance.compare_exchange_strong(expected, nullptr); -} - -void WrappedAsio::quiesce_for_reuse() { - // a previous abandoned probing cycle may have left a running stream and live buffers on - // the real driver without calling stop/dispose; tear that down now (without destroying - // the driver) so the host's next create_buffers starts clean. stop() guarantees no - // further buffer_switch, and dispose_buffers detaches our trampolines - if (this->started) { - log_info( - "audio::wrappedasio", - "reuse: stopping leftover stream on '{}' before handing the driver back", - this->driver_name); - this->stop(); - } - if (this->buffers_created) { - log_info( - "audio::wrappedasio", - "reuse: disposing leftover buffers on '{}' before handing the driver back", - this->driver_name); - this->dispose_buffers(); - } -} - -void WrappedAsio::apply_output_volume(long double_buffer_index) { - if (double_buffer_index != 0 && double_buffer_index != 1) { - return; - } - const float gain = this->volume_gain; - const long frames = this->volume_buffer_size; - for (const VolumeOutputChannel &ch : this->volume_channels) { - apply_gain_planar(ch.buffers[double_buffer_index], frames, ch.type, gain); - } -} - -void WrappedAsio::apply_downmix(long double_buffer_index) { - if (!this->downmix_active) { - return; - } - if (double_buffer_index != 0 && double_buffer_index != 1) { - return; - } - - // raw planar copy of the selected source channels onto device channels 0/1; for the - // center selection both copies share one source. skip self-copies (front) - for (const DownmixCopy © : this->downmix_copies) { - void *dst = copy.dst[double_buffer_index]; - const void *src = copy.src[double_buffer_index]; - if (dst != nullptr && src != nullptr && dst != src) { - std::memcpy(dst, src, this->downmix_bytes); - } - } -} - -void __cdecl WrappedAsio::proxy_buffer_switch(long double_buffer_index, AsioBool direct_process) { - WrappedAsio *self = WrappedAsio::active_instance.load(std::memory_order_acquire); - if (self == nullptr) { - return; - } - - // let the game write its samples into the driver buffers first, then rework them before - // the driver plays this half on the next switch: downmix first (arrange channels 0/1), - // then scale the device outputs by the volume boost - if (self->game_callbacks.buffer_switch != nullptr) { - self->game_callbacks.buffer_switch(double_buffer_index, direct_process); - } - self->apply_downmix(double_buffer_index); - self->apply_output_volume(double_buffer_index); -} - -AsioTime * __cdecl WrappedAsio::proxy_buffer_switch_time_info( - AsioTime *params, long double_buffer_index, AsioBool direct_process) -{ - WrappedAsio *self = WrappedAsio::active_instance.load(std::memory_order_acquire); - if (self == nullptr) { - return params; - } - - AsioTime *ret = params; - if (self->game_callbacks.buffer_switch_time_info != nullptr) { - ret = self->game_callbacks.buffer_switch_time_info( - params, double_buffer_index, direct_process); - } else if (self->game_callbacks.buffer_switch != nullptr) { - self->game_callbacks.buffer_switch(double_buffer_index, direct_process); - } - self->apply_downmix(double_buffer_index); - self->apply_output_volume(double_buffer_index); - return ret; -} - -AsioError __thiscall WrappedAsio::create_buffers( - AsioBufferInfo *buffer_infos, - long num_channels, - long buffer_size, - AsioCallbacks *callbacks) -{ - // swap in our buffer-switch trampolines if any post-processing is configured, so the - // real driver calls us and we rework its output after the game fills it (no-op otherwise) - AsioCallbacks *effective = this->install_proxy_callbacks(callbacks); - - if (force_two_channels()) { - return this->create_buffers_front_pair(buffer_infos, num_channels, buffer_size, effective); - } - - const AsioError result = this->pReal->create_buffers(buffer_infos, num_channels, buffer_size, effective); - if (result == ASE_OK) { - log_info( - "audio::wrappedasio", - "create_buffers(channels={}, size={} frames) succeeded", - num_channels, - buffer_size); - - // capture the post-process state now the buffers exist, then publish ourselves to - // the realtime thread once everything is in place. downmix is not recorded here: any - // active stereo extraction forces the front-pair path above, so this path only ever - // runs the volume boost - if (this->volume_active) { - for (long i = 0; i < num_channels; i++) { - this->record_volume_output_channel(buffer_infos[i]); - } - } - this->publish_post_process(buffer_size); - this->buffers_created = true; - } else { - log_warning( - "audio::wrappedasio", - "create_buffers(channels={}, size={} frames) failed, err={}", - num_channels, - buffer_size, - static_cast(result)); - } - - return result; -} - -AsioError WrappedAsio::create_buffers_front_pair( - AsioBufferInfo *buffer_infos, - long num_channels, - long buffer_size, - AsioCallbacks *callbacks) -{ - // front-pair extraction (forced two-channel ASIO): the game asks for more output - // channels than the real device has (e.g. 8 vs 2). forward only the channels the - // device actually provides (channel 0/1 = front L/R) and hand the game throwaway - // buffers for the rest, so its front mix lands on the device and the surround - // channels are discarded. the game writes directly into the driver/dummy buffers - // from its own bufferSwitch; the realtime work we do is the optional volume boost - // (scaling the forwarded device channels) and, if a non-front stereo downmix is - // selected, copying that pair from its dummy buffers onto the device's front pair - long real_in = 0, real_out = 0; - const AsioError ch_result = this->pReal->get_channels(&real_in, &real_out); - if (ch_result != ASE_OK) { - log_warning( - "audio::wrappedasio", - "create_buffers: get_channels failed, err={}", - static_cast(ch_result)); - return ch_result; - } - - // partition the requested channels: those the device can serve are forwarded, the rest - // are discarded. record source indices for both so we can patch the game's array after - std::vector forwarded; - std::vector forwarded_src; - std::vector discarded_src; - forwarded.reserve(num_channels); - forwarded_src.reserve(num_channels); - discarded_src.reserve(num_channels); - for (long i = 0; i < num_channels; i++) { - const AsioBufferInfo &bi = buffer_infos[i]; - const long limit = (bi.is_input == AsioTrue) ? real_in : real_out; - if (bi.channel_num < limit) { - forwarded.push_back(bi); - forwarded_src.push_back(i); - } else { - discarded_src.push_back(i); - } - } - - const AsioError result = this->pReal->create_buffers( - forwarded.data(), static_cast(forwarded.size()), buffer_size, callbacks); - if (result != ASE_OK) { - log_warning( - "audio::wrappedasio", - "create_buffers(forwarded={} of {}, size={} frames) failed, err={}", - forwarded.size(), - num_channels, - buffer_size, - static_cast(result)); - return result; - } - - // copy the real driver buffer pointers back into the game's array - for (size_t k = 0; k < forwarded.size(); k++) { - AsioBufferInfo &dst = buffer_infos[forwarded_src[k]]; - dst.buffers[0] = forwarded[k].buffers[0]; - dst.buffers[1] = forwarded[k].buffers[1]; - - // only the forwarded channels reach the device, so those are the ones the volume - // boost scales (the discarded channels go to throwaway buffers below) - if (this->volume_active) { - this->record_volume_output_channel(dst); - } - } - - // hand throwaway double buffers to the discarded channels. sized generously at - // 8 bytes/sample (covers every ASIO sample type) so the game can never overrun them - // regardless of the negotiated format - this->dummy_buffers.clear(); - this->dummy_buffers.reserve(discarded_src.size() * 2); - const size_t dummy_bytes = static_cast(buffer_size) * 8; - for (const long i : discarded_src) { - for (void *&buffer : buffer_infos[i].buffers) { - auto buf = std::make_unique(dummy_bytes); - std::memset(buf.get(), 0, dummy_bytes); - buffer = buf.get(); - this->dummy_buffers.push_back(std::move(buf)); - } - } - - // record the downmix source/destination buffers now the array is fully patched: the - // selected pair (e.g. rear) lives in the dummy buffers above, the device front pair in - // the forwarded driver buffers, so the realtime copy lands the chosen pair on the - // device's 2.0 output. then publish ourselves to the realtime thread - this->record_downmix_channels(buffer_infos, num_channels, buffer_size); - this->publish_post_process(buffer_size); - this->buffers_created = true; - - log_info( - "audio::wrappedasio", - "create_buffers: front-pair extraction - forwarded {} channel(s) to device, " - "discarded {} (requested {}, size={} frames)", - forwarded.size(), - discarded_src.size(), - num_channels, - buffer_size); - - return ASE_OK; -} - -AsioError __thiscall WrappedAsio::dispose_buffers() { - // stop our realtime trampolines from touching buffers the driver is about to free - this->detach_post_process(); - - const AsioError result = this->pReal->dispose_buffers(); - this->dummy_buffers.clear(); - this->volume_channels.clear(); - this->volume_active = false; - this->downmix_active = false; - this->buffers_created = false; - return result; -} - -AsioError __thiscall WrappedAsio::control_panel() { - return this->pReal->control_panel(); -} - -AsioError __thiscall WrappedAsio::future(long selector, void *opt) { - return this->pReal->future(selector, opt); -} - -AsioError __thiscall WrappedAsio::output_ready() { - return this->pReal->output_ready(); -} -#pragma endregion - -namespace hooks::audio::asio { - - IUnknown *wrap(REFCLSID clsid, void *real) { - log_info("audio::wrappedasio", "wrapping ASIO driver interface, clsid={}", guid2s(clsid)); - - auto *wrapper = new WrappedAsio( - reinterpret_cast(real), clsid, registered_asio_name(clsid)); - - // pin the wrapper (and the real driver it owns) for the process lifetime; later - // CoCreate calls reuse this same instance via wrap_existing - wrapper->AddRef(); - store_wrapper(clsid, wrapper); - - return static_cast(wrapper); - } - - IUnknown *wrap_existing(REFCLSID clsid) { - WrappedAsio *wrapper = find_wrapper(clsid); - if (wrapper == nullptr) { - return nullptr; - } - - log_misc( - "audio::wrappedasio", - "reusing cached ASIO driver instance, clsid={}", - guid2s(clsid)); - - wrapper->quiesce_for_reuse(); - - // hand the host another reference to the one instance we keep alive - wrapper->AddRef(); - return static_cast(wrapper); - } - - void release_all_wrappers() { - // collect the pinned wrappers under the lock, then drop our references outside it so - // a final Release (which can run the driver's own teardown) never happens while the - // lock is held - std::vector> wrappers; - { - std::lock_guard lock(g_wrappers_mutex); - wrappers = g_wrappers; - g_wrappers.clear(); - } - - for (auto &entry : wrappers) { - // drop only the pin wrap() took. if the host has already released its own - // references (the normal case at shutdown) this destroys the wrapper and releases - // the real driver; if the host is still using it, the refcount stays above zero - // and we leave its live stream/buffers untouched - log_misc( - "audio::wrappedasio", - "releasing cached ASIO driver instance, clsid={}", - guid2s(entry.first)); - entry.second->Release(); - } - } -} - +#include "asio_proxy.h" + +#include +#include +#include +#include +#include +#include + +#include "external/asio/asiolist.h" +#include "hooks/audio/audio.h" +#include "util/logging.h" +#include "util/utils.h" + +namespace { + + // readable name for an ASIO sample type (e.g. "ASIOSTInt32LSB"), falling back to the + // numeric value for unknown types + const char *asio_sample_type_name(AsioSampleType type) { + switch (type) { + case ASIOSTInt16MSB: return "ASIOSTInt16MSB"; + case ASIOSTInt24MSB: return "ASIOSTInt24MSB"; + case ASIOSTInt32MSB: return "ASIOSTInt32MSB"; + case ASIOSTFloat32MSB: return "ASIOSTFloat32MSB"; + case ASIOSTFloat64MSB: return "ASIOSTFloat64MSB"; + case ASIOSTInt32MSB16: return "ASIOSTInt32MSB16"; + case ASIOSTInt32MSB18: return "ASIOSTInt32MSB18"; + case ASIOSTInt32MSB20: return "ASIOSTInt32MSB20"; + case ASIOSTInt32MSB24: return "ASIOSTInt32MSB24"; + case ASIOSTInt16LSB: return "ASIOSTInt16LSB"; + case ASIOSTInt24LSB: return "ASIOSTInt24LSB"; + case ASIOSTInt32LSB: return "ASIOSTInt32LSB"; + case ASIOSTFloat32LSB: return "ASIOSTFloat32LSB"; + case ASIOSTFloat64LSB: return "ASIOSTFloat64LSB"; + case ASIOSTInt32LSB16: return "ASIOSTInt32LSB16"; + case ASIOSTInt32LSB18: return "ASIOSTInt32LSB18"; + case ASIOSTInt32LSB20: return "ASIOSTInt32LSB20"; + case ASIOSTInt32LSB24: return "ASIOSTInt32LSB24"; + default: return "unknown"; + } + } + + // bytes occupied by one sample of the given ASIO type, or 0 for formats we cannot size + // (used to compute the byte length of a planar channel buffer for raw copies) + int asio_sample_bytes(AsioSampleType type) { + switch (type) { + case ASIOSTInt16LSB: + case ASIOSTInt16MSB: + return 2; + case ASIOSTInt24LSB: + case ASIOSTInt24MSB: + return 3; + case ASIOSTInt32LSB: + case ASIOSTInt32MSB: + case ASIOSTInt32LSB16: + case ASIOSTInt32LSB18: + case ASIOSTInt32LSB20: + case ASIOSTInt32LSB24: + case ASIOSTInt32MSB16: + case ASIOSTInt32MSB18: + case ASIOSTInt32MSB20: + case ASIOSTInt32MSB24: + case ASIOSTFloat32LSB: + case ASIOSTFloat32MSB: + return 4; + case ASIOSTFloat64LSB: + case ASIOSTFloat64MSB: + return 8; + default: + return 0; + } + } + + // readable name for a stereo downmix selection, used in our logs + const char *stereo_downmix_name(WrappedAsio::StereoDownmix mode) { + switch (mode) { + case WrappedAsio::StereoDownmix::None: return "none"; + case WrappedAsio::StereoDownmix::Front: return "front"; + case WrappedAsio::StereoDownmix::Center: return "center"; + case WrappedAsio::StereoDownmix::Rear: return "rear"; + case WrappedAsio::StereoDownmix::Side: return "side"; + default: return "unknown"; + } + } + + // duration in milliseconds of a buffer of the given frame count at a sample rate, or a + // negative sentinel when the frame count or sample rate is unusable + double frames_to_ms(long frames, AsioSampleRate sample_rate) { + if (frames < 0 || sample_rate <= 0.0) { + return -1.0; + } + return (frames * 1000.0) / sample_rate; + } + + // scales one planar ASIO output buffer (frames samples of the given type) by gain in + // place, clamping integer formats so a boost saturates instead of wrapping. unsupported + // formats are left untouched. runs on the driver's realtime thread, so no allocation, + // locking or logging here + void apply_gain_planar(void *buffer, long frames, AsioSampleType type, float gain) { + if (buffer == nullptr || frames <= 0) { + return; + } + switch (type) { + case ASIOSTFloat32LSB: { + auto p = static_cast(buffer); + for (long i = 0; i < frames; i++) { + p[i] = std::clamp(p[i] * gain, -1.0f, 1.0f); + } + break; + } + case ASIOSTFloat64LSB: { + auto p = static_cast(buffer); + for (long i = 0; i < frames; i++) { + p[i] = std::clamp(p[i] * static_cast(gain), -1.0, 1.0); + } + break; + } + case ASIOSTInt16LSB: { + auto p = static_cast(buffer); + for (long i = 0; i < frames; i++) { + p[i] = static_cast( + std::clamp(std::lround(p[i] * gain), -32768L, 32767L)); + } + break; + } + case ASIOSTInt24LSB: { + // packed 24-bit little-endian, 3 bytes per sample + auto bytes = static_cast(buffer); + for (long i = 0; i < frames; i++) { + uint8_t *s = bytes + i * 3; + int32_t v = s[0] | (s[1] << 8) | (s[2] << 16); + if (v & 0x800000) { + v |= ~0xFFFFFF; // sign extend + } + int64_t scaled = std::clamp( + std::llround(static_cast(v) * gain), -8388608, 8388607); + s[0] = scaled & 0xFF; + s[1] = (scaled >> 8) & 0xFF; + s[2] = (scaled >> 16) & 0xFF; + } + break; + } + case ASIOSTInt32LSB: { + auto p = static_cast(buffer); + for (long i = 0; i < frames; i++) { + p[i] = static_cast(std::clamp( + std::llround(static_cast(p[i]) * gain), INT32_MIN, INT32_MAX)); + } + break; + } + default: + // unsupported format (MSB, aligned 32-bit, DSD): leave untouched + break; + } + } + + // one wrapper per CLSID, kept alive for the process lifetime. ASIO drivers are + // single-instance, and some hardware drivers (e.g. Neva Uno) crash if their COM object + // is destroyed and re-created within a process - which the host triggers by leaking and + // re-instantiating the driver during startup probing. so we build the real driver and + // its wrapper once, hold a reference so it survives the host's Release calls, and hand + // the same wrapper back for every later CoCreate. as a result the real driver is created + // and initialized exactly once + std::mutex g_wrappers_mutex; + std::vector> g_wrappers; + + WrappedAsio *find_wrapper(REFCLSID clsid) { + std::lock_guard lock(g_wrappers_mutex); + for (auto &entry : g_wrappers) { + if (IsEqualCLSID(entry.first, clsid)) { + return entry.second; + } + } + + return nullptr; + } + + void store_wrapper(REFCLSID clsid, WrappedAsio *wrapper) { + std::lock_guard lock(g_wrappers_mutex); + g_wrappers.emplace_back(clsid, wrapper); + } + + // ASIO drivers registered on this system (CLSID + registry name), scanned once + const std::vector> ®istered_asio_drivers() { + static const std::vector> drivers = [] { + std::vector> result; + AsioDriverList driver_list; + for (const auto &driver : driver_list.driver_list) { + result.emplace_back(driver.clsid, driver.name); + log_info( + "audio::wrappedasio", + "registered ASIO driver: name='{}', clsid={}", + driver.name, + guid2s(driver.clsid)); + } + + log_info("audio::wrappedasio", "discovered {} registered ASIO driver(s)", result.size()); + return result; + }(); + + return drivers; + } + + std::string registered_asio_name(REFCLSID clsid) { + for (const auto &driver : registered_asio_drivers()) { + if (IsEqualCLSID(driver.first, clsid)) { + return driver.second; + } + } + + return guid2s(clsid); + } +} + +namespace hooks::audio::asio { + + bool is_asio_creation(REFCLSID rclsid, REFIID riid) { + + // ASIO hosts request the driver using its own CLSID as the interface id + if (!IsEqualGUID(rclsid, riid)) { + return false; + } + + for (const auto &driver : registered_asio_drivers()) { + if (IsEqualCLSID(driver.first, rclsid)) { + return true; + } + } + + return false; + } +} + +#pragma region IUnknown +WrappedAsio::StereoDownmix WrappedAsio::STEREO_DOWNMIX = WrappedAsio::StereoDownmix::None; +std::atomic WrappedAsio::active_instance {nullptr}; + +WrappedAsio::StereoDownmix WrappedAsio::name_to_stereo_downmix(const char *name) { + if (_stricmp(name, "front") == 0) { + return StereoDownmix::Front; + } else if (_stricmp(name, "center") == 0) { + return StereoDownmix::Center; + } else if (_stricmp(name, "rear") == 0) { + return StereoDownmix::Rear; + } else if (_stricmp(name, "side") == 0) { + return StereoDownmix::Side; + } + + return StereoDownmix::None; +} + +WrappedAsio::~WrappedAsio() { + this->detach_post_process(); + + // never runs mid-run: wrap() pins the wrapper so the refcount stays above zero until + // release_all_wrappers() drops the pin at shutdown - the only point this can fire, and + // only once the host has released its own references. tears down the real driver + this->pReal->Release(); + + log_info("audio::wrappedasio", "destroying wrapped ASIO driver, clsid={}", guid2s(this->clsid)); +} + +HRESULT STDMETHODCALLTYPE WrappedAsio::QueryInterface(REFIID riid, void **ppv) { + if (ppv == nullptr) { + return E_POINTER; + } + + // ASIO hosts query for the driver using its own CLSID as the IID + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, this->clsid)) { + this->AddRef(); + *ppv = static_cast(this); + + return S_OK; + } + + // the host is asking for some other interface; forward to the real driver. a failure + // here is a common reason a host discards a driver and retries + const HRESULT ret = this->pReal->QueryInterface(riid, ppv); + if (SUCCEEDED(ret)) { + log_info("audio::wrappedasio", "QueryInterface({}) -> forwarded to real driver", guid2s(riid)); + } else { + log_info( + "audio::wrappedasio", + "QueryInterface({}) -> not supported by driver, hr={:#x}", + guid2s(riid), + static_cast(ret)); + } + + return ret; +} + +ULONG STDMETHODCALLTYPE WrappedAsio::AddRef() { + // decoupled from the real driver: we count host references on the wrapper and hold a + // single reference on pReal for our lifetime. this neutralizes a host bug (iidx32+) + // that takes a duplicate AddRef with no matching Release, which would leak the driver + return ++this->ref_count; +} + +ULONG STDMETHODCALLTYPE WrappedAsio::Release() { + const ULONG refs = --this->ref_count; + if (refs == 0) { + delete this; + } + + return refs; +} +#pragma endregion + +#pragma region IAsio +AsioBool __thiscall WrappedAsio::init(void *sys_handle) { + // the real driver is single-instance and kept alive; initialize it exactly once. the + // host re-calls init() on each CoCreate during probing, but re-initializing a live + // driver crashes some hardware drivers, so once we have a live driver we report success + if (this->initialized) { + log_misc("audio::wrappedasio", "init skipped, '{}' already initialized", this->driver_name); + return AsioTrue; + } + + const AsioBool result = this->pReal->init(sys_handle); + if (result == AsioTrue) { + this->initialized = true; + log_info( + "audio::wrappedasio", + "init succeeded for '{}' (driver version {})", + this->driver_name, + this->pReal->get_driver_version()); + } else { + char message[128] = {}; + this->pReal->get_error_message(message); + log_warning("audio::wrappedasio", "init failed: {}", message); + } + + return result; +} + +void __thiscall WrappedAsio::get_driver_name(char *name) { + this->pReal->get_driver_name(name); +} + +long __thiscall WrappedAsio::get_driver_version() { + return this->pReal->get_driver_version(); +} + +void __thiscall WrappedAsio::get_error_message(char *string) { + this->pReal->get_error_message(string); +} + +AsioError __thiscall WrappedAsio::start() { + const AsioError result = this->pReal->start(); + if (result == ASE_OK) { + this->started = true; + log_info( + "audio::wrappedasio", + "start succeeded, ASIO stream is now running on '{}'", + this->driver_name); + } else { + log_warning("audio::wrappedasio", "start failed, err={}", static_cast(result)); + } + + return result; +} + +AsioError __thiscall WrappedAsio::stop() { + const AsioError result = this->pReal->stop(); + if (result == ASE_OK) { + this->started = false; + log_info("audio::wrappedasio", "stop succeeded, ASIO stream on '{}' halted", this->driver_name); + } else { + log_warning("audio::wrappedasio", "stop failed, err={}", static_cast(result)); + } + + return result; +} + +AsioError __thiscall WrappedAsio::get_channels(long *num_input_channels, long *num_output_channels) { + const AsioError result = this->pReal->get_channels(num_input_channels, num_output_channels); + if (result != ASE_OK) { + log_warning("audio::wrappedasio", "get_channels failed, err={}", static_cast(result)); + return result; + } + + if (force_two_channels() + && num_output_channels != nullptr + && *num_output_channels < FORCED_OUTPUT_CHANNELS) + { + // the device has fewer outputs than the game hardcodes; report the count it + // expects so it proceeds to create_buffers, where we forward only the real + // front pair and discard the rest + log_info( + "audio::wrappedasio", + "reporting output channel count as {} (device has {}) for forced two-channel", + FORCED_OUTPUT_CHANNELS, + *num_output_channels); + *num_output_channels = FORCED_OUTPUT_CHANNELS; + } + + log_info( + "audio::wrappedasio", + "get_channels -> in={}, out={}", + num_input_channels ? *num_input_channels : -1, + num_output_channels ? *num_output_channels : -1); + + return result; +} + +AsioError __thiscall WrappedAsio::get_latencies(long *input_latency, long *output_latency) { + const AsioError result = this->pReal->get_latencies(input_latency, output_latency); + if (result == ASE_OK) { + // include millisecond equivalents alongside the frame counts for readability + AsioSampleRate sample_rate = 0.0; + this->pReal->get_sample_rate(&sample_rate); + const long in_frames = input_latency ? *input_latency : -1; + const long out_frames = output_latency ? *output_latency : -1; + log_info( + "audio::wrappedasio", + "get_latencies -> in={} frames ({:.2f} ms), out={} frames ({:.2f} ms)", + in_frames, + frames_to_ms(in_frames, sample_rate), + out_frames, + frames_to_ms(out_frames, sample_rate)); + } else { + log_warning("audio::wrappedasio", "get_latencies failed, err={}", static_cast(result)); + } + + return result; +} + +AsioError __thiscall WrappedAsio::get_buffer_size( + long *min_size, + long *max_size, + long *preferred_size, + long *granularity) +{ + const AsioError result = this->pReal->get_buffer_size(min_size, max_size, preferred_size, granularity); + if (result != ASE_OK) { + log_warning("audio::wrappedasio", "get_buffer_size failed, err={}", static_cast(result)); + return result; + } + + // include millisecond equivalents alongside the frame counts for readability + AsioSampleRate sample_rate = 0.0; + this->pReal->get_sample_rate(&sample_rate); + const long min_frames = min_size ? *min_size : -1; + const long max_frames = max_size ? *max_size : -1; + const long preferred_frames = preferred_size ? *preferred_size : -1; + log_info( + "audio::wrappedasio", + "get_buffer_size -> min={} frames ({:.2f} ms), max={} frames ({:.2f} ms), " + "preferred={} frames ({:.2f} ms), granularity={}", + min_frames, + frames_to_ms(min_frames, sample_rate), + max_frames, + frames_to_ms(max_frames, sample_rate), + preferred_frames, + frames_to_ms(preferred_frames, sample_rate), + granularity ? *granularity : -1); + + return result; +} + +AsioError __thiscall WrappedAsio::can_sample_rate(AsioSampleRate sample_rate) { + const AsioError result = this->pReal->can_sample_rate(sample_rate); + if (result == ASE_OK) { + log_misc("audio::wrappedasio", "can_sample_rate({} Hz) -> supported", sample_rate); + } else { + log_misc( + "audio::wrappedasio", + "can_sample_rate({} Hz) -> not supported, err={}", + sample_rate, + static_cast(result)); + } + + return result; +} + +AsioError __thiscall WrappedAsio::get_sample_rate(AsioSampleRate *sample_rate) { + const AsioError result = this->pReal->get_sample_rate(sample_rate); + if (result == ASE_OK) { + log_misc("audio::wrappedasio", "get_sample_rate -> {} Hz", sample_rate ? *sample_rate : 0.0); + } else { + log_warning("audio::wrappedasio", "get_sample_rate failed, err={}", static_cast(result)); + } + + return result; +} + +AsioError __thiscall WrappedAsio::set_sample_rate(AsioSampleRate sample_rate) { + const AsioError result = this->pReal->set_sample_rate(sample_rate); + if (result == ASE_OK) { + log_info("audio::wrappedasio", "set_sample_rate({} Hz) succeeded", sample_rate); + } else { + log_warning( + "audio::wrappedasio", + "set_sample_rate({} Hz) failed, err={}", + sample_rate, + static_cast(result)); + } + + return result; +} + +AsioError __thiscall WrappedAsio::get_clock_sources(ASIOClockSource *clocks, long *num_sources) { + return this->pReal->get_clock_sources(clocks, num_sources); +} + +AsioError __thiscall WrappedAsio::set_clock_source(long reference) { + return this->pReal->set_clock_source(reference); +} + +AsioError __thiscall WrappedAsio::get_sample_position(ASIOSamples *s_pos, ASIOTimeStamp *t_stamp) { + return this->pReal->get_sample_position(s_pos, t_stamp); +} + +AsioError __thiscall WrappedAsio::get_channel_info(AsioChannelInfo *info) { + // forced two-channel: the game probes all output channels it thinks exist, but the + // device only has the real front pair. fabricate a plausible entry for the channels + // beyond the device without touching the real driver - they are discarded in + // create_buffers anyway + long real_in = 0, real_out = 0; + if (force_two_channels() + && info != nullptr + && info->is_input == AsioFalse + && this->pReal->get_channels(&real_in, &real_out) == ASE_OK + && info->channel >= real_out) + { + const long channel = info->channel; + + // report the real device's output sample format rather than a fixed type: when + // stereo downmix is active the game writes these dummy channels in this format and + // we raw-copy the selected pair onto the device's front channels, so the formats + // must match or the copy produces static. the guard above already proved the device + // has output channels, so query channel 0's format directly (avoiding a redundant + // get_channels) and fall back to Int32LSB if that query fails + AsioChannelInfo real_ci {}; + real_ci.channel = 0; + real_ci.is_input = AsioFalse; + AsioSampleType fake_type = ASIOSTInt32LSB; + if (this->pReal->get_channel_info(&real_ci) == ASE_OK) { + fake_type = real_ci.type; + } + + info->is_active = AsioTrue; + info->channel_group = 0; + info->type = fake_type; + snprintf(info->name, sizeof(info->name), "Fake ASIO OUT %ld", channel); + log_info( + "audio::wrappedasio", + "get_channel_info(channel={}, dir=output) -> fake channel, type={} ({})", + channel, + asio_sample_type_name(info->type), + static_cast(info->type)); + + return ASE_OK; + } + + const AsioError result = this->pReal->get_channel_info(info); + if (result == ASE_OK && info != nullptr) { + log_info( + "audio::wrappedasio", + "get_channel_info(channel={}, dir={}) -> active={}, group={}, type={} ({}), name='{}'", + info->channel, + info->is_input == AsioTrue ? "input" : "output", + info->is_active == AsioTrue, + info->channel_group, + asio_sample_type_name(info->type), + static_cast(info->type), + info->name); + } else if (result != ASE_OK) { + log_warning("audio::wrappedasio", "get_channel_info failed, err={}", static_cast(result)); + } + + return result; +} + +AsioCallbacks *WrappedAsio::install_proxy_callbacks(AsioCallbacks *game_callbacks) { + const float gain = hooks::audio::VOLUME_BOOST; + + // start from a clean slate; a previous buffer set may have left state behind + this->volume_channels.clear(); + this->volume_active = false; + this->downmix_active = false; + + const bool want_volume = (gain != 1.0f); + + // front is the device's own pair, so selecting it (or None) means no copy is needed + const bool want_downmix = (STEREO_DOWNMIX != StereoDownmix::None + && STEREO_DOWNMIX != StereoDownmix::Front); + + // no post-processing configured (or no callbacks to wrap): pass the game's callbacks + // straight through and do zero realtime work, exactly as before + if ((!want_volume && !want_downmix) || game_callbacks == nullptr) { + return game_callbacks; + } + + if (want_volume) { + this->volume_active = true; + this->volume_gain = gain; + } + + // the trampolines reach the game's buffer_switch through this copy, regardless of which + // effect is active + this->game_callbacks = *game_callbacks; + + // wrap only the buffer-switch callbacks, where the audio data lives and we rework it. + // the other two carry no data we touch, so forward the game's own pointers unchanged - + // the driver expects them non-null and the game already owns their context + this->proxy_callbacks = {}; + this->proxy_callbacks.buffer_switch = &WrappedAsio::proxy_buffer_switch; + this->proxy_callbacks.sample_rate_did_change = game_callbacks->sample_rate_did_change; + this->proxy_callbacks.asio_message = game_callbacks->asio_message; + this->proxy_callbacks.buffer_switch_time_info = + game_callbacks->buffer_switch_time_info ? &WrappedAsio::proxy_buffer_switch_time_info : nullptr; + + return &this->proxy_callbacks; +} + +AsioSampleType WrappedAsio::device_output_sample_type() { + long real_in = 0, real_out = 0; + if (this->pReal->get_channels(&real_in, &real_out) != ASE_OK || real_out <= 0) { + return ASIOSTLastEntry; + } + + AsioChannelInfo ci {}; + ci.channel = 0; + ci.is_input = AsioFalse; + if (this->pReal->get_channel_info(&ci) != ASE_OK) { + return ASIOSTLastEntry; + } + return ci.type; +} + +void WrappedAsio::record_volume_output_channel(const AsioBufferInfo &info) { + if (info.is_input != AsioFalse) { + return; + } + + // ask the real driver for this channel's sample format so the realtime path knows how + // to scale it; fall back to a sentinel that apply_gain_planar leaves untouched + AsioChannelInfo ci {}; + ci.channel = info.channel_num; + ci.is_input = AsioFalse; + AsioSampleType type = ASIOSTLastEntry; + if (this->pReal->get_channel_info(&ci) == ASE_OK) { + type = ci.type; + } + + VolumeOutputChannel ch; + ch.buffers[0] = info.buffers[0]; + ch.buffers[1] = info.buffers[1]; + ch.type = type; + this->volume_channels.push_back(ch); +} + +void WrappedAsio::record_downmix_channels( + AsioBufferInfo *buffer_infos, long num_channels, long buffer_size) +{ + // map the selected pair to source channel indices (0-indexed, standard 7.1 layout); + // None and Front need no copy + long src_left = 0, src_right = 0; + switch (STEREO_DOWNMIX) { + case StereoDownmix::Center: src_left = 2; src_right = 2; break; + case StereoDownmix::Rear: src_left = 4; src_right = 5; break; + case StereoDownmix::Side: src_left = 6; src_right = 7; break; + default: return; + } + + // find the double-buffer pair for a given output channel among those the game created, + // or nullptr if the device does not expose it + auto find_output = [&](long channel) -> void ** { + for (long i = 0; i < num_channels; i++) { + AsioBufferInfo &bi = buffer_infos[i]; + if (bi.is_input == AsioFalse && bi.channel_num == channel) { + return bi.buffers; + } + } + return nullptr; + }; + + // destinations are device channels 0/1; sources are the selected pair + void **dst0 = find_output(0); + void **dst1 = find_output(1); + void **src_l = find_output(src_left); + void **src_r = find_output(src_right); + if (dst0 == nullptr || dst1 == nullptr || src_l == nullptr || src_r == nullptr) { + log_warning( + "audio::wrappedasio", + "stereo downmix disabled: device is missing the front pair or source channels " + "{}/{} (game created {} channel(s))", + src_left, + src_right, + num_channels); + return; + } + + // all device output channels share one sample format; query it to size the copy + const AsioSampleType type = this->device_output_sample_type(); + const int sample_bytes = asio_sample_bytes(type); + if (sample_bytes <= 0) { + log_warning( + "audio::wrappedasio", + "stereo downmix disabled: unsupported sample format {} ({})", + asio_sample_type_name(type), + static_cast(type)); + return; + } + + this->downmix_copies[0] = {{dst0[0], dst0[1]}, {src_l[0], src_l[1]}}; + this->downmix_copies[1] = {{dst1[0], dst1[1]}, {src_r[0], src_r[1]}}; + this->downmix_bytes = static_cast(buffer_size) * sample_bytes; + this->downmix_active = true; + + log_info( + "audio::wrappedasio", + "stereo downmix active: pair={} (src {}/{} -> device 0/1), {} frames, {} byte(s)/sample", + stereo_downmix_name(STEREO_DOWNMIX), + src_left, + src_right, + buffer_size, + sample_bytes); +} + +void WrappedAsio::publish_post_process(long buffer_size) { + if (!this->volume_active && !this->downmix_active) { + return; + } + + // everything the realtime thread reads is now in place; make ourselves reachable + this->volume_buffer_size = buffer_size; + WrappedAsio::active_instance.store(this, std::memory_order_release); + + if (this->volume_active) { + log_info( + "audio::wrappedasio", + "volume boost active: gain={}, scaling {} output channel(s)", + this->volume_gain, + this->volume_channels.size()); + } +} + +void WrappedAsio::detach_post_process() { + // stop our realtime trampolines from reaching this wrapper, but only if we are the + // currently published instance + WrappedAsio *expected = this; + WrappedAsio::active_instance.compare_exchange_strong(expected, nullptr); +} + +void WrappedAsio::quiesce_for_reuse() { + // a previous abandoned probing cycle may have left a running stream and live buffers on + // the real driver without calling stop/dispose; tear that down now (without destroying + // the driver) so the host's next create_buffers starts clean. stop() guarantees no + // further buffer_switch, and dispose_buffers detaches our trampolines + if (this->started) { + log_info( + "audio::wrappedasio", + "reuse: stopping leftover stream on '{}' before handing the driver back", + this->driver_name); + this->stop(); + } + if (this->buffers_created) { + log_info( + "audio::wrappedasio", + "reuse: disposing leftover buffers on '{}' before handing the driver back", + this->driver_name); + this->dispose_buffers(); + } +} + +void WrappedAsio::apply_output_volume(long double_buffer_index) { + if (double_buffer_index != 0 && double_buffer_index != 1) { + return; + } + const float gain = this->volume_gain; + const long frames = this->volume_buffer_size; + for (const VolumeOutputChannel &ch : this->volume_channels) { + apply_gain_planar(ch.buffers[double_buffer_index], frames, ch.type, gain); + } +} + +void WrappedAsio::apply_downmix(long double_buffer_index) { + if (!this->downmix_active) { + return; + } + if (double_buffer_index != 0 && double_buffer_index != 1) { + return; + } + + // raw planar copy of the selected source channels onto device channels 0/1; for the + // center selection both copies share one source. skip self-copies (front) + for (const DownmixCopy © : this->downmix_copies) { + void *dst = copy.dst[double_buffer_index]; + const void *src = copy.src[double_buffer_index]; + if (dst != nullptr && src != nullptr && dst != src) { + std::memcpy(dst, src, this->downmix_bytes); + } + } +} + +void __cdecl WrappedAsio::proxy_buffer_switch(long double_buffer_index, AsioBool direct_process) { + WrappedAsio *self = WrappedAsio::active_instance.load(std::memory_order_acquire); + if (self == nullptr) { + return; + } + + // let the game write its samples into the driver buffers first, then rework them before + // the driver plays this half on the next switch: downmix first (arrange channels 0/1), + // then scale the device outputs by the volume boost + if (self->game_callbacks.buffer_switch != nullptr) { + self->game_callbacks.buffer_switch(double_buffer_index, direct_process); + } + self->apply_downmix(double_buffer_index); + self->apply_output_volume(double_buffer_index); +} + +AsioTime * __cdecl WrappedAsio::proxy_buffer_switch_time_info( + AsioTime *params, long double_buffer_index, AsioBool direct_process) +{ + WrappedAsio *self = WrappedAsio::active_instance.load(std::memory_order_acquire); + if (self == nullptr) { + return params; + } + + AsioTime *ret = params; + if (self->game_callbacks.buffer_switch_time_info != nullptr) { + ret = self->game_callbacks.buffer_switch_time_info( + params, double_buffer_index, direct_process); + } else if (self->game_callbacks.buffer_switch != nullptr) { + self->game_callbacks.buffer_switch(double_buffer_index, direct_process); + } + self->apply_downmix(double_buffer_index); + self->apply_output_volume(double_buffer_index); + return ret; +} + +AsioError __thiscall WrappedAsio::create_buffers( + AsioBufferInfo *buffer_infos, + long num_channels, + long buffer_size, + AsioCallbacks *callbacks) +{ + // swap in our buffer-switch trampolines if any post-processing is configured, so the + // real driver calls us and we rework its output after the game fills it (no-op otherwise) + AsioCallbacks *effective = this->install_proxy_callbacks(callbacks); + + if (force_two_channels()) { + return this->create_buffers_front_pair(buffer_infos, num_channels, buffer_size, effective); + } + + const AsioError result = this->pReal->create_buffers(buffer_infos, num_channels, buffer_size, effective); + if (result == ASE_OK) { + log_info( + "audio::wrappedasio", + "create_buffers(channels={}, size={} frames) succeeded", + num_channels, + buffer_size); + + // capture the post-process state now the buffers exist, then publish ourselves to + // the realtime thread once everything is in place. downmix is not recorded here: any + // active stereo extraction forces the front-pair path above, so this path only ever + // runs the volume boost + if (this->volume_active) { + for (long i = 0; i < num_channels; i++) { + this->record_volume_output_channel(buffer_infos[i]); + } + } + this->publish_post_process(buffer_size); + this->buffers_created = true; + } else { + log_warning( + "audio::wrappedasio", + "create_buffers(channels={}, size={} frames) failed, err={}", + num_channels, + buffer_size, + static_cast(result)); + } + + return result; +} + +AsioError WrappedAsio::create_buffers_front_pair( + AsioBufferInfo *buffer_infos, + long num_channels, + long buffer_size, + AsioCallbacks *callbacks) +{ + // front-pair extraction (forced two-channel ASIO): the game asks for more output + // channels than the real device has (e.g. 8 vs 2). forward only the channels the + // device actually provides (channel 0/1 = front L/R) and hand the game throwaway + // buffers for the rest, so its front mix lands on the device and the surround + // channels are discarded. the game writes directly into the driver/dummy buffers + // from its own bufferSwitch; the realtime work we do is the optional volume boost + // (scaling the forwarded device channels) and, if a non-front stereo downmix is + // selected, copying that pair from its dummy buffers onto the device's front pair + long real_in = 0, real_out = 0; + const AsioError ch_result = this->pReal->get_channels(&real_in, &real_out); + if (ch_result != ASE_OK) { + log_warning( + "audio::wrappedasio", + "create_buffers: get_channels failed, err={}", + static_cast(ch_result)); + return ch_result; + } + + // partition the requested channels: those the device can serve are forwarded, the rest + // are discarded. record source indices for both so we can patch the game's array after + std::vector forwarded; + std::vector forwarded_src; + std::vector discarded_src; + forwarded.reserve(num_channels); + forwarded_src.reserve(num_channels); + discarded_src.reserve(num_channels); + for (long i = 0; i < num_channels; i++) { + const AsioBufferInfo &bi = buffer_infos[i]; + const long limit = (bi.is_input == AsioTrue) ? real_in : real_out; + if (bi.channel_num < limit) { + forwarded.push_back(bi); + forwarded_src.push_back(i); + } else { + discarded_src.push_back(i); + } + } + + const AsioError result = this->pReal->create_buffers( + forwarded.data(), static_cast(forwarded.size()), buffer_size, callbacks); + if (result != ASE_OK) { + log_warning( + "audio::wrappedasio", + "create_buffers(forwarded={} of {}, size={} frames) failed, err={}", + forwarded.size(), + num_channels, + buffer_size, + static_cast(result)); + return result; + } + + // copy the real driver buffer pointers back into the game's array + for (size_t k = 0; k < forwarded.size(); k++) { + AsioBufferInfo &dst = buffer_infos[forwarded_src[k]]; + dst.buffers[0] = forwarded[k].buffers[0]; + dst.buffers[1] = forwarded[k].buffers[1]; + + // only the forwarded channels reach the device, so those are the ones the volume + // boost scales (the discarded channels go to throwaway buffers below) + if (this->volume_active) { + this->record_volume_output_channel(dst); + } + } + + // hand throwaway double buffers to the discarded channels. sized generously at + // 8 bytes/sample (covers every ASIO sample type) so the game can never overrun them + // regardless of the negotiated format + this->dummy_buffers.clear(); + this->dummy_buffers.reserve(discarded_src.size() * 2); + const size_t dummy_bytes = static_cast(buffer_size) * 8; + for (const long i : discarded_src) { + for (void *&buffer : buffer_infos[i].buffers) { + auto buf = std::make_unique(dummy_bytes); + std::memset(buf.get(), 0, dummy_bytes); + buffer = buf.get(); + this->dummy_buffers.push_back(std::move(buf)); + } + } + + // record the downmix source/destination buffers now the array is fully patched: the + // selected pair (e.g. rear) lives in the dummy buffers above, the device front pair in + // the forwarded driver buffers, so the realtime copy lands the chosen pair on the + // device's 2.0 output. then publish ourselves to the realtime thread + this->record_downmix_channels(buffer_infos, num_channels, buffer_size); + this->publish_post_process(buffer_size); + this->buffers_created = true; + + log_info( + "audio::wrappedasio", + "create_buffers: front-pair extraction - forwarded {} channel(s) to device, " + "discarded {} (requested {}, size={} frames)", + forwarded.size(), + discarded_src.size(), + num_channels, + buffer_size); + + return ASE_OK; +} + +AsioError __thiscall WrappedAsio::dispose_buffers() { + // stop our realtime trampolines from touching buffers the driver is about to free + this->detach_post_process(); + + const AsioError result = this->pReal->dispose_buffers(); + this->dummy_buffers.clear(); + this->volume_channels.clear(); + this->volume_active = false; + this->downmix_active = false; + this->buffers_created = false; + return result; +} + +AsioError __thiscall WrappedAsio::control_panel() { + return this->pReal->control_panel(); +} + +AsioError __thiscall WrappedAsio::future(long selector, void *opt) { + return this->pReal->future(selector, opt); +} + +AsioError __thiscall WrappedAsio::output_ready() { + return this->pReal->output_ready(); +} +#pragma endregion + +namespace hooks::audio::asio { + + IUnknown *wrap(REFCLSID clsid, void *real) { + log_info("audio::wrappedasio", "wrapping ASIO driver interface, clsid={}", guid2s(clsid)); + + auto *wrapper = new WrappedAsio( + reinterpret_cast(real), clsid, registered_asio_name(clsid)); + + // pin the wrapper (and the real driver it owns) for the process lifetime; later + // CoCreate calls reuse this same instance via wrap_existing + wrapper->AddRef(); + store_wrapper(clsid, wrapper); + + return static_cast(wrapper); + } + + IUnknown *wrap_existing(REFCLSID clsid) { + WrappedAsio *wrapper = find_wrapper(clsid); + if (wrapper == nullptr) { + return nullptr; + } + + log_misc( + "audio::wrappedasio", + "reusing cached ASIO driver instance, clsid={}", + guid2s(clsid)); + + wrapper->quiesce_for_reuse(); + + // hand the host another reference to the one instance we keep alive + wrapper->AddRef(); + return static_cast(wrapper); + } + + void release_all_wrappers() { + // collect the pinned wrappers under the lock, then drop our references outside it so + // a final Release (which can run the driver's own teardown) never happens while the + // lock is held + std::vector> wrappers; + { + std::lock_guard lock(g_wrappers_mutex); + wrappers = g_wrappers; + g_wrappers.clear(); + } + + for (auto &entry : wrappers) { + // drop only the pin wrap() took. if the host has already released its own + // references (the normal case at shutdown) this destroys the wrapper and releases + // the real driver; if the host is still using it, the refcount stays above zero + // and we leave its live stream/buffers untouched + log_misc( + "audio::wrappedasio", + "releasing cached ASIO driver instance, clsid={}", + guid2s(entry.first)); + entry.second->Release(); + } + } +} + diff --git a/src/spice2x/hooks/audio/asio_proxy.h b/src/spice2x/hooks/audio/asio_proxy.h index 2b2c229..9dd7eea 100644 --- a/src/spice2x/hooks/audio/asio_proxy.h +++ b/src/spice2x/hooks/audio/asio_proxy.h @@ -1,240 +1,240 @@ -#pragma once - -#include -#include -#include -#include - -#include - -#include "external/asio/asio.h" -#include "external/asio/iasiodrv.h" - -namespace hooks::audio::asio { - - // returns true if a CoCreateInstance call is instantiating a registered ASIO driver. - // ASIO hosts pass the driver CLSID as both class id and interface id; we also validate - // it against the system's registered ASIO drivers to avoid false positives - bool is_asio_creation(REFCLSID rclsid, REFIID riid); - - // wrap a real ASIO driver instance, taking ownership of the supplied reference, and - // return a proxy that forwards every call to it. also records it as the cached - // instance for its CLSID so later CoCreate calls can reuse it (see wrap_existing) - IUnknown *wrap(REFCLSID clsid, void *real); - - // if a cached wrapper already exists for this CLSID, return it (with an added - // reference); otherwise nullptr to signal the caller to create the real driver and - // wrap it. lets the host reuse one driver instance instead of re-instantiating it - IUnknown *wrap_existing(REFCLSID clsid); - - // drop the process-lifetime references taken by wrap() so cached drivers can be released - // at shutdown. only relinquishes our pin, so a real driver is torn down once the host - // has released its own references too. call from a controlled shutdown point, never from - // a static destructor (the driver DLL may already be unloaded) - void release_all_wrappers(); -} - -// transparent proxy around a real ASIO driver; a single place to intercept ASIO traffic -struct WrappedAsio final : IAsio { - WrappedAsio(IAsio *real, REFCLSID clsid, std::string name) - : pReal(real), clsid(clsid), driver_name(std::move(name)) { - } - - WrappedAsio(const WrappedAsio &) = delete; - WrappedAsio &operator=(const WrappedAsio &) = delete; - - virtual ~WrappedAsio(); - - // selects which source channel pair of a multichannel ASIO output reaches the device's - // 2.0 front pair. when not None, the proxy presents the game's expected multichannel - // layout to the host so it proceeds to create_buffers, then opens only a two-channel - // stream on the real device and routes the selected pair onto it (see create_buffers). - // Front is the plain "force two channel" case (forward the device's own front pair); - // the others copy a different pair onto 0/1. assumes a standard 7.1 layout (0-indexed). - // set once at boot, before any wrapper exists, so it needs no synchronization - enum class StereoDownmix { - None, // feature disabled - full multichannel passthrough - Front, // channels 0/1 - the device front pair is forwarded as-is (no copy) - Center, // channel 2 duplicated to both 0 and 1 - Rear, // channels 4/5 -> 0/1 - Side, // channels 6/7 -> 0/1 - }; - static StereoDownmix STEREO_DOWNMIX; - - // true when a stereo extraction is configured, i.e. the real device should open a 2.0 - // stream and only the selected pair should reach it. the former standalone - // FORCE_TWO_CHANNELS flag is now just the Front case of this - static bool force_two_channels() { - return STEREO_DOWNMIX != StereoDownmix::None; - } - - // some games hardcode a multichannel ASIO output and bail before create_buffers if - // get_channels reports fewer, so we report at least this many output channels when a - // stereo extraction is active - static constexpr long FORCED_OUTPUT_CHANNELS = 8; - - // maps an option string ("front", "center", "rear", "side") to a StereoDownmix value, - // returning None for anything unrecognized - static StereoDownmix name_to_stereo_downmix(const char *name); - -#pragma region IUnknown - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) override; - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; -#pragma endregion - -#pragma region IAsio - AsioBool __thiscall init(void *sys_handle) override; - void __thiscall get_driver_name(char *name) override; - long __thiscall get_driver_version() override; - void __thiscall get_error_message(char *string) override; - AsioError __thiscall start() override; - AsioError __thiscall stop() override; - AsioError __thiscall get_channels(long *num_input_channels, long *num_output_channels) override; - AsioError __thiscall get_latencies(long *input_latency, long *output_latency) override; - AsioError __thiscall get_buffer_size( - long *min_size, - long *max_size, - long *preferred_size, - long *granularity) override; - AsioError __thiscall can_sample_rate(AsioSampleRate sample_rate) override; - AsioError __thiscall get_sample_rate(AsioSampleRate *sample_rate) override; - AsioError __thiscall set_sample_rate(AsioSampleRate sample_rate) override; - AsioError __thiscall get_clock_sources(ASIOClockSource *clocks, long *num_sources) override; - AsioError __thiscall set_clock_source(long reference) override; - AsioError __thiscall get_sample_position(ASIOSamples *s_pos, ASIOTimeStamp *t_stamp) override; - AsioError __thiscall get_channel_info(AsioChannelInfo *info) override; - AsioError __thiscall create_buffers( - AsioBufferInfo *buffer_infos, - long num_channels, - long buffer_size, - AsioCallbacks *callbacks) override; - AsioError __thiscall dispose_buffers() override; - AsioError __thiscall control_panel() override; - AsioError __thiscall future(long selector, void *opt) override; - AsioError __thiscall output_ready() override; -#pragma endregion - - // quiesces any leftover stream/buffer state before the cached wrapper is handed back - // for reuse, without destroying the real driver (see wrap_existing) - void quiesce_for_reuse(); - -private: - // create_buffers implementation used when a stereo extraction is active: forwards only - // the channels the real device has and hands the game throwaway buffers for the rest - AsioError create_buffers_front_pair( - AsioBufferInfo *buffer_infos, - long num_channels, - long buffer_size, - AsioCallbacks *callbacks); - - // if any post-processing effect (volume boost or stereo downmix) is active, saves the - // game's callbacks and returns a proxy callback set (our buffer-switch trampolines) to - // hand the real driver instead, so we can rework its output buffers after the game - // fills them. otherwise returns the game's callbacks unchanged. called at create_buffers - // time, before the stream starts - AsioCallbacks *install_proxy_callbacks(AsioCallbacks *game_callbacks); - - // records a device output channel whose buffers we scale by the volume boost. queries - // the real driver for the channel's sample format. called at create_buffers time - void record_volume_output_channel(const AsioBufferInfo &info); - - // the real device's output sample format, queried from its first output channel. all - // output channels of a device share one format, so this characterizes them all. returns - // ASIOSTLastEntry if the device has no output channels or the query fails - AsioSampleType device_output_sample_type(); - - // locates the destination pair (device channels 0/1) and the configured source channels - // in the game's buffer set so the realtime path can copy the selected pair onto 0/1. - // a no-op unless STEREO_DOWNMIX selects a non-front pair. called at create_buffers time - void record_downmix_channels(AsioBufferInfo *buffer_infos, long num_channels, long buffer_size); - - // publishes the captured post-process state to the realtime thread once the buffers - // exist, making our trampolines start reworking output. called at the end of either - // create_buffers path - void publish_post_process(long buffer_size); - - // detaches this instance from the realtime trampolines so they stop touching its - // buffers. called from dispose_buffers and the destructor - void detach_post_process(); - - // multiplies every recorded output channel's buffer for the given double-buffer index - // by the volume boost. runs on the driver's realtime thread from our buffer switch - void apply_output_volume(long double_buffer_index); - - // copies the configured source channel pair onto device channels 0/1 for the given - // double-buffer index. runs on the driver's realtime thread from our buffer switch - void apply_downmix(long double_buffer_index); - - // realtime-thread trampolines for the buffer-switch callbacks, handed to the real - // driver in place of the game's; ASIO callbacks carry no user data, so they reach the - // active wrapper through active_instance, call the game's original, then rework output. - // the other two callbacks (sample_rate_did_change, asio_message) are forwarded as the - // game's own pointers, so they need no trampoline - static void __cdecl proxy_buffer_switch(long double_buffer_index, AsioBool direct_process); - static AsioTime * __cdecl proxy_buffer_switch_time_info( - AsioTime *params, long double_buffer_index, AsioBool direct_process); - - // the single wrapper whose proxy callbacks are installed (ASIO is single-instance with - // one running stream); read by the static trampolines to reach the right wrapper - static std::atomic active_instance; - - IAsio *const pReal; - const CLSID clsid; - - // registry name of the driver (not get_driver_name), used in our logs as a single - // unambiguous name; constant for our lifetime - std::string driver_name; - - // the real driver is initialized exactly once; repeat init() calls are a no-op success - bool initialized = false; - - // whether the real driver currently has a buffer set / running stream. used to quiesce - // leftover state when the cached wrapper is reused (see quiesce_for_reuse) - bool buffers_created = false; - bool started = false; - - // our own reference count; we hold one reference on pReal and release it when this - // drops to zero - std::atomic ref_count {1}; - - // throwaway double buffers handed to the channels we discard when a stereo extraction - // is active (see create_buffers). owned for the lifetime of the buffer set and freed - // in dispose_buffers; only read by the game from its own bufferSwitch, never by us - std::vector> dummy_buffers; - - // one device output channel scaled by the volume boost in our buffer switch - struct VolumeOutputChannel { - void *buffers[2]; - AsioSampleType type; - }; - - // the game's original callbacks (captured when we install our proxy set) and the proxy - // set we hand the real driver; the realtime trampolines reach the game's buffer_switch - // through game_callbacks regardless of which effect is active - AsioCallbacks game_callbacks {}; - AsioCallbacks proxy_callbacks {}; - - // volume boost state, captured at create_buffers time and published to the realtime - // thread via active_instance once fully built; untouched while the stream runs. - // volume_active gates whether the realtime path scales any buffers - bool volume_active = false; - float volume_gain = 1.0f; - long volume_buffer_size = 0; - std::vector volume_channels; - - // one device channel (0 or 1) fed by a source channel during stereo downmix; both - // buffer pointers are indexed by the ASIO double-buffer index, the same as the channels - struct DownmixCopy { - void *dst[2]; - void *src[2]; - }; - - // stereo downmix state, captured at create_buffers time and published alongside the - // volume state; untouched while the stream runs. downmix_active gates whether the - // realtime path copies the selected source pair onto device channels 0/1. copies[0] - // feeds device channel 0, copies[1] feeds device channel 1 - bool downmix_active = false; - DownmixCopy downmix_copies[2] {}; - size_t downmix_bytes = 0; -}; +#pragma once + +#include +#include +#include +#include + +#include + +#include "external/asio/asio.h" +#include "external/asio/iasiodrv.h" + +namespace hooks::audio::asio { + + // returns true if a CoCreateInstance call is instantiating a registered ASIO driver. + // ASIO hosts pass the driver CLSID as both class id and interface id; we also validate + // it against the system's registered ASIO drivers to avoid false positives + bool is_asio_creation(REFCLSID rclsid, REFIID riid); + + // wrap a real ASIO driver instance, taking ownership of the supplied reference, and + // return a proxy that forwards every call to it. also records it as the cached + // instance for its CLSID so later CoCreate calls can reuse it (see wrap_existing) + IUnknown *wrap(REFCLSID clsid, void *real); + + // if a cached wrapper already exists for this CLSID, return it (with an added + // reference); otherwise nullptr to signal the caller to create the real driver and + // wrap it. lets the host reuse one driver instance instead of re-instantiating it + IUnknown *wrap_existing(REFCLSID clsid); + + // drop the process-lifetime references taken by wrap() so cached drivers can be released + // at shutdown. only relinquishes our pin, so a real driver is torn down once the host + // has released its own references too. call from a controlled shutdown point, never from + // a static destructor (the driver DLL may already be unloaded) + void release_all_wrappers(); +} + +// transparent proxy around a real ASIO driver; a single place to intercept ASIO traffic +struct WrappedAsio final : IAsio { + WrappedAsio(IAsio *real, REFCLSID clsid, std::string name) + : pReal(real), clsid(clsid), driver_name(std::move(name)) { + } + + WrappedAsio(const WrappedAsio &) = delete; + WrappedAsio &operator=(const WrappedAsio &) = delete; + + virtual ~WrappedAsio(); + + // selects which source channel pair of a multichannel ASIO output reaches the device's + // 2.0 front pair. when not None, the proxy presents the game's expected multichannel + // layout to the host so it proceeds to create_buffers, then opens only a two-channel + // stream on the real device and routes the selected pair onto it (see create_buffers). + // Front is the plain "force two channel" case (forward the device's own front pair); + // the others copy a different pair onto 0/1. assumes a standard 7.1 layout (0-indexed). + // set once at boot, before any wrapper exists, so it needs no synchronization + enum class StereoDownmix { + None, // feature disabled - full multichannel passthrough + Front, // channels 0/1 - the device front pair is forwarded as-is (no copy) + Center, // channel 2 duplicated to both 0 and 1 + Rear, // channels 4/5 -> 0/1 + Side, // channels 6/7 -> 0/1 + }; + static StereoDownmix STEREO_DOWNMIX; + + // true when a stereo extraction is configured, i.e. the real device should open a 2.0 + // stream and only the selected pair should reach it. the former standalone + // FORCE_TWO_CHANNELS flag is now just the Front case of this + static bool force_two_channels() { + return STEREO_DOWNMIX != StereoDownmix::None; + } + + // some games hardcode a multichannel ASIO output and bail before create_buffers if + // get_channels reports fewer, so we report at least this many output channels when a + // stereo extraction is active + static constexpr long FORCED_OUTPUT_CHANNELS = 8; + + // maps an option string ("front", "center", "rear", "side") to a StereoDownmix value, + // returning None for anything unrecognized + static StereoDownmix name_to_stereo_downmix(const char *name); + +#pragma region IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; +#pragma endregion + +#pragma region IAsio + AsioBool __thiscall init(void *sys_handle) override; + void __thiscall get_driver_name(char *name) override; + long __thiscall get_driver_version() override; + void __thiscall get_error_message(char *string) override; + AsioError __thiscall start() override; + AsioError __thiscall stop() override; + AsioError __thiscall get_channels(long *num_input_channels, long *num_output_channels) override; + AsioError __thiscall get_latencies(long *input_latency, long *output_latency) override; + AsioError __thiscall get_buffer_size( + long *min_size, + long *max_size, + long *preferred_size, + long *granularity) override; + AsioError __thiscall can_sample_rate(AsioSampleRate sample_rate) override; + AsioError __thiscall get_sample_rate(AsioSampleRate *sample_rate) override; + AsioError __thiscall set_sample_rate(AsioSampleRate sample_rate) override; + AsioError __thiscall get_clock_sources(ASIOClockSource *clocks, long *num_sources) override; + AsioError __thiscall set_clock_source(long reference) override; + AsioError __thiscall get_sample_position(ASIOSamples *s_pos, ASIOTimeStamp *t_stamp) override; + AsioError __thiscall get_channel_info(AsioChannelInfo *info) override; + AsioError __thiscall create_buffers( + AsioBufferInfo *buffer_infos, + long num_channels, + long buffer_size, + AsioCallbacks *callbacks) override; + AsioError __thiscall dispose_buffers() override; + AsioError __thiscall control_panel() override; + AsioError __thiscall future(long selector, void *opt) override; + AsioError __thiscall output_ready() override; +#pragma endregion + + // quiesces any leftover stream/buffer state before the cached wrapper is handed back + // for reuse, without destroying the real driver (see wrap_existing) + void quiesce_for_reuse(); + +private: + // create_buffers implementation used when a stereo extraction is active: forwards only + // the channels the real device has and hands the game throwaway buffers for the rest + AsioError create_buffers_front_pair( + AsioBufferInfo *buffer_infos, + long num_channels, + long buffer_size, + AsioCallbacks *callbacks); + + // if any post-processing effect (volume boost or stereo downmix) is active, saves the + // game's callbacks and returns a proxy callback set (our buffer-switch trampolines) to + // hand the real driver instead, so we can rework its output buffers after the game + // fills them. otherwise returns the game's callbacks unchanged. called at create_buffers + // time, before the stream starts + AsioCallbacks *install_proxy_callbacks(AsioCallbacks *game_callbacks); + + // records a device output channel whose buffers we scale by the volume boost. queries + // the real driver for the channel's sample format. called at create_buffers time + void record_volume_output_channel(const AsioBufferInfo &info); + + // the real device's output sample format, queried from its first output channel. all + // output channels of a device share one format, so this characterizes them all. returns + // ASIOSTLastEntry if the device has no output channels or the query fails + AsioSampleType device_output_sample_type(); + + // locates the destination pair (device channels 0/1) and the configured source channels + // in the game's buffer set so the realtime path can copy the selected pair onto 0/1. + // a no-op unless STEREO_DOWNMIX selects a non-front pair. called at create_buffers time + void record_downmix_channels(AsioBufferInfo *buffer_infos, long num_channels, long buffer_size); + + // publishes the captured post-process state to the realtime thread once the buffers + // exist, making our trampolines start reworking output. called at the end of either + // create_buffers path + void publish_post_process(long buffer_size); + + // detaches this instance from the realtime trampolines so they stop touching its + // buffers. called from dispose_buffers and the destructor + void detach_post_process(); + + // multiplies every recorded output channel's buffer for the given double-buffer index + // by the volume boost. runs on the driver's realtime thread from our buffer switch + void apply_output_volume(long double_buffer_index); + + // copies the configured source channel pair onto device channels 0/1 for the given + // double-buffer index. runs on the driver's realtime thread from our buffer switch + void apply_downmix(long double_buffer_index); + + // realtime-thread trampolines for the buffer-switch callbacks, handed to the real + // driver in place of the game's; ASIO callbacks carry no user data, so they reach the + // active wrapper through active_instance, call the game's original, then rework output. + // the other two callbacks (sample_rate_did_change, asio_message) are forwarded as the + // game's own pointers, so they need no trampoline + static void __cdecl proxy_buffer_switch(long double_buffer_index, AsioBool direct_process); + static AsioTime * __cdecl proxy_buffer_switch_time_info( + AsioTime *params, long double_buffer_index, AsioBool direct_process); + + // the single wrapper whose proxy callbacks are installed (ASIO is single-instance with + // one running stream); read by the static trampolines to reach the right wrapper + static std::atomic active_instance; + + IAsio *const pReal; + const CLSID clsid; + + // registry name of the driver (not get_driver_name), used in our logs as a single + // unambiguous name; constant for our lifetime + std::string driver_name; + + // the real driver is initialized exactly once; repeat init() calls are a no-op success + bool initialized = false; + + // whether the real driver currently has a buffer set / running stream. used to quiesce + // leftover state when the cached wrapper is reused (see quiesce_for_reuse) + bool buffers_created = false; + bool started = false; + + // our own reference count; we hold one reference on pReal and release it when this + // drops to zero + std::atomic ref_count {1}; + + // throwaway double buffers handed to the channels we discard when a stereo extraction + // is active (see create_buffers). owned for the lifetime of the buffer set and freed + // in dispose_buffers; only read by the game from its own bufferSwitch, never by us + std::vector> dummy_buffers; + + // one device output channel scaled by the volume boost in our buffer switch + struct VolumeOutputChannel { + void *buffers[2]; + AsioSampleType type; + }; + + // the game's original callbacks (captured when we install our proxy set) and the proxy + // set we hand the real driver; the realtime trampolines reach the game's buffer_switch + // through game_callbacks regardless of which effect is active + AsioCallbacks game_callbacks {}; + AsioCallbacks proxy_callbacks {}; + + // volume boost state, captured at create_buffers time and published to the realtime + // thread via active_instance once fully built; untouched while the stream runs. + // volume_active gates whether the realtime path scales any buffers + bool volume_active = false; + float volume_gain = 1.0f; + long volume_buffer_size = 0; + std::vector volume_channels; + + // one device channel (0 or 1) fed by a source channel during stereo downmix; both + // buffer pointers are indexed by the ASIO double-buffer index, the same as the channels + struct DownmixCopy { + void *dst[2]; + void *src[2]; + }; + + // stereo downmix state, captured at create_buffers time and published alongside the + // volume state; untouched while the stream runs. downmix_active gates whether the + // realtime path copies the selected source pair onto device channels 0/1. copies[0] + // feeds device channel 0, copies[1] feeds device channel 1 + bool downmix_active = false; + DownmixCopy downmix_copies[2] {}; + size_t downmix_bytes = 0; +}; diff --git a/src/spice2x/hooks/audio/backends/mmdevice/audio_endpoint_volume.h b/src/spice2x/hooks/audio/backends/mmdevice/audio_endpoint_volume.h index 205cf33..13e28ed 100644 --- a/src/spice2x/hooks/audio/backends/mmdevice/audio_endpoint_volume.h +++ b/src/spice2x/hooks/audio/backends/mmdevice/audio_endpoint_volume.h @@ -1,43 +1,43 @@ -#pragma once - -#include -#include - -struct WrappedIAudioEndpointVolume : IAudioEndpointVolume { - explicit WrappedIAudioEndpointVolume(IAudioEndpointVolume *orig) : pReal(orig) {} - - WrappedIAudioEndpointVolume(const WrappedIAudioEndpointVolume &) = delete; - WrappedIAudioEndpointVolume &operator=(const WrappedIAudioEndpointVolume &) = delete; - - virtual ~WrappedIAudioEndpointVolume() = default; - -#pragma region IUnknown - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObj) override; - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; -#pragma endregion - -#pragma region IAudioEndpointVolume - HRESULT STDMETHODCALLTYPE RegisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) override; - HRESULT STDMETHODCALLTYPE UnregisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) override; - HRESULT STDMETHODCALLTYPE GetChannelCount(uint32_t *pnChannelCount) override; - HRESULT STDMETHODCALLTYPE SetMasterVolumeLevel(float fLevelDB, LPCGUID pguidEventContext) override; - HRESULT STDMETHODCALLTYPE SetMasterVolumeLevelScalar(float fLevel, LPCGUID pguidEventContext) override; - HRESULT STDMETHODCALLTYPE GetMasterVolumeLevel(float *fLevelDB) override; - HRESULT STDMETHODCALLTYPE GetMasterVolumeLevelScalar(float *fLevel) override; - HRESULT STDMETHODCALLTYPE SetChannelVolumeLevel(uint32_t nChannel, float fLevelDB, LPCGUID pguidEventContext) override; - HRESULT STDMETHODCALLTYPE SetChannelVolumeLevelScalar(uint32_t nChannel, float fLevel, LPCGUID pguidEventContext) override; - HRESULT STDMETHODCALLTYPE GetChannelVolumeLevel(uint32_t nChannel, float *fLevelDB) override; - HRESULT STDMETHODCALLTYPE GetChannelVolumeLevelScalar(uint32_t nChannel, float *fLevel) override; - HRESULT STDMETHODCALLTYPE SetMute(WINBOOL bMute, LPCGUID pguidEventContext) override; - HRESULT STDMETHODCALLTYPE GetMute(WINBOOL *bMute) override; - HRESULT STDMETHODCALLTYPE GetVolumeStepInfo(uint32_t *pnStep, uint32_t *pnStepCount) override; - HRESULT STDMETHODCALLTYPE VolumeStepUp(LPCGUID pguidEventContext) override; - HRESULT STDMETHODCALLTYPE VolumeStepDown(LPCGUID pguidEventContext) override; - HRESULT STDMETHODCALLTYPE QueryHardwareSupport(DWORD *pdwHardwareSupportMask) override; - HRESULT STDMETHODCALLTYPE GetVolumeRange(float *pflVolumeMindB, float *pflVolumeMaxdB, float *pflVolumeIncrementdB) override; -#pragma endregion - -private: - IAudioEndpointVolume *const pReal; +#pragma once + +#include +#include + +struct WrappedIAudioEndpointVolume : IAudioEndpointVolume { + explicit WrappedIAudioEndpointVolume(IAudioEndpointVolume *orig) : pReal(orig) {} + + WrappedIAudioEndpointVolume(const WrappedIAudioEndpointVolume &) = delete; + WrappedIAudioEndpointVolume &operator=(const WrappedIAudioEndpointVolume &) = delete; + + virtual ~WrappedIAudioEndpointVolume() = default; + +#pragma region IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObj) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; +#pragma endregion + +#pragma region IAudioEndpointVolume + HRESULT STDMETHODCALLTYPE RegisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) override; + HRESULT STDMETHODCALLTYPE UnregisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) override; + HRESULT STDMETHODCALLTYPE GetChannelCount(uint32_t *pnChannelCount) override; + HRESULT STDMETHODCALLTYPE SetMasterVolumeLevel(float fLevelDB, LPCGUID pguidEventContext) override; + HRESULT STDMETHODCALLTYPE SetMasterVolumeLevelScalar(float fLevel, LPCGUID pguidEventContext) override; + HRESULT STDMETHODCALLTYPE GetMasterVolumeLevel(float *fLevelDB) override; + HRESULT STDMETHODCALLTYPE GetMasterVolumeLevelScalar(float *fLevel) override; + HRESULT STDMETHODCALLTYPE SetChannelVolumeLevel(uint32_t nChannel, float fLevelDB, LPCGUID pguidEventContext) override; + HRESULT STDMETHODCALLTYPE SetChannelVolumeLevelScalar(uint32_t nChannel, float fLevel, LPCGUID pguidEventContext) override; + HRESULT STDMETHODCALLTYPE GetChannelVolumeLevel(uint32_t nChannel, float *fLevelDB) override; + HRESULT STDMETHODCALLTYPE GetChannelVolumeLevelScalar(uint32_t nChannel, float *fLevel) override; + HRESULT STDMETHODCALLTYPE SetMute(WINBOOL bMute, LPCGUID pguidEventContext) override; + HRESULT STDMETHODCALLTYPE GetMute(WINBOOL *bMute) override; + HRESULT STDMETHODCALLTYPE GetVolumeStepInfo(uint32_t *pnStep, uint32_t *pnStepCount) override; + HRESULT STDMETHODCALLTYPE VolumeStepUp(LPCGUID pguidEventContext) override; + HRESULT STDMETHODCALLTYPE VolumeStepDown(LPCGUID pguidEventContext) override; + HRESULT STDMETHODCALLTYPE QueryHardwareSupport(DWORD *pdwHardwareSupportMask) override; + HRESULT STDMETHODCALLTYPE GetVolumeRange(float *pflVolumeMindB, float *pflVolumeMaxdB, float *pflVolumeIncrementdB) override; +#pragma endregion + +private: + IAudioEndpointVolume *const pReal; }; \ No newline at end of file diff --git a/src/spice2x/hooks/audio/backends/mmdevice/null_device.cpp b/src/spice2x/hooks/audio/backends/mmdevice/null_device.cpp index 424e612..0e5a2bd 100644 --- a/src/spice2x/hooks/audio/backends/mmdevice/null_device.cpp +++ b/src/spice2x/hooks/audio/backends/mmdevice/null_device.cpp @@ -1,185 +1,185 @@ -#include "null_device.h" - -#include -#include - -#include - -#include "hooks/audio/audio.h" -#include "hooks/audio/audio_private.h" -#include "hooks/audio/backends/wasapi/dummy_audio_client.h" -#include "util/logging.h" -#include "util/utils.h" - -#include "null_discard_backend.h" - -// friendly name reported by the synthetic device. must contain "Realtek" so the -// gitadora arena device search matches it. -static const wchar_t NULL_DEVICE_FRIENDLY_NAME[] = L"Realtek High Definition Audio"; - -// arbitrary identifier reported by the synthetic device. -static const wchar_t NULL_DEVICE_ID[] = L"{spice2x-null-render-device}"; - -// PKEY_Device_FriendlyName, hardcoded to avoid pulling in functiondiscoverykeys_devpkey.h -static const PROPERTYKEY PKEY_DEVICE_FRIENDLY_NAME_LOCAL = { - { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } }, - 14 -}; - -bool null_render_device_enabled() { - return hooks::audio::INJECT_FAKE_REALTEK_AUDIO; -} - -// duplicate a wide string into CoTaskMem so the caller can free it with -// CoTaskMemFree / PropVariantClear as the COM API contract requires. -static LPWSTR co_task_wcsdup(const wchar_t *src) { - const size_t bytes = (wcslen(src) + 1) * sizeof(wchar_t); - auto *dst = static_cast(CoTaskMemAlloc(bytes)); - if (dst != nullptr) { - memcpy(dst, src, bytes); - } - return dst; -} - -namespace { - - // minimal IPropertyStore that only answers PKEY_Device_FriendlyName. - struct NullPropertyStore : IPropertyStore { - std::atomic ref_cnt = 1; - - virtual ~NullPropertyStore() = default; - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObj) override { - if (ppvObj == nullptr) { - return E_POINTER; - } - if (riid == __uuidof(IUnknown) || riid == __uuidof(IPropertyStore)) { - this->AddRef(); - *ppvObj = this; - return S_OK; - } - *ppvObj = nullptr; - return E_NOINTERFACE; - } - ULONG STDMETHODCALLTYPE AddRef() override { - return ++this->ref_cnt; - } - ULONG STDMETHODCALLTYPE Release() override { - const ULONG refs = --this->ref_cnt; - if (refs == 0) { - delete this; - } - return refs; - } - HRESULT STDMETHODCALLTYPE GetCount(DWORD *cProps) override { - if (cProps == nullptr) { - return E_POINTER; - } - *cProps = 1; - return S_OK; - } - HRESULT STDMETHODCALLTYPE GetAt(DWORD iProp, PROPERTYKEY *pkey) override { - if (pkey == nullptr) { - return E_POINTER; - } - if (iProp != 0) { - return E_INVALIDARG; - } - *pkey = PKEY_DEVICE_FRIENDLY_NAME_LOCAL; - return S_OK; - } - HRESULT STDMETHODCALLTYPE GetValue(REFPROPERTYKEY key, PROPVARIANT *pv) override { - if (pv == nullptr) { - return E_POINTER; - } - PropVariantInit(pv); - if (key.fmtid == PKEY_DEVICE_FRIENDLY_NAME_LOCAL.fmtid - && key.pid == PKEY_DEVICE_FRIENDLY_NAME_LOCAL.pid) { - pv->pwszVal = co_task_wcsdup(NULL_DEVICE_FRIENDLY_NAME); - if (pv->pwszVal == nullptr) { - return E_OUTOFMEMORY; - } - pv->vt = VT_LPWSTR; - } - // unknown keys are returned as VT_EMPTY / S_OK - return S_OK; - } - HRESULT STDMETHODCALLTYPE SetValue(REFPROPERTYKEY, REFPROPVARIANT) override { - return STG_E_ACCESSDENIED; - } - HRESULT STDMETHODCALLTYPE Commit() override { - return S_OK; - } - }; -} - -#pragma region IUnknown -HRESULT STDMETHODCALLTYPE NullMMDevice::QueryInterface(REFIID riid, void **ppvObj) { - if (ppvObj == nullptr) { - return E_POINTER; - } - if (riid == __uuidof(IUnknown) || riid == __uuidof(IMMDevice)) { - this->AddRef(); - *ppvObj = this; - return S_OK; - } - *ppvObj = nullptr; - return E_NOINTERFACE; -} -ULONG STDMETHODCALLTYPE NullMMDevice::AddRef() { - return ++this->ref_cnt; -} -ULONG STDMETHODCALLTYPE NullMMDevice::Release() { - const ULONG refs = --this->ref_cnt; - if (refs == 0) { - delete this; - } - return refs; -} -#pragma endregion - -#pragma region IMMDevice -HRESULT STDMETHODCALLTYPE NullMMDevice::Activate( - REFIID iid, - DWORD, - PROPVARIANT *, - void **ppInterface) -{ - if (ppInterface == nullptr) { - return E_POINTER; - } - *ppInterface = nullptr; - - log_info("audio::null", "NullMMDevice::Activate {}", guid2s(iid)); - - if (iid == IID_IAudioClient) { - auto *client = static_cast(new DummyIAudioClient(new NullDiscardBackend())); - *ppInterface = client; - - return S_OK; - } - - return E_NOINTERFACE; -} -HRESULT STDMETHODCALLTYPE NullMMDevice::OpenPropertyStore(DWORD, IPropertyStore **ppProperties) { - if (ppProperties == nullptr) { - return E_POINTER; - } - *ppProperties = new NullPropertyStore(); - return S_OK; -} -HRESULT STDMETHODCALLTYPE NullMMDevice::GetId(LPWSTR *ppstrId) { - if (ppstrId == nullptr) { - return E_POINTER; - } - *ppstrId = co_task_wcsdup(NULL_DEVICE_ID); - return *ppstrId != nullptr ? S_OK : E_OUTOFMEMORY; -} -HRESULT STDMETHODCALLTYPE NullMMDevice::GetState(DWORD *pdwState) { - if (pdwState == nullptr) { - return E_POINTER; - } - *pdwState = DEVICE_STATE_ACTIVE; - return S_OK; -} -#pragma endregion +#include "null_device.h" + +#include +#include + +#include + +#include "hooks/audio/audio.h" +#include "hooks/audio/audio_private.h" +#include "hooks/audio/backends/wasapi/dummy_audio_client.h" +#include "util/logging.h" +#include "util/utils.h" + +#include "null_discard_backend.h" + +// friendly name reported by the synthetic device. must contain "Realtek" so the +// gitadora arena device search matches it. +static const wchar_t NULL_DEVICE_FRIENDLY_NAME[] = L"Realtek High Definition Audio"; + +// arbitrary identifier reported by the synthetic device. +static const wchar_t NULL_DEVICE_ID[] = L"{spice2x-null-render-device}"; + +// PKEY_Device_FriendlyName, hardcoded to avoid pulling in functiondiscoverykeys_devpkey.h +static const PROPERTYKEY PKEY_DEVICE_FRIENDLY_NAME_LOCAL = { + { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } }, + 14 +}; + +bool null_render_device_enabled() { + return hooks::audio::INJECT_FAKE_REALTEK_AUDIO; +} + +// duplicate a wide string into CoTaskMem so the caller can free it with +// CoTaskMemFree / PropVariantClear as the COM API contract requires. +static LPWSTR co_task_wcsdup(const wchar_t *src) { + const size_t bytes = (wcslen(src) + 1) * sizeof(wchar_t); + auto *dst = static_cast(CoTaskMemAlloc(bytes)); + if (dst != nullptr) { + memcpy(dst, src, bytes); + } + return dst; +} + +namespace { + + // minimal IPropertyStore that only answers PKEY_Device_FriendlyName. + struct NullPropertyStore : IPropertyStore { + std::atomic ref_cnt = 1; + + virtual ~NullPropertyStore() = default; + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObj) override { + if (ppvObj == nullptr) { + return E_POINTER; + } + if (riid == __uuidof(IUnknown) || riid == __uuidof(IPropertyStore)) { + this->AddRef(); + *ppvObj = this; + return S_OK; + } + *ppvObj = nullptr; + return E_NOINTERFACE; + } + ULONG STDMETHODCALLTYPE AddRef() override { + return ++this->ref_cnt; + } + ULONG STDMETHODCALLTYPE Release() override { + const ULONG refs = --this->ref_cnt; + if (refs == 0) { + delete this; + } + return refs; + } + HRESULT STDMETHODCALLTYPE GetCount(DWORD *cProps) override { + if (cProps == nullptr) { + return E_POINTER; + } + *cProps = 1; + return S_OK; + } + HRESULT STDMETHODCALLTYPE GetAt(DWORD iProp, PROPERTYKEY *pkey) override { + if (pkey == nullptr) { + return E_POINTER; + } + if (iProp != 0) { + return E_INVALIDARG; + } + *pkey = PKEY_DEVICE_FRIENDLY_NAME_LOCAL; + return S_OK; + } + HRESULT STDMETHODCALLTYPE GetValue(REFPROPERTYKEY key, PROPVARIANT *pv) override { + if (pv == nullptr) { + return E_POINTER; + } + PropVariantInit(pv); + if (key.fmtid == PKEY_DEVICE_FRIENDLY_NAME_LOCAL.fmtid + && key.pid == PKEY_DEVICE_FRIENDLY_NAME_LOCAL.pid) { + pv->pwszVal = co_task_wcsdup(NULL_DEVICE_FRIENDLY_NAME); + if (pv->pwszVal == nullptr) { + return E_OUTOFMEMORY; + } + pv->vt = VT_LPWSTR; + } + // unknown keys are returned as VT_EMPTY / S_OK + return S_OK; + } + HRESULT STDMETHODCALLTYPE SetValue(REFPROPERTYKEY, REFPROPVARIANT) override { + return STG_E_ACCESSDENIED; + } + HRESULT STDMETHODCALLTYPE Commit() override { + return S_OK; + } + }; +} + +#pragma region IUnknown +HRESULT STDMETHODCALLTYPE NullMMDevice::QueryInterface(REFIID riid, void **ppvObj) { + if (ppvObj == nullptr) { + return E_POINTER; + } + if (riid == __uuidof(IUnknown) || riid == __uuidof(IMMDevice)) { + this->AddRef(); + *ppvObj = this; + return S_OK; + } + *ppvObj = nullptr; + return E_NOINTERFACE; +} +ULONG STDMETHODCALLTYPE NullMMDevice::AddRef() { + return ++this->ref_cnt; +} +ULONG STDMETHODCALLTYPE NullMMDevice::Release() { + const ULONG refs = --this->ref_cnt; + if (refs == 0) { + delete this; + } + return refs; +} +#pragma endregion + +#pragma region IMMDevice +HRESULT STDMETHODCALLTYPE NullMMDevice::Activate( + REFIID iid, + DWORD, + PROPVARIANT *, + void **ppInterface) +{ + if (ppInterface == nullptr) { + return E_POINTER; + } + *ppInterface = nullptr; + + log_info("audio::null", "NullMMDevice::Activate {}", guid2s(iid)); + + if (iid == IID_IAudioClient) { + auto *client = static_cast(new DummyIAudioClient(new NullDiscardBackend())); + *ppInterface = client; + + return S_OK; + } + + return E_NOINTERFACE; +} +HRESULT STDMETHODCALLTYPE NullMMDevice::OpenPropertyStore(DWORD, IPropertyStore **ppProperties) { + if (ppProperties == nullptr) { + return E_POINTER; + } + *ppProperties = new NullPropertyStore(); + return S_OK; +} +HRESULT STDMETHODCALLTYPE NullMMDevice::GetId(LPWSTR *ppstrId) { + if (ppstrId == nullptr) { + return E_POINTER; + } + *ppstrId = co_task_wcsdup(NULL_DEVICE_ID); + return *ppstrId != nullptr ? S_OK : E_OUTOFMEMORY; +} +HRESULT STDMETHODCALLTYPE NullMMDevice::GetState(DWORD *pdwState) { + if (pdwState == nullptr) { + return E_POINTER; + } + *pdwState = DEVICE_STATE_ACTIVE; + return S_OK; +} +#pragma endregion diff --git a/src/spice2x/hooks/audio/backends/mmdevice/null_device.h b/src/spice2x/hooks/audio/backends/mmdevice/null_device.h index ea64f6f..a479869 100644 --- a/src/spice2x/hooks/audio/backends/mmdevice/null_device.h +++ b/src/spice2x/hooks/audio/backends/mmdevice/null_device.h @@ -1,39 +1,39 @@ -#pragma once - -#include - -#include - -// returns true when a synthetic render endpoint should be injected into device -// enumeration. games like gitadora arena search the render endpoint list for a -// device whose friendly name contains "Realtek" and crash with a null pointer -// dereference when no match exists. presenting a fake match that routes to the -// null audio backend lets the search succeed while discarding the audio. -bool null_render_device_enabled(); - -// fake IMMDevice that reports a "Realtek" friendly name and activates straight -// into the null audio backend, never touching real hardware. -struct NullMMDevice : IMMDevice { - NullMMDevice() = default; - - NullMMDevice(const NullMMDevice &) = delete; - NullMMDevice &operator=(const NullMMDevice &) = delete; - - virtual ~NullMMDevice() = default; - -#pragma region IUnknown - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObj) override; - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; -#pragma endregion - -#pragma region IMMDevice - HRESULT STDMETHODCALLTYPE Activate(REFIID iid, DWORD dwClsCtx, PROPVARIANT *pActivationParams, void **ppInterface) override; - HRESULT STDMETHODCALLTYPE OpenPropertyStore(DWORD stgmAccess, IPropertyStore **ppProperties) override; - HRESULT STDMETHODCALLTYPE GetId(LPWSTR *ppstrId) override; - HRESULT STDMETHODCALLTYPE GetState(DWORD *pdwState) override; -#pragma endregion - -private: - std::atomic ref_cnt = 1; -}; +#pragma once + +#include + +#include + +// returns true when a synthetic render endpoint should be injected into device +// enumeration. games like gitadora arena search the render endpoint list for a +// device whose friendly name contains "Realtek" and crash with a null pointer +// dereference when no match exists. presenting a fake match that routes to the +// null audio backend lets the search succeed while discarding the audio. +bool null_render_device_enabled(); + +// fake IMMDevice that reports a "Realtek" friendly name and activates straight +// into the null audio backend, never touching real hardware. +struct NullMMDevice : IMMDevice { + NullMMDevice() = default; + + NullMMDevice(const NullMMDevice &) = delete; + NullMMDevice &operator=(const NullMMDevice &) = delete; + + virtual ~NullMMDevice() = default; + +#pragma region IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObj) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; +#pragma endregion + +#pragma region IMMDevice + HRESULT STDMETHODCALLTYPE Activate(REFIID iid, DWORD dwClsCtx, PROPVARIANT *pActivationParams, void **ppInterface) override; + HRESULT STDMETHODCALLTYPE OpenPropertyStore(DWORD stgmAccess, IPropertyStore **ppProperties) override; + HRESULT STDMETHODCALLTYPE GetId(LPWSTR *ppstrId) override; + HRESULT STDMETHODCALLTYPE GetState(DWORD *pdwState) override; +#pragma endregion + +private: + std::atomic ref_cnt = 1; +}; diff --git a/src/spice2x/hooks/audio/backends/mmdevice/null_discard_backend.cpp b/src/spice2x/hooks/audio/backends/mmdevice/null_discard_backend.cpp index 4c1c949..20d0a1b 100644 --- a/src/spice2x/hooks/audio/backends/mmdevice/null_discard_backend.cpp +++ b/src/spice2x/hooks/audio/backends/mmdevice/null_discard_backend.cpp @@ -1,139 +1,139 @@ -#include "null_discard_backend.h" - -#include -#include -#include - -#include "hooks/audio/util.h" -#include "util/logging.h" - -NullDiscardBackend::~NullDiscardBackend() { - this->running = false; - if (this->pacing_thread.joinable()) { - this->pacing_thread.join(); - } -} - -const WAVEFORMATEXTENSIBLE &NullDiscardBackend::format() const noexcept { - return this->format_; -} - -HRESULT NullDiscardBackend::on_initialize( - AUDCLNT_SHAREMODE *, - DWORD *, - REFERENCE_TIME *hnsBufferDuration, - REFERENCE_TIME *, - const WAVEFORMATEX *pFormat, - LPCGUID) -{ - copy_wave_format(&this->format_, pFormat); - - // honor the game's requested buffer duration, falling back to 10 ms - constexpr REFERENCE_TIME DEFAULT_REFTIME = 100000; // 10 ms in 100-ns units - this->period_reftime = (hnsBufferDuration && *hnsBufferDuration > 0) - ? *hnsBufferDuration - : DEFAULT_REFTIME; - - this->buffer_frames = std::max(1, static_cast( - static_cast(this->format_.Format.nSamplesPerSec) - * this->period_reftime / 10000000.0 + 0.5)); - - log_info("audio::null", "initializing null render device with {} channels, {} Hz, {}-bit", - this->format_.Format.nChannels, - this->format_.Format.nSamplesPerSec, - this->format_.Format.wBitsPerSample); - - return S_OK; -} - -HRESULT NullDiscardBackend::on_get_buffer_size(uint32_t *buffer_frames) { - *buffer_frames = this->buffer_frames; - return S_OK; -} - -HRESULT NullDiscardBackend::on_get_stream_latency(REFERENCE_TIME *latency) { - *latency = this->period_reftime; - return S_OK; -} - -HRESULT NullDiscardBackend::on_get_current_padding(std::optional &padding_frames) { - // discarded immediately, so the buffer always reads as fully drained - padding_frames = 0; - return S_OK; -} - -HRESULT NullDiscardBackend::on_is_format_supported( - AUDCLNT_SHAREMODE *, - const WAVEFORMATEX *, - WAVEFORMATEX **ppClosestMatch) -{ - if (ppClosestMatch) { - *ppClosestMatch = nullptr; - } - return S_OK; -} - -HRESULT NullDiscardBackend::on_get_mix_format(WAVEFORMATEX **) { - return E_NOTIMPL; -} - -HRESULT NullDiscardBackend::on_get_device_period( - REFERENCE_TIME *default_device_period, - REFERENCE_TIME *minimum_device_period) -{ - if (default_device_period) { - *default_device_period = this->period_reftime; - } - if (minimum_device_period) { - *minimum_device_period = this->period_reftime; - } - return S_OK; -} - -HRESULT NullDiscardBackend::on_start() { - if (!this->running.exchange(true)) { - this->pacing_thread = std::thread(&NullDiscardBackend::pace_loop, this); - } - return S_OK; -} - -HRESULT NullDiscardBackend::on_stop() { - return S_OK; -} - -HRESULT NullDiscardBackend::on_set_event_handle(HANDLE *event_handle) { - // keep the game's event so pace_loop() can wake it; there is no real device behind it - this->relay_handle = *event_handle; - return S_OK; -} - -HRESULT NullDiscardBackend::on_get_buffer(uint32_t num_frames_requested, BYTE **ppData) { - const size_t buffer_size = - static_cast(this->format_.Format.nBlockAlign) * num_frames_requested; - if (this->scratch.size() < buffer_size) { - this->scratch.resize(buffer_size); - } - *ppData = this->scratch.data(); - return S_OK; -} - -HRESULT NullDiscardBackend::on_release_buffer(uint32_t, DWORD) { - // discard the audio entirely - return S_OK; -} - -void NullDiscardBackend::pace_loop() { - using namespace std::chrono; - - // audio is discarded, so timing precision and drift do not matter; just wake the - // game once per buffer period to keep its render thread from blocking on the event. - const auto period = duration_cast( - duration(this->period_reftime / 10000000.0)); - - while (this->running.load()) { - if (this->relay_handle) { - SetEvent(this->relay_handle); - } - std::this_thread::sleep_for(period); - } -} +#include "null_discard_backend.h" + +#include +#include +#include + +#include "hooks/audio/util.h" +#include "util/logging.h" + +NullDiscardBackend::~NullDiscardBackend() { + this->running = false; + if (this->pacing_thread.joinable()) { + this->pacing_thread.join(); + } +} + +const WAVEFORMATEXTENSIBLE &NullDiscardBackend::format() const noexcept { + return this->format_; +} + +HRESULT NullDiscardBackend::on_initialize( + AUDCLNT_SHAREMODE *, + DWORD *, + REFERENCE_TIME *hnsBufferDuration, + REFERENCE_TIME *, + const WAVEFORMATEX *pFormat, + LPCGUID) +{ + copy_wave_format(&this->format_, pFormat); + + // honor the game's requested buffer duration, falling back to 10 ms + constexpr REFERENCE_TIME DEFAULT_REFTIME = 100000; // 10 ms in 100-ns units + this->period_reftime = (hnsBufferDuration && *hnsBufferDuration > 0) + ? *hnsBufferDuration + : DEFAULT_REFTIME; + + this->buffer_frames = std::max(1, static_cast( + static_cast(this->format_.Format.nSamplesPerSec) + * this->period_reftime / 10000000.0 + 0.5)); + + log_info("audio::null", "initializing null render device with {} channels, {} Hz, {}-bit", + this->format_.Format.nChannels, + this->format_.Format.nSamplesPerSec, + this->format_.Format.wBitsPerSample); + + return S_OK; +} + +HRESULT NullDiscardBackend::on_get_buffer_size(uint32_t *buffer_frames) { + *buffer_frames = this->buffer_frames; + return S_OK; +} + +HRESULT NullDiscardBackend::on_get_stream_latency(REFERENCE_TIME *latency) { + *latency = this->period_reftime; + return S_OK; +} + +HRESULT NullDiscardBackend::on_get_current_padding(std::optional &padding_frames) { + // discarded immediately, so the buffer always reads as fully drained + padding_frames = 0; + return S_OK; +} + +HRESULT NullDiscardBackend::on_is_format_supported( + AUDCLNT_SHAREMODE *, + const WAVEFORMATEX *, + WAVEFORMATEX **ppClosestMatch) +{ + if (ppClosestMatch) { + *ppClosestMatch = nullptr; + } + return S_OK; +} + +HRESULT NullDiscardBackend::on_get_mix_format(WAVEFORMATEX **) { + return E_NOTIMPL; +} + +HRESULT NullDiscardBackend::on_get_device_period( + REFERENCE_TIME *default_device_period, + REFERENCE_TIME *minimum_device_period) +{ + if (default_device_period) { + *default_device_period = this->period_reftime; + } + if (minimum_device_period) { + *minimum_device_period = this->period_reftime; + } + return S_OK; +} + +HRESULT NullDiscardBackend::on_start() { + if (!this->running.exchange(true)) { + this->pacing_thread = std::thread(&NullDiscardBackend::pace_loop, this); + } + return S_OK; +} + +HRESULT NullDiscardBackend::on_stop() { + return S_OK; +} + +HRESULT NullDiscardBackend::on_set_event_handle(HANDLE *event_handle) { + // keep the game's event so pace_loop() can wake it; there is no real device behind it + this->relay_handle = *event_handle; + return S_OK; +} + +HRESULT NullDiscardBackend::on_get_buffer(uint32_t num_frames_requested, BYTE **ppData) { + const size_t buffer_size = + static_cast(this->format_.Format.nBlockAlign) * num_frames_requested; + if (this->scratch.size() < buffer_size) { + this->scratch.resize(buffer_size); + } + *ppData = this->scratch.data(); + return S_OK; +} + +HRESULT NullDiscardBackend::on_release_buffer(uint32_t, DWORD) { + // discard the audio entirely + return S_OK; +} + +void NullDiscardBackend::pace_loop() { + using namespace std::chrono; + + // audio is discarded, so timing precision and drift do not matter; just wake the + // game once per buffer period to keep its render thread from blocking on the event. + const auto period = duration_cast( + duration(this->period_reftime / 10000000.0)); + + while (this->running.load()) { + if (this->relay_handle) { + SetEvent(this->relay_handle); + } + std::this_thread::sleep_for(period); + } +} diff --git a/src/spice2x/hooks/audio/backends/mmdevice/null_discard_backend.h b/src/spice2x/hooks/audio/backends/mmdevice/null_discard_backend.h index ceef773..7072de6 100644 --- a/src/spice2x/hooks/audio/backends/mmdevice/null_discard_backend.h +++ b/src/spice2x/hooks/audio/backends/mmdevice/null_discard_backend.h @@ -1,54 +1,54 @@ -#pragma once - -#include -#include -#include -#include - -#include - -#include "hooks/audio/implementations/backend.h" - -// discards all audio while pacing the game's event handle once per buffer period, so the game -// keeps running normally with nothing output to any real device. routed through the shared -// DummyIAudioClient, the same plumbing the asio backend uses. -struct NullDiscardBackend final : AudioBackend { - ~NullDiscardBackend() final; - - const WAVEFORMATEXTENSIBLE &format() const noexcept override; - - HRESULT on_initialize( - AUDCLNT_SHAREMODE *, - DWORD *, - REFERENCE_TIME *hnsBufferDuration, - REFERENCE_TIME *, - const WAVEFORMATEX *pFormat, - LPCGUID) override; - HRESULT on_get_buffer_size(uint32_t *buffer_frames) override; - HRESULT on_get_stream_latency(REFERENCE_TIME *latency) override; - HRESULT on_get_current_padding(std::optional &padding_frames) override; - HRESULT on_is_format_supported( - AUDCLNT_SHAREMODE *, - const WAVEFORMATEX *, - WAVEFORMATEX **ppClosestMatch) override; - HRESULT on_get_mix_format(WAVEFORMATEX **) override; - HRESULT on_get_device_period( - REFERENCE_TIME *default_device_period, - REFERENCE_TIME *minimum_device_period) override; - HRESULT on_start() override; - HRESULT on_stop() override; - HRESULT on_set_event_handle(HANDLE *event_handle) override; - HRESULT on_get_buffer(uint32_t num_frames_requested, BYTE **ppData) override; - HRESULT on_release_buffer(uint32_t, DWORD) override; - -private: - void pace_loop(); - - WAVEFORMATEXTENSIBLE format_ {}; - uint32_t buffer_frames = 0; - REFERENCE_TIME period_reftime = 0; - HANDLE relay_handle = nullptr; - std::vector scratch; - std::thread pacing_thread; - std::atomic running = false; -}; +#pragma once + +#include +#include +#include +#include + +#include + +#include "hooks/audio/implementations/backend.h" + +// discards all audio while pacing the game's event handle once per buffer period, so the game +// keeps running normally with nothing output to any real device. routed through the shared +// DummyIAudioClient, the same plumbing the asio backend uses. +struct NullDiscardBackend final : AudioBackend { + ~NullDiscardBackend() final; + + const WAVEFORMATEXTENSIBLE &format() const noexcept override; + + HRESULT on_initialize( + AUDCLNT_SHAREMODE *, + DWORD *, + REFERENCE_TIME *hnsBufferDuration, + REFERENCE_TIME *, + const WAVEFORMATEX *pFormat, + LPCGUID) override; + HRESULT on_get_buffer_size(uint32_t *buffer_frames) override; + HRESULT on_get_stream_latency(REFERENCE_TIME *latency) override; + HRESULT on_get_current_padding(std::optional &padding_frames) override; + HRESULT on_is_format_supported( + AUDCLNT_SHAREMODE *, + const WAVEFORMATEX *, + WAVEFORMATEX **ppClosestMatch) override; + HRESULT on_get_mix_format(WAVEFORMATEX **) override; + HRESULT on_get_device_period( + REFERENCE_TIME *default_device_period, + REFERENCE_TIME *minimum_device_period) override; + HRESULT on_start() override; + HRESULT on_stop() override; + HRESULT on_set_event_handle(HANDLE *event_handle) override; + HRESULT on_get_buffer(uint32_t num_frames_requested, BYTE **ppData) override; + HRESULT on_release_buffer(uint32_t, DWORD) override; + +private: + void pace_loop(); + + WAVEFORMATEXTENSIBLE format_ {}; + uint32_t buffer_frames = 0; + REFERENCE_TIME period_reftime = 0; + HANDLE relay_handle = nullptr; + std::vector scratch; + std::thread pacing_thread; + std::atomic running = false; +}; diff --git a/src/spice2x/hooks/audio/backends/wasapi/downmix.cpp b/src/spice2x/hooks/audio/backends/wasapi/downmix.cpp index 16a3da4..b960832 100644 --- a/src/spice2x/hooks/audio/backends/wasapi/downmix.cpp +++ b/src/spice2x/hooks/audio/backends/wasapi/downmix.cpp @@ -1,264 +1,264 @@ -#include "downmix.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include "util/logging.h" - -#include "util.h" - -namespace hooks::audio { - - namespace { - - constexpr float ATT_3DB = 0.70710678f; - - // speakers routed to the left/right output; anything else (center) feeds both sides - constexpr DWORD LEFT_SPEAKERS = SPEAKER_FRONT_LEFT | SPEAKER_BACK_LEFT | SPEAKER_SIDE_LEFT - | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_TOP_FRONT_LEFT | SPEAKER_TOP_BACK_LEFT; - constexpr DWORD RIGHT_SPEAKERS = SPEAKER_FRONT_RIGHT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_RIGHT - | SPEAKER_FRONT_RIGHT_OF_CENTER | SPEAKER_TOP_FRONT_RIGHT | SPEAKER_TOP_BACK_RIGHT; - - // the speaker mask is only present on WAVE_FORMAT_EXTENSIBLE formats - DWORD read_channel_mask(const WAVEFORMATEX *fmt) { - if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE - && fmt->cbSize >= sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) { - return reinterpret_cast(fmt)->dwChannelMask; - } - return 0; - } - - // call visit(channel_index, speaker_bit) for each present speaker, in channel order - template - void for_each_speaker(DWORD mask, int channels, F &&visit) { - int channel = 0; - for (int bit = 0; bit < 18 && channel < channels; bit++) { - const DWORD speaker = 1u << bit; - if (mask & speaker) { - visit(channel++, speaker); - } - } - } - } - - void Downmix::setup(const WAVEFORMATEX *game_format, WAVEFORMATEXTENSIBLE *stereo_out, - DownmixAlgorithm algorithm) { - this->enabled = true; - this->algorithm = algorithm; - this->bytes_per_sample = game_format->wBitsPerSample / 8; - this->game_frame_size = game_format->nChannels * this->bytes_per_sample; - - this->is_float = is_ieee_float(game_format); - - // supported: 16/24/32-bit integer PCM and 32-bit float; anything else mixes to silence - const bool supported = this->is_float - ? this->bytes_per_sample == 4 - : (this->bytes_per_sample >= 2 && this->bytes_per_sample <= 4); - if (!supported) { - log_fatal( - "audio::downmix", - "unsupported sample format ({}-bit {}), downmix will output silence", - game_format->wBitsPerSample, this->is_float ? "float" : "int"); - } - - this->left_mix.clear(); - this->right_mix.clear(); - this->build_layout_mix(game_format); - - make_stereo_format(game_format, stereo_out); - } - - void Downmix::make_stereo_format(const WAVEFORMATEX *game_format, - WAVEFORMATEXTENSIBLE *stereo_out) { - const int bytes_per_sample = game_format->wBitsPerSample / 8; - - memcpy(stereo_out, game_format, sizeof(WAVEFORMATEXTENSIBLE)); - stereo_out->Format.nChannels = 2; - stereo_out->Format.nBlockAlign = 2 * bytes_per_sample; - stereo_out->Format.nAvgBytesPerSec = - game_format->nSamplesPerSec * stereo_out->Format.nBlockAlign; - stereo_out->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; - } - - HRESULT Downmix::initialize(IAudioClient *real, AUDCLNT_SHAREMODE share_mode, DWORD stream_flags, - REFERENCE_TIME buffer_duration, REFERENCE_TIME periodicity, - const WAVEFORMATEX *device_format, LPCGUID session_guid) { - - // the smaller stereo buffer can end up unaligned for the device when the game sized the - // duration for its larger multi-channel format; the helper recovers from that. - return initialize_with_alignment_retry(real, "audio::downmix", share_mode, stream_flags, - buffer_duration, periodicity, device_format, session_guid); - } - - void Downmix::add_channel(int channel, DWORD speaker, float gain) { - if (speaker & LEFT_SPEAKERS) { - this->left_mix.push_back({ channel, gain }); - } else if (speaker & RIGHT_SPEAKERS) { - this->right_mix.push_back({ channel, gain }); - } else { // center: feed both sides - this->left_mix.push_back({ channel, gain }); - this->right_mix.push_back({ channel, gain }); - } - } - - // AC-4 stereo downmix (ETSI TS 103 190-1): front pair at unity, everything else -3 dB, LFE dropped - void Downmix::build_ac4_mix(DWORD mask, int channels) { - for_each_speaker(mask, channels, [&](int ch, DWORD speaker) { - if (speaker == SPEAKER_LOW_FREQUENCY) { - return; - } - const bool front_pair = speaker & (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT); - this->add_channel(ch, speaker, front_pair ? 1.0f : ATT_3DB); - }); - } - - // keep only the channels in `keep` (front/rear/side), each at unity gain - void Downmix::build_extract_mix(DWORD mask, int channels, DWORD keep) { - for_each_speaker(mask, channels, [&](int ch, DWORD speaker) { - if (speaker & keep) { - this->add_channel(ch, speaker, 1.0f); - } - }); - } - - // keep every channel (LFE dropped), then average each side so its gains sum to unity - void Downmix::build_normalize_mix(DWORD mask, int channels) { - for_each_speaker(mask, channels, [&](int ch, DWORD speaker) { - if (speaker != SPEAKER_LOW_FREQUENCY) { - this->add_channel(ch, speaker, 1.0f); - } - }); - - for (auto *mix : { &this->left_mix, &this->right_mix }) { - if (!mix->empty()) { - const float gain = 1.0f / mix->size(); - for (auto &c : *mix) { - c.gain = gain; - } - } - } - } - - // fallback when no speaker mask is present: fold interleaved L/R pairs (even->left, odd->right) - void Downmix::build_pairs_mix(int channels, float gain) { - for (int ch = 0; ch < channels; ch++) { - (((ch & 1) == 0) ? this->left_mix : this->right_mix).push_back({ ch, gain }); - } - } - - void Downmix::build_layout_mix(const WAVEFORMATEX *game_format) { - const int channels = game_format->nChannels; - const DWORD mask = read_channel_mask(game_format); - - // without a mask the layout is unknown: extract/normalize have nothing to act on, so all - // algorithms fall back to folding L/R pairs (AC-4 still attenuates by -3 dB) - if (mask == 0) { - this->build_pairs_mix(channels, - this->algorithm == DownmixAlgorithm::AC4 ? ATT_3DB : 1.0f); - return; - } - - switch (this->algorithm) { - case DownmixAlgorithm::FrontOnly: - this->build_extract_mix(mask, channels, - SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT); - break; - case DownmixAlgorithm::RearOnly: - this->build_extract_mix(mask, channels, - SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_BACK_CENTER); - break; - case DownmixAlgorithm::SideOnly: - this->build_extract_mix(mask, channels, - SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT); - break; - case DownmixAlgorithm::Normalize: - this->build_normalize_mix(mask, channels); - break; - case DownmixAlgorithm::AC4: - this->build_ac4_mix(mask, channels); - break; - } - } - - void Downmix::process(BYTE *dst, const BYTE *src, UINT32 frames) const { - const int bps = this->bytes_per_sample; - const int src_stride = this->game_frame_size; - const int dst_stride = 2 * bps; - - if (dst == nullptr || src == nullptr || bps <= 0) { - return; - } - - // sum each speaker's source channels into the matching stereo output - for (UINT32 i = 0; i < frames; i++) { - const BYTE *in = src + (size_t) i * src_stride; - BYTE *out = dst + (size_t) i * dst_stride; - - float left = 0.0f; - float right = 0.0f; - for (const auto &c : this->left_mix) { - left += read_sample(in + c.channel * bps, bps, this->is_float) * c.gain; - } - for (const auto &c : this->right_mix) { - right += read_sample(in + c.channel * bps, bps, this->is_float) * c.gain; - } - write_sample(out, bps, this->is_float, left); - write_sample(out + bps, bps, this->is_float, right); - } - } - - HRESULT Downmix::get_buffer(IAudioRenderClient *real, UINT32 frames, BYTE **ppData) { - const size_t needed = (size_t) frames * this->game_frame_size; - if (this->scratch.size() < needed) { - this->scratch.resize(needed); - } - - HRESULT ret = real->GetBuffer(frames, &this->device_buffer); - if (FAILED(ret)) { - this->device_buffer = nullptr; - return ret; - } - - *ppData = this->scratch.data(); - - return S_OK; - } - - HRESULT Downmix::get_scratch(UINT32 frames, BYTE **ppData) { - const size_t needed = (size_t) frames * this->game_frame_size; - if (this->scratch.size() < needed) { - this->scratch.resize(needed); - } - - *ppData = this->scratch.data(); - - return S_OK; - } - - void Downmix::downmix_into(BYTE *dst, UINT32 frames) const { - this->process(dst, this->scratch.data(), frames); - } - - void Downmix::write_device_buffer(UINT32 frames, DWORD flags) { - const int bps = this->bytes_per_sample; - const int dst_stride = 2 * bps; - - if (this->device_buffer == nullptr || frames == 0 || bps <= 0) { - return; - } - - // mute the first few buffers to avoid a pop on stream start - if (this->buffers_to_mute > 0) { - memset(this->device_buffer, 0, (size_t) frames * dst_stride); - this->buffers_to_mute--; - } else if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) == 0) { - this->process(this->device_buffer, this->scratch.data(), frames); - } - } -} +#include "downmix.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "util/logging.h" + +#include "util.h" + +namespace hooks::audio { + + namespace { + + constexpr float ATT_3DB = 0.70710678f; + + // speakers routed to the left/right output; anything else (center) feeds both sides + constexpr DWORD LEFT_SPEAKERS = SPEAKER_FRONT_LEFT | SPEAKER_BACK_LEFT | SPEAKER_SIDE_LEFT + | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_TOP_FRONT_LEFT | SPEAKER_TOP_BACK_LEFT; + constexpr DWORD RIGHT_SPEAKERS = SPEAKER_FRONT_RIGHT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_RIGHT + | SPEAKER_FRONT_RIGHT_OF_CENTER | SPEAKER_TOP_FRONT_RIGHT | SPEAKER_TOP_BACK_RIGHT; + + // the speaker mask is only present on WAVE_FORMAT_EXTENSIBLE formats + DWORD read_channel_mask(const WAVEFORMATEX *fmt) { + if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE + && fmt->cbSize >= sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) { + return reinterpret_cast(fmt)->dwChannelMask; + } + return 0; + } + + // call visit(channel_index, speaker_bit) for each present speaker, in channel order + template + void for_each_speaker(DWORD mask, int channels, F &&visit) { + int channel = 0; + for (int bit = 0; bit < 18 && channel < channels; bit++) { + const DWORD speaker = 1u << bit; + if (mask & speaker) { + visit(channel++, speaker); + } + } + } + } + + void Downmix::setup(const WAVEFORMATEX *game_format, WAVEFORMATEXTENSIBLE *stereo_out, + DownmixAlgorithm algorithm) { + this->enabled = true; + this->algorithm = algorithm; + this->bytes_per_sample = game_format->wBitsPerSample / 8; + this->game_frame_size = game_format->nChannels * this->bytes_per_sample; + + this->is_float = is_ieee_float(game_format); + + // supported: 16/24/32-bit integer PCM and 32-bit float; anything else mixes to silence + const bool supported = this->is_float + ? this->bytes_per_sample == 4 + : (this->bytes_per_sample >= 2 && this->bytes_per_sample <= 4); + if (!supported) { + log_fatal( + "audio::downmix", + "unsupported sample format ({}-bit {}), downmix will output silence", + game_format->wBitsPerSample, this->is_float ? "float" : "int"); + } + + this->left_mix.clear(); + this->right_mix.clear(); + this->build_layout_mix(game_format); + + make_stereo_format(game_format, stereo_out); + } + + void Downmix::make_stereo_format(const WAVEFORMATEX *game_format, + WAVEFORMATEXTENSIBLE *stereo_out) { + const int bytes_per_sample = game_format->wBitsPerSample / 8; + + memcpy(stereo_out, game_format, sizeof(WAVEFORMATEXTENSIBLE)); + stereo_out->Format.nChannels = 2; + stereo_out->Format.nBlockAlign = 2 * bytes_per_sample; + stereo_out->Format.nAvgBytesPerSec = + game_format->nSamplesPerSec * stereo_out->Format.nBlockAlign; + stereo_out->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; + } + + HRESULT Downmix::initialize(IAudioClient *real, AUDCLNT_SHAREMODE share_mode, DWORD stream_flags, + REFERENCE_TIME buffer_duration, REFERENCE_TIME periodicity, + const WAVEFORMATEX *device_format, LPCGUID session_guid) { + + // the smaller stereo buffer can end up unaligned for the device when the game sized the + // duration for its larger multi-channel format; the helper recovers from that. + return initialize_with_alignment_retry(real, "audio::downmix", share_mode, stream_flags, + buffer_duration, periodicity, device_format, session_guid); + } + + void Downmix::add_channel(int channel, DWORD speaker, float gain) { + if (speaker & LEFT_SPEAKERS) { + this->left_mix.push_back({ channel, gain }); + } else if (speaker & RIGHT_SPEAKERS) { + this->right_mix.push_back({ channel, gain }); + } else { // center: feed both sides + this->left_mix.push_back({ channel, gain }); + this->right_mix.push_back({ channel, gain }); + } + } + + // AC-4 stereo downmix (ETSI TS 103 190-1): front pair at unity, everything else -3 dB, LFE dropped + void Downmix::build_ac4_mix(DWORD mask, int channels) { + for_each_speaker(mask, channels, [&](int ch, DWORD speaker) { + if (speaker == SPEAKER_LOW_FREQUENCY) { + return; + } + const bool front_pair = speaker & (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT); + this->add_channel(ch, speaker, front_pair ? 1.0f : ATT_3DB); + }); + } + + // keep only the channels in `keep` (front/rear/side), each at unity gain + void Downmix::build_extract_mix(DWORD mask, int channels, DWORD keep) { + for_each_speaker(mask, channels, [&](int ch, DWORD speaker) { + if (speaker & keep) { + this->add_channel(ch, speaker, 1.0f); + } + }); + } + + // keep every channel (LFE dropped), then average each side so its gains sum to unity + void Downmix::build_normalize_mix(DWORD mask, int channels) { + for_each_speaker(mask, channels, [&](int ch, DWORD speaker) { + if (speaker != SPEAKER_LOW_FREQUENCY) { + this->add_channel(ch, speaker, 1.0f); + } + }); + + for (auto *mix : { &this->left_mix, &this->right_mix }) { + if (!mix->empty()) { + const float gain = 1.0f / mix->size(); + for (auto &c : *mix) { + c.gain = gain; + } + } + } + } + + // fallback when no speaker mask is present: fold interleaved L/R pairs (even->left, odd->right) + void Downmix::build_pairs_mix(int channels, float gain) { + for (int ch = 0; ch < channels; ch++) { + (((ch & 1) == 0) ? this->left_mix : this->right_mix).push_back({ ch, gain }); + } + } + + void Downmix::build_layout_mix(const WAVEFORMATEX *game_format) { + const int channels = game_format->nChannels; + const DWORD mask = read_channel_mask(game_format); + + // without a mask the layout is unknown: extract/normalize have nothing to act on, so all + // algorithms fall back to folding L/R pairs (AC-4 still attenuates by -3 dB) + if (mask == 0) { + this->build_pairs_mix(channels, + this->algorithm == DownmixAlgorithm::AC4 ? ATT_3DB : 1.0f); + return; + } + + switch (this->algorithm) { + case DownmixAlgorithm::FrontOnly: + this->build_extract_mix(mask, channels, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT); + break; + case DownmixAlgorithm::RearOnly: + this->build_extract_mix(mask, channels, + SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_BACK_CENTER); + break; + case DownmixAlgorithm::SideOnly: + this->build_extract_mix(mask, channels, + SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT); + break; + case DownmixAlgorithm::Normalize: + this->build_normalize_mix(mask, channels); + break; + case DownmixAlgorithm::AC4: + this->build_ac4_mix(mask, channels); + break; + } + } + + void Downmix::process(BYTE *dst, const BYTE *src, UINT32 frames) const { + const int bps = this->bytes_per_sample; + const int src_stride = this->game_frame_size; + const int dst_stride = 2 * bps; + + if (dst == nullptr || src == nullptr || bps <= 0) { + return; + } + + // sum each speaker's source channels into the matching stereo output + for (UINT32 i = 0; i < frames; i++) { + const BYTE *in = src + (size_t) i * src_stride; + BYTE *out = dst + (size_t) i * dst_stride; + + float left = 0.0f; + float right = 0.0f; + for (const auto &c : this->left_mix) { + left += read_sample(in + c.channel * bps, bps, this->is_float) * c.gain; + } + for (const auto &c : this->right_mix) { + right += read_sample(in + c.channel * bps, bps, this->is_float) * c.gain; + } + write_sample(out, bps, this->is_float, left); + write_sample(out + bps, bps, this->is_float, right); + } + } + + HRESULT Downmix::get_buffer(IAudioRenderClient *real, UINT32 frames, BYTE **ppData) { + const size_t needed = (size_t) frames * this->game_frame_size; + if (this->scratch.size() < needed) { + this->scratch.resize(needed); + } + + HRESULT ret = real->GetBuffer(frames, &this->device_buffer); + if (FAILED(ret)) { + this->device_buffer = nullptr; + return ret; + } + + *ppData = this->scratch.data(); + + return S_OK; + } + + HRESULT Downmix::get_scratch(UINT32 frames, BYTE **ppData) { + const size_t needed = (size_t) frames * this->game_frame_size; + if (this->scratch.size() < needed) { + this->scratch.resize(needed); + } + + *ppData = this->scratch.data(); + + return S_OK; + } + + void Downmix::downmix_into(BYTE *dst, UINT32 frames) const { + this->process(dst, this->scratch.data(), frames); + } + + void Downmix::write_device_buffer(UINT32 frames, DWORD flags) { + const int bps = this->bytes_per_sample; + const int dst_stride = 2 * bps; + + if (this->device_buffer == nullptr || frames == 0 || bps <= 0) { + return; + } + + // mute the first few buffers to avoid a pop on stream start + if (this->buffers_to_mute > 0) { + memset(this->device_buffer, 0, (size_t) frames * dst_stride); + this->buffers_to_mute--; + } else if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) == 0) { + this->process(this->device_buffer, this->scratch.data(), frames); + } + } +} diff --git a/src/spice2x/hooks/audio/backends/wasapi/downmix.h b/src/spice2x/hooks/audio/backends/wasapi/downmix.h index db32fc1..bee91f2 100644 --- a/src/spice2x/hooks/audio/backends/wasapi/downmix.h +++ b/src/spice2x/hooks/audio/backends/wasapi/downmix.h @@ -1,150 +1,150 @@ -#pragma once - -#include -#include - -#include -#include -#include - -#include "hooks/audio/audio.h" - -struct IAudioClient; -struct IAudioRenderClient; - -namespace hooks::audio { - - // Generic WASAPI surround-to-stereo downmix. The real device is opened in stereo while the - // game keeps writing its native multi-channel audio into a scratch buffer; on release that - // buffer is mixed down into the two front channels. - // - // The mix is derived from the source format's speaker mask according to the selected - // DownmixAlgorithm: - // FrontOnly / RearOnly / SideOnly - keep only that group of channels, routed to their side - // AC4 - AC-4 stereo downmix coefficients (ETSI TS 103 190-1 §6.2.17): front left/right - // pass at 0 dB, center and surrounds fold in at -3 dB, LFE dropped - // Normalize - every channel folded in (center to both sides) with each output side averaged - // so its channels are equally loud, LFE dropped - struct Downmix { - - // a source channel routed into one output speaker at the given gain - struct Contribution { - int channel; - float gain; - }; - - // map an option value (front/rear/side/ac4/normalize) to its algorithm. - static std::optional name_to_algorithm(const char *value) { - if (_stricmp(value, "front") == 0) { - return DownmixAlgorithm::FrontOnly; - } else if (_stricmp(value, "rear") == 0) { - return DownmixAlgorithm::RearOnly; - } else if (_stricmp(value, "side") == 0) { - return DownmixAlgorithm::SideOnly; - } else if (_stricmp(value, "ac4") == 0) { - return DownmixAlgorithm::AC4; - } else if (_stricmp(value, "normalize") == 0) { - return DownmixAlgorithm::Normalize; - } - - return std::nullopt; - } - - // human-readable name of an algorithm, for logging. - static const char *algorithm_name(DownmixAlgorithm algorithm) { - switch (algorithm) { - case DownmixAlgorithm::FrontOnly: return "front"; - case DownmixAlgorithm::RearOnly: return "rear"; - case DownmixAlgorithm::SideOnly: return "side"; - case DownmixAlgorithm::AC4: return "ac4"; - case DownmixAlgorithm::Normalize: return "normalize"; - default: return "unknown"; - } - } - - // whether the downmix is active for the current stream - bool enabled = false; - - // algorithm used to fold the multi-channel audio into stereo - DownmixAlgorithm algorithm = DownmixAlgorithm::AC4; - - // size in bytes of one frame of the game's multi-channel format - int game_frame_size = 0; - - // size in bytes of a single sample (per channel) - int bytes_per_sample = 0; - - // whether samples are IEEE floating point rather than integer PCM - bool is_float = false; - - // enable the downmix for the given game format and fill stereo_out with the equivalent - // stereo format to open the real device with. - void setup(const WAVEFORMATEX *game_format, WAVEFORMATEXTENSIBLE *stereo_out, - DownmixAlgorithm algorithm); - - // build the stereo format equivalent to game_format (same sample rate and bit depth). - static void make_stereo_format(const WAVEFORMATEX *game_format, - WAVEFORMATEXTENSIBLE *stereo_out); - - // initialize the real device with the stereo format. downmixing reduces the channel count, - // shrinking the buffer's byte size, so the duration the game sized for its multi-channel - // format can leave the smaller stereo buffer unaligned. on AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED - // this performs the standard WASAPI realignment and retries. - HRESULT initialize(IAudioClient *real, AUDCLNT_SHAREMODE share_mode, DWORD stream_flags, - REFERENCE_TIME buffer_duration, REFERENCE_TIME periodicity, - const WAVEFORMATEX *device_format, LPCGUID session_guid); - - // mix `frames` frames of multi-channel `src` down into stereo `dst`. - void process(BYTE *dst, const BYTE *src, UINT32 frames) const; - - // grab the real stereo device buffer and hand the game the scratch buffer to write into. - HRESULT get_buffer(IAudioRenderClient *real, UINT32 frames, BYTE **ppData); - - // size the scratch and hand it to the game without acquiring a device buffer. used when a - // later stage (the resampler) owns the device interaction. - HRESULT get_scratch(UINT32 frames, BYTE **ppData); - - // downmix the scratch the game wrote into the caller's stereo buffer, without touching the - // device. used to feed the resampler when the two stages are chained. - void downmix_into(BYTE *dst, UINT32 frames) const; - - // mix the scratch buffer into the stereo device buffer held since get_buffer. the caller - // owns releasing the device buffer afterwards (see current_buffer / buffer_released). - void write_device_buffer(UINT32 frames, DWORD flags); - - // the real device buffer currently held, or null. - BYTE *current_buffer() const { return this->device_buffer; } - - // forget the held device buffer once the caller has released it. - void buffer_released() { this->device_buffer = nullptr; } - - private: - - // build the mix from the source speaker layout for the selected algorithm - void build_layout_mix(const WAVEFORMATEX *game_format); - - // per-algorithm builders, each filling left_mix / right_mix from the speaker mask - void build_ac4_mix(DWORD mask, int channels); - void build_extract_mix(DWORD mask, int channels, DWORD keep); - void build_normalize_mix(DWORD mask, int channels); - - // fallback for streams without a speaker mask: fold interleaved L/R pairs at `gain` - void build_pairs_mix(int channels, float gain); - - // append one source channel to the output side(s) matching its speaker, at `gain` - void add_channel(int channel, DWORD speaker, float gain); - - // source channels summed into each output speaker - std::vector left_mix; - std::vector right_mix; - - // buffer the game writes its multi-channel audio into between get/release - std::vector scratch; - - // the real stereo device buffer currently held, or null - BYTE *device_buffer = nullptr; - - // leading buffers to silence to avoid a pop on stream start - int buffers_to_mute = 16; - }; -} +#pragma once + +#include +#include + +#include +#include +#include + +#include "hooks/audio/audio.h" + +struct IAudioClient; +struct IAudioRenderClient; + +namespace hooks::audio { + + // Generic WASAPI surround-to-stereo downmix. The real device is opened in stereo while the + // game keeps writing its native multi-channel audio into a scratch buffer; on release that + // buffer is mixed down into the two front channels. + // + // The mix is derived from the source format's speaker mask according to the selected + // DownmixAlgorithm: + // FrontOnly / RearOnly / SideOnly - keep only that group of channels, routed to their side + // AC4 - AC-4 stereo downmix coefficients (ETSI TS 103 190-1 §6.2.17): front left/right + // pass at 0 dB, center and surrounds fold in at -3 dB, LFE dropped + // Normalize - every channel folded in (center to both sides) with each output side averaged + // so its channels are equally loud, LFE dropped + struct Downmix { + + // a source channel routed into one output speaker at the given gain + struct Contribution { + int channel; + float gain; + }; + + // map an option value (front/rear/side/ac4/normalize) to its algorithm. + static std::optional name_to_algorithm(const char *value) { + if (_stricmp(value, "front") == 0) { + return DownmixAlgorithm::FrontOnly; + } else if (_stricmp(value, "rear") == 0) { + return DownmixAlgorithm::RearOnly; + } else if (_stricmp(value, "side") == 0) { + return DownmixAlgorithm::SideOnly; + } else if (_stricmp(value, "ac4") == 0) { + return DownmixAlgorithm::AC4; + } else if (_stricmp(value, "normalize") == 0) { + return DownmixAlgorithm::Normalize; + } + + return std::nullopt; + } + + // human-readable name of an algorithm, for logging. + static const char *algorithm_name(DownmixAlgorithm algorithm) { + switch (algorithm) { + case DownmixAlgorithm::FrontOnly: return "front"; + case DownmixAlgorithm::RearOnly: return "rear"; + case DownmixAlgorithm::SideOnly: return "side"; + case DownmixAlgorithm::AC4: return "ac4"; + case DownmixAlgorithm::Normalize: return "normalize"; + default: return "unknown"; + } + } + + // whether the downmix is active for the current stream + bool enabled = false; + + // algorithm used to fold the multi-channel audio into stereo + DownmixAlgorithm algorithm = DownmixAlgorithm::AC4; + + // size in bytes of one frame of the game's multi-channel format + int game_frame_size = 0; + + // size in bytes of a single sample (per channel) + int bytes_per_sample = 0; + + // whether samples are IEEE floating point rather than integer PCM + bool is_float = false; + + // enable the downmix for the given game format and fill stereo_out with the equivalent + // stereo format to open the real device with. + void setup(const WAVEFORMATEX *game_format, WAVEFORMATEXTENSIBLE *stereo_out, + DownmixAlgorithm algorithm); + + // build the stereo format equivalent to game_format (same sample rate and bit depth). + static void make_stereo_format(const WAVEFORMATEX *game_format, + WAVEFORMATEXTENSIBLE *stereo_out); + + // initialize the real device with the stereo format. downmixing reduces the channel count, + // shrinking the buffer's byte size, so the duration the game sized for its multi-channel + // format can leave the smaller stereo buffer unaligned. on AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED + // this performs the standard WASAPI realignment and retries. + HRESULT initialize(IAudioClient *real, AUDCLNT_SHAREMODE share_mode, DWORD stream_flags, + REFERENCE_TIME buffer_duration, REFERENCE_TIME periodicity, + const WAVEFORMATEX *device_format, LPCGUID session_guid); + + // mix `frames` frames of multi-channel `src` down into stereo `dst`. + void process(BYTE *dst, const BYTE *src, UINT32 frames) const; + + // grab the real stereo device buffer and hand the game the scratch buffer to write into. + HRESULT get_buffer(IAudioRenderClient *real, UINT32 frames, BYTE **ppData); + + // size the scratch and hand it to the game without acquiring a device buffer. used when a + // later stage (the resampler) owns the device interaction. + HRESULT get_scratch(UINT32 frames, BYTE **ppData); + + // downmix the scratch the game wrote into the caller's stereo buffer, without touching the + // device. used to feed the resampler when the two stages are chained. + void downmix_into(BYTE *dst, UINT32 frames) const; + + // mix the scratch buffer into the stereo device buffer held since get_buffer. the caller + // owns releasing the device buffer afterwards (see current_buffer / buffer_released). + void write_device_buffer(UINT32 frames, DWORD flags); + + // the real device buffer currently held, or null. + BYTE *current_buffer() const { return this->device_buffer; } + + // forget the held device buffer once the caller has released it. + void buffer_released() { this->device_buffer = nullptr; } + + private: + + // build the mix from the source speaker layout for the selected algorithm + void build_layout_mix(const WAVEFORMATEX *game_format); + + // per-algorithm builders, each filling left_mix / right_mix from the speaker mask + void build_ac4_mix(DWORD mask, int channels); + void build_extract_mix(DWORD mask, int channels, DWORD keep); + void build_normalize_mix(DWORD mask, int channels); + + // fallback for streams without a speaker mask: fold interleaved L/R pairs at `gain` + void build_pairs_mix(int channels, float gain); + + // append one source channel to the output side(s) matching its speaker, at `gain` + void add_channel(int channel, DWORD speaker, float gain); + + // source channels summed into each output speaker + std::vector left_mix; + std::vector right_mix; + + // buffer the game writes its multi-channel audio into between get/release + std::vector scratch; + + // the real stereo device buffer currently held, or null + BYTE *device_buffer = nullptr; + + // leading buffers to silence to avoid a pop on stream start + int buffers_to_mute = 16; + }; +} diff --git a/src/spice2x/hooks/audio/backends/wasapi/resample.cpp b/src/spice2x/hooks/audio/backends/wasapi/resample.cpp index c651069..cf8f20c 100644 --- a/src/spice2x/hooks/audio/backends/wasapi/resample.cpp +++ b/src/spice2x/hooks/audio/backends/wasapi/resample.cpp @@ -1,437 +1,437 @@ -#include "resample.h" - -#include -#include -#include -#include -#include - -#include - -#include "util/logging.h" - -#include "util.h" - -namespace hooks::audio { - - namespace { - - constexpr double PI = 3.14159265358979323846; - - // normalized sinc: sin(pi*x) / (pi*x), with the removable singularity at 0 filled in - inline double sinc(double x) { - if (x == 0.0) { - return 1.0; - } - const double px = PI * x; - return std::sin(px) / px; - } - - // Blackman window across the kernel radius; zero at +/- radius - inline double blackman(double x, double radius) { - const double n = (x + radius) / (2.0 * radius); - if (n <= 0.0 || n >= 1.0) { - return 0.0; - } - return 0.42 - 0.5 * std::cos(2.0 * PI * n) + 0.08 * std::cos(4.0 * PI * n); - } - } - - std::optional Resampler::resolve(const WAVEFORMATEX *game_format) { - if (game_format == nullptr || !RESAMPLE_RATE.has_value()) { - return std::nullopt; - } - if (game_format->nSamplesPerSec == 0 - || game_format->nSamplesPerSec == RESAMPLE_RATE.value()) { - return std::nullopt; - } - return RESAMPLE_RATE; - } - - void Resampler::setup(const WAVEFORMATEX *game_format, WAVEFORMATEXTENSIBLE *device_out, - uint32_t target_rate) { - this->enabled = true; - this->channels = game_format->nChannels; - this->bytes_per_sample = game_format->wBitsPerSample / 8; - this->game_frame_size = this->channels * this->bytes_per_sample; - - this->is_float = is_ieee_float(game_format); - - const bool supported = this->is_float - ? this->bytes_per_sample == 4 - : (this->bytes_per_sample >= 2 && this->bytes_per_sample <= 4); - if (!supported) { - log_fatal( - "audio::resample", - "unsupported sample format ({}-bit {}) for -resample", - game_format->wBitsPerSample, this->is_float ? "float" : "int"); - } - - this->src_rate = game_format->nSamplesPerSec; - this->dst_rate = target_rate; - - // anti-alias cutoff: full bandwidth when upsampling, scaled down when decimating - this->cutoff = std::min(1.0, (double) this->dst_rate / (double) this->src_rate); - this->half_taps = 16; - - // precompute the windowed-sinc kernel now that cutoff is known - this->build_kernel(); - - // prime the queue with half a window of silence so the first outputs have left history - this->in_queue.assign((size_t) this->half_taps * this->channels, 0.0f); - this->in_pos = this->half_taps; - - this->make_device_format(game_format, device_out, target_rate); - } - - void Resampler::make_device_format(const WAVEFORMATEX *game_format, - WAVEFORMATEXTENSIBLE *device_out, uint32_t target_rate) { - const size_t src_size = sizeof(WAVEFORMATEX) + game_format->cbSize; - - memset(device_out, 0, sizeof(WAVEFORMATEXTENSIBLE)); - memcpy(device_out, game_format, std::min(src_size, sizeof(WAVEFORMATEXTENSIBLE))); - - device_out->Format.nSamplesPerSec = target_rate; - device_out->Format.nAvgBytesPerSec = target_rate * device_out->Format.nBlockAlign; - } - - HRESULT Resampler::initialize(IAudioClient *real, AUDCLNT_SHAREMODE share_mode, - DWORD stream_flags, REFERENCE_TIME buffer_duration, REFERENCE_TIME periodicity, - const WAVEFORMATEX *device_format, LPCGUID session_guid) { - - // the resampler bypasses the OS mixer and talks to the device directly, so it only makes - // sense (and only works) for exclusive streams. shared streams are already resampled by - // the Windows audio engine, so refuse loudly rather than silently doing nothing. - if (share_mode != AUDCLNT_SHAREMODE_EXCLUSIVE) { - log_fatal("audio::resample", - "-resample requires WASAPI exclusive mode, but this stream is shared " - "(Windows already resamples shared streams)"); - } - - // record the pacing model. event-driven streams fill the whole device buffer each period - // (produce_exact); timer-driven streams poll padding and write variable partial chunks, so - // they drain the pending output to the device's free space each call (flush_timer). - this->event_driven = (stream_flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) != 0; - - return initialize_with_alignment_retry(real, "audio::resample", share_mode, stream_flags, - buffer_duration, periodicity, device_format, session_guid); - } - - UINT32 Resampler::frames_device_to_game(UINT32 device_frames) const { - if (this->dst_rate == 0) { - return device_frames; - } - // round down so the game never believes it has more room than the device can hold - return (UINT32) (((double) device_frames * this->src_rate) / this->dst_rate); - } - - UINT32 Resampler::padding_device_to_game(UINT32 device_padding) const { - if (this->dst_rate == 0) { - return device_padding; - } - // round up so the reported free space stays conservative - return (UINT32) std::ceil(((double) device_padding * this->src_rate) / this->dst_rate); - } - - HRESULT Resampler::get_buffer(UINT32 frames, BYTE **ppData) { - const size_t needed = (size_t) frames * this->game_frame_size; - if (this->scratch.size() < needed) { - this->scratch.resize(needed); - } - - *ppData = this->scratch.data(); - - return S_OK; - } - - void Resampler::enqueue_input(UINT32 frames, bool silent) { - const int bps = this->bytes_per_sample; - const int ch = this->channels; - const size_t base = this->in_queue.size(); - - this->in_queue.resize(base + (size_t) frames * ch); - - if (silent || bps <= 0 || ch <= 0) { - std::fill(this->in_queue.begin() + base, this->in_queue.end(), 0.0f); - return; - } - - const BYTE *src = this->scratch.data(); - for (UINT32 f = 0; f < frames; f++) { - for (int c = 0; c < ch; c++) { - const size_t s = (size_t) f * ch + c; - this->in_queue[base + s] = read_sample(src + s * bps, bps, this->is_float); - } - } - } - - void Resampler::build_kernel() { - const int taps = 2 * this->half_taps; - const int phases = this->kernel_phases; - const double cut = this->cutoff; - const double radius = (double) this->half_taps; - - // one extra row at frac == 1.0 so emit_frame can interpolate against row p + 1 safely - this->kernel_table.resize((size_t) (phases + 1) * taps); - - for (int p = 0; p <= phases; p++) { - const double frac = (double) p / (double) phases; - for (int k = 0; k < taps; k++) { - // tap k maps to input offset t = k - (half_taps - 1), matching emit_frame - const double x = frac - (double) (k - (this->half_taps - 1)); - this->kernel_table[(size_t) p * taps + k] = - (float) (cut * sinc(cut * x) * blackman(x, radius)); - } - } - } - - void Resampler::emit_frame() { - const int ch = this->channels; - const int radius = this->half_taps; - const int taps = 2 * radius; - const long avail = (long) (this->in_queue.size() / ch); - const long center = (long) std::floor(this->in_pos); - - // pick the two kernel rows bracketing this fractional position and the blend between them - const double frac = this->in_pos - (double) center; - const double fp = frac * (double) this->kernel_phases; - const int p0 = (int) fp; - const float blend = (float) (fp - (double) p0); - const float *row0 = &this->kernel_table[(size_t) p0 * taps]; - const float *row1 = &this->kernel_table[(size_t) (p0 + 1) * taps]; - - // base input index for tap 0 (t = -(radius - 1)) - const long base = center - (radius - 1); - - for (int c = 0; c < ch; c++) { - double acc = 0.0; - for (int k = 0; k < taps; k++) { - const long idx = base + k; - if (idx < 0 || idx >= avail) { - continue; - } - const float w = row0[k] + blend * (row1[k] - row0[k]); - acc += (double) this->in_queue[(size_t) idx * ch + c] * w; - } - this->out_float.push_back((float) acc); - } - } - - void Resampler::drop_consumed() { - const int ch = this->channels; - const long drop = (long) std::floor(this->in_pos) - this->half_taps; - if (drop > 0) { - const size_t drop_samples = (size_t) drop * ch; - if (drop_samples <= this->in_queue.size()) { - this->in_queue.erase(this->in_queue.begin(), - this->in_queue.begin() + drop_samples); - this->in_pos -= drop; - } - } - } - - UINT32 Resampler::produce_exact(UINT32 out_frames) { - const int ch = this->channels; - this->out_float.clear(); - if (ch <= 0 || out_frames == 0) { - return 0; - } - this->out_float.reserve((size_t) out_frames * ch); - - // resample ratio. drive it from the buffer size actually advertised to the game rather - // than the nominal src/dst ratio: GetBufferSize reports floor(dev_buf * src/dst) game - // frames, so the game only ever delivers that many input frames per device period. - // consuming at the nominal ratio would eat slightly more input than arrives on any device - // where dev_buf * src/dst is non-integer (e.g. 144 -> 132.3, floored to 132), slowly - // draining the queue until it underruns to permanent silence. using the advertised integer - // ratio keeps input and output exactly balanced; the resulting pitch error is below 0.3% - // and inaudible, and it collapses to the exact ratio when the division is integer (160 -> - // 147 stays 147/160 = 44100/48000). - const double step = (double) this->frames_device_to_game(this->device_buffer_frames) - / (double) this->device_buffer_frames; - - // input frames the block will touch: from in_pos through the right edge of the sinc kernel - // at the final output sample. if the queue is short of this, the kernel tail reads past the - // end and distorts every buffer, so buffer one extra block of input before the first output - // (emitting silence without consuming) to build a cushion the kernel can always reach into. - const long avail = (long) (this->in_queue.size() / ch); - const long need = (long) std::ceil(this->in_pos + step * (double) out_frames) - + this->half_taps; - - if (this->priming) { - if (avail < need + (long) out_frames) { - this->out_float.assign((size_t) out_frames * ch, 0.0f); - return out_frames; - } - this->priming = false; - } - - for (UINT32 o = 0; o < out_frames; o++) { - this->emit_frame(); - this->in_pos += step; - } - - this->drop_consumed(); - return out_frames; - } - - UINT32 Resampler::produce_variable() { - const int ch = this->channels; - if (ch <= 0) { - return 0; - } - - // input frames consumed per output frame. timer-driven streams write variable partial - // chunks, so produce however many output frames the currently queued input can fully - // support and leave the rest for the next call; this keeps input and output balanced at - // the exact src/dst ratio over time without depending on the device buffer size. - const double step = (double) this->src_rate / (double) this->dst_rate; - const long avail = (long) (this->in_queue.size() / ch); - - // emit only while the sinc kernel's right edge stays within the queued input. the kernel - // reaches from in_pos out to half_taps frames ahead, so stop once that would read past the - // end; the remaining input becomes the next block's lookahead. - UINT32 produced = 0; - while ((long) std::ceil(this->in_pos) + this->half_taps < avail) { - this->emit_frame(); - this->in_pos += step; - produced++; - } - - this->drop_consumed(); - return produced; - } - - void Resampler::write_output(BYTE *dst, UINT32 frames, float gain) const { - const int bps = this->bytes_per_sample; - const int ch = this->channels; - const size_t count = (size_t) frames * ch; - - for (size_t i = 0; i < count; i++) { - write_sample(dst + i * bps, bps, this->is_float, this->out_float[i] * gain); - } - } - - HRESULT Resampler::flush(IAudioRenderClient *real, IAudioClient *client, UINT32 frames, - DWORD flags, float boost) { - if (!this->enabled) { - return S_OK; - } - - // cache the device buffer size once - if (this->device_buffer_frames == 0) { - client->GetBufferSize(&this->device_buffer_frames); - } - if (this->device_buffer_frames == 0) { - return S_OK; - } - - const bool silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT) != 0; - this->enqueue_input(frames, silent); - - // confirm once that conversion actually started producing output - static std::once_flag active_printed; - std::call_once(active_printed, [this]() { - log_info("audio::resample", "resample active: {} Hz -> {} Hz ({} ch, {})", - this->src_rate, this->dst_rate, this->channels, - this->event_driven ? "event-driven" : "timer-driven"); - }); - - // the boost is applied here (inside write_output) rather than in the standard ReleaseBuffer - // path, so log it once for parity with that path's "volume boost active" line. - if (boost != 1.0f) { - static std::once_flag boost_printed; - std::call_once(boost_printed, [boost]() { - log_info("audio::resample", "volume boost active (resample): gain={}", boost); - }); - } - - return this->event_driven - ? this->flush_event(real, boost) - : this->flush_timer(real, client, boost); - } - - HRESULT Resampler::flush_event(IAudioRenderClient *real, float boost) { - - // event-driven exclusive streams must hand the device a full buffer every period and may - // not push partial counts. resample the whole input block into exactly the device buffer - // size. - const UINT32 produced = this->produce_exact(this->device_buffer_frames); - if (produced == 0) { - return S_OK; - } - - BYTE *dev = nullptr; - HRESULT ret = real->GetBuffer(produced, &dev); - if (FAILED(ret) || dev == nullptr) { - return ret; - } - - // mute the first few buffers to avoid a pop on stream start - float gain = boost; - if (this->buffers_to_mute > 0) { - gain = 0.0f; - this->buffers_to_mute--; - } - - this->write_output(dev, produced, gain); - - return real->ReleaseBuffer(produced, 0); - } - - HRESULT Resampler::flush_timer(IAudioRenderClient *real, IAudioClient *client, float boost) { - - // convert everything currently queued into the pending output FIFO (out_float). timer- - // driven games write variable partial chunks, so produce only what the queued input can - // fully support and keep the remainder for the next call. - this->produce_variable(); - - const int ch = this->channels; - if (ch <= 0) { - return S_OK; - } - - const UINT32 pending = (UINT32) (this->out_float.size() / ch); - if (pending == 0) { - return S_OK; - } - - // push as many frames as the device currently has free, keeping the rest queued for the - // next call. timer-driven games poll padding and write whenever there is room, so matching - // the device's free space here avoids overflowing the ring while staying device-paced. - UINT32 padding = 0; - if (FAILED(client->GetCurrentPadding(&padding))) { - return S_OK; - } - const UINT32 device_free = this->device_buffer_frames > padding - ? this->device_buffer_frames - padding - : 0; - if (device_free == 0) { - return S_OK; - } - - const UINT32 to_write = std::min(pending, device_free); - - BYTE *dev = nullptr; - HRESULT ret = real->GetBuffer(to_write, &dev); - if (FAILED(ret) || dev == nullptr) { - return ret; - } - - // mute the first few buffers to avoid a pop on stream start - float gain = boost; - if (this->buffers_to_mute > 0) { - gain = 0.0f; - this->buffers_to_mute--; - } - - this->write_output(dev, to_write, gain); - ret = real->ReleaseBuffer(to_write, 0); - - // drop the frames just written from the front of the pending FIFO - this->out_float.erase(this->out_float.begin(), - this->out_float.begin() + (size_t) to_write * ch); - - return ret; - } -} +#include "resample.h" + +#include +#include +#include +#include +#include + +#include + +#include "util/logging.h" + +#include "util.h" + +namespace hooks::audio { + + namespace { + + constexpr double PI = 3.14159265358979323846; + + // normalized sinc: sin(pi*x) / (pi*x), with the removable singularity at 0 filled in + inline double sinc(double x) { + if (x == 0.0) { + return 1.0; + } + const double px = PI * x; + return std::sin(px) / px; + } + + // Blackman window across the kernel radius; zero at +/- radius + inline double blackman(double x, double radius) { + const double n = (x + radius) / (2.0 * radius); + if (n <= 0.0 || n >= 1.0) { + return 0.0; + } + return 0.42 - 0.5 * std::cos(2.0 * PI * n) + 0.08 * std::cos(4.0 * PI * n); + } + } + + std::optional Resampler::resolve(const WAVEFORMATEX *game_format) { + if (game_format == nullptr || !RESAMPLE_RATE.has_value()) { + return std::nullopt; + } + if (game_format->nSamplesPerSec == 0 + || game_format->nSamplesPerSec == RESAMPLE_RATE.value()) { + return std::nullopt; + } + return RESAMPLE_RATE; + } + + void Resampler::setup(const WAVEFORMATEX *game_format, WAVEFORMATEXTENSIBLE *device_out, + uint32_t target_rate) { + this->enabled = true; + this->channels = game_format->nChannels; + this->bytes_per_sample = game_format->wBitsPerSample / 8; + this->game_frame_size = this->channels * this->bytes_per_sample; + + this->is_float = is_ieee_float(game_format); + + const bool supported = this->is_float + ? this->bytes_per_sample == 4 + : (this->bytes_per_sample >= 2 && this->bytes_per_sample <= 4); + if (!supported) { + log_fatal( + "audio::resample", + "unsupported sample format ({}-bit {}) for -resample", + game_format->wBitsPerSample, this->is_float ? "float" : "int"); + } + + this->src_rate = game_format->nSamplesPerSec; + this->dst_rate = target_rate; + + // anti-alias cutoff: full bandwidth when upsampling, scaled down when decimating + this->cutoff = std::min(1.0, (double) this->dst_rate / (double) this->src_rate); + this->half_taps = 16; + + // precompute the windowed-sinc kernel now that cutoff is known + this->build_kernel(); + + // prime the queue with half a window of silence so the first outputs have left history + this->in_queue.assign((size_t) this->half_taps * this->channels, 0.0f); + this->in_pos = this->half_taps; + + this->make_device_format(game_format, device_out, target_rate); + } + + void Resampler::make_device_format(const WAVEFORMATEX *game_format, + WAVEFORMATEXTENSIBLE *device_out, uint32_t target_rate) { + const size_t src_size = sizeof(WAVEFORMATEX) + game_format->cbSize; + + memset(device_out, 0, sizeof(WAVEFORMATEXTENSIBLE)); + memcpy(device_out, game_format, std::min(src_size, sizeof(WAVEFORMATEXTENSIBLE))); + + device_out->Format.nSamplesPerSec = target_rate; + device_out->Format.nAvgBytesPerSec = target_rate * device_out->Format.nBlockAlign; + } + + HRESULT Resampler::initialize(IAudioClient *real, AUDCLNT_SHAREMODE share_mode, + DWORD stream_flags, REFERENCE_TIME buffer_duration, REFERENCE_TIME periodicity, + const WAVEFORMATEX *device_format, LPCGUID session_guid) { + + // the resampler bypasses the OS mixer and talks to the device directly, so it only makes + // sense (and only works) for exclusive streams. shared streams are already resampled by + // the Windows audio engine, so refuse loudly rather than silently doing nothing. + if (share_mode != AUDCLNT_SHAREMODE_EXCLUSIVE) { + log_fatal("audio::resample", + "-resample requires WASAPI exclusive mode, but this stream is shared " + "(Windows already resamples shared streams)"); + } + + // record the pacing model. event-driven streams fill the whole device buffer each period + // (produce_exact); timer-driven streams poll padding and write variable partial chunks, so + // they drain the pending output to the device's free space each call (flush_timer). + this->event_driven = (stream_flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) != 0; + + return initialize_with_alignment_retry(real, "audio::resample", share_mode, stream_flags, + buffer_duration, periodicity, device_format, session_guid); + } + + UINT32 Resampler::frames_device_to_game(UINT32 device_frames) const { + if (this->dst_rate == 0) { + return device_frames; + } + // round down so the game never believes it has more room than the device can hold + return (UINT32) (((double) device_frames * this->src_rate) / this->dst_rate); + } + + UINT32 Resampler::padding_device_to_game(UINT32 device_padding) const { + if (this->dst_rate == 0) { + return device_padding; + } + // round up so the reported free space stays conservative + return (UINT32) std::ceil(((double) device_padding * this->src_rate) / this->dst_rate); + } + + HRESULT Resampler::get_buffer(UINT32 frames, BYTE **ppData) { + const size_t needed = (size_t) frames * this->game_frame_size; + if (this->scratch.size() < needed) { + this->scratch.resize(needed); + } + + *ppData = this->scratch.data(); + + return S_OK; + } + + void Resampler::enqueue_input(UINT32 frames, bool silent) { + const int bps = this->bytes_per_sample; + const int ch = this->channels; + const size_t base = this->in_queue.size(); + + this->in_queue.resize(base + (size_t) frames * ch); + + if (silent || bps <= 0 || ch <= 0) { + std::fill(this->in_queue.begin() + base, this->in_queue.end(), 0.0f); + return; + } + + const BYTE *src = this->scratch.data(); + for (UINT32 f = 0; f < frames; f++) { + for (int c = 0; c < ch; c++) { + const size_t s = (size_t) f * ch + c; + this->in_queue[base + s] = read_sample(src + s * bps, bps, this->is_float); + } + } + } + + void Resampler::build_kernel() { + const int taps = 2 * this->half_taps; + const int phases = this->kernel_phases; + const double cut = this->cutoff; + const double radius = (double) this->half_taps; + + // one extra row at frac == 1.0 so emit_frame can interpolate against row p + 1 safely + this->kernel_table.resize((size_t) (phases + 1) * taps); + + for (int p = 0; p <= phases; p++) { + const double frac = (double) p / (double) phases; + for (int k = 0; k < taps; k++) { + // tap k maps to input offset t = k - (half_taps - 1), matching emit_frame + const double x = frac - (double) (k - (this->half_taps - 1)); + this->kernel_table[(size_t) p * taps + k] = + (float) (cut * sinc(cut * x) * blackman(x, radius)); + } + } + } + + void Resampler::emit_frame() { + const int ch = this->channels; + const int radius = this->half_taps; + const int taps = 2 * radius; + const long avail = (long) (this->in_queue.size() / ch); + const long center = (long) std::floor(this->in_pos); + + // pick the two kernel rows bracketing this fractional position and the blend between them + const double frac = this->in_pos - (double) center; + const double fp = frac * (double) this->kernel_phases; + const int p0 = (int) fp; + const float blend = (float) (fp - (double) p0); + const float *row0 = &this->kernel_table[(size_t) p0 * taps]; + const float *row1 = &this->kernel_table[(size_t) (p0 + 1) * taps]; + + // base input index for tap 0 (t = -(radius - 1)) + const long base = center - (radius - 1); + + for (int c = 0; c < ch; c++) { + double acc = 0.0; + for (int k = 0; k < taps; k++) { + const long idx = base + k; + if (idx < 0 || idx >= avail) { + continue; + } + const float w = row0[k] + blend * (row1[k] - row0[k]); + acc += (double) this->in_queue[(size_t) idx * ch + c] * w; + } + this->out_float.push_back((float) acc); + } + } + + void Resampler::drop_consumed() { + const int ch = this->channels; + const long drop = (long) std::floor(this->in_pos) - this->half_taps; + if (drop > 0) { + const size_t drop_samples = (size_t) drop * ch; + if (drop_samples <= this->in_queue.size()) { + this->in_queue.erase(this->in_queue.begin(), + this->in_queue.begin() + drop_samples); + this->in_pos -= drop; + } + } + } + + UINT32 Resampler::produce_exact(UINT32 out_frames) { + const int ch = this->channels; + this->out_float.clear(); + if (ch <= 0 || out_frames == 0) { + return 0; + } + this->out_float.reserve((size_t) out_frames * ch); + + // resample ratio. drive it from the buffer size actually advertised to the game rather + // than the nominal src/dst ratio: GetBufferSize reports floor(dev_buf * src/dst) game + // frames, so the game only ever delivers that many input frames per device period. + // consuming at the nominal ratio would eat slightly more input than arrives on any device + // where dev_buf * src/dst is non-integer (e.g. 144 -> 132.3, floored to 132), slowly + // draining the queue until it underruns to permanent silence. using the advertised integer + // ratio keeps input and output exactly balanced; the resulting pitch error is below 0.3% + // and inaudible, and it collapses to the exact ratio when the division is integer (160 -> + // 147 stays 147/160 = 44100/48000). + const double step = (double) this->frames_device_to_game(this->device_buffer_frames) + / (double) this->device_buffer_frames; + + // input frames the block will touch: from in_pos through the right edge of the sinc kernel + // at the final output sample. if the queue is short of this, the kernel tail reads past the + // end and distorts every buffer, so buffer one extra block of input before the first output + // (emitting silence without consuming) to build a cushion the kernel can always reach into. + const long avail = (long) (this->in_queue.size() / ch); + const long need = (long) std::ceil(this->in_pos + step * (double) out_frames) + + this->half_taps; + + if (this->priming) { + if (avail < need + (long) out_frames) { + this->out_float.assign((size_t) out_frames * ch, 0.0f); + return out_frames; + } + this->priming = false; + } + + for (UINT32 o = 0; o < out_frames; o++) { + this->emit_frame(); + this->in_pos += step; + } + + this->drop_consumed(); + return out_frames; + } + + UINT32 Resampler::produce_variable() { + const int ch = this->channels; + if (ch <= 0) { + return 0; + } + + // input frames consumed per output frame. timer-driven streams write variable partial + // chunks, so produce however many output frames the currently queued input can fully + // support and leave the rest for the next call; this keeps input and output balanced at + // the exact src/dst ratio over time without depending on the device buffer size. + const double step = (double) this->src_rate / (double) this->dst_rate; + const long avail = (long) (this->in_queue.size() / ch); + + // emit only while the sinc kernel's right edge stays within the queued input. the kernel + // reaches from in_pos out to half_taps frames ahead, so stop once that would read past the + // end; the remaining input becomes the next block's lookahead. + UINT32 produced = 0; + while ((long) std::ceil(this->in_pos) + this->half_taps < avail) { + this->emit_frame(); + this->in_pos += step; + produced++; + } + + this->drop_consumed(); + return produced; + } + + void Resampler::write_output(BYTE *dst, UINT32 frames, float gain) const { + const int bps = this->bytes_per_sample; + const int ch = this->channels; + const size_t count = (size_t) frames * ch; + + for (size_t i = 0; i < count; i++) { + write_sample(dst + i * bps, bps, this->is_float, this->out_float[i] * gain); + } + } + + HRESULT Resampler::flush(IAudioRenderClient *real, IAudioClient *client, UINT32 frames, + DWORD flags, float boost) { + if (!this->enabled) { + return S_OK; + } + + // cache the device buffer size once + if (this->device_buffer_frames == 0) { + client->GetBufferSize(&this->device_buffer_frames); + } + if (this->device_buffer_frames == 0) { + return S_OK; + } + + const bool silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT) != 0; + this->enqueue_input(frames, silent); + + // confirm once that conversion actually started producing output + static std::once_flag active_printed; + std::call_once(active_printed, [this]() { + log_info("audio::resample", "resample active: {} Hz -> {} Hz ({} ch, {})", + this->src_rate, this->dst_rate, this->channels, + this->event_driven ? "event-driven" : "timer-driven"); + }); + + // the boost is applied here (inside write_output) rather than in the standard ReleaseBuffer + // path, so log it once for parity with that path's "volume boost active" line. + if (boost != 1.0f) { + static std::once_flag boost_printed; + std::call_once(boost_printed, [boost]() { + log_info("audio::resample", "volume boost active (resample): gain={}", boost); + }); + } + + return this->event_driven + ? this->flush_event(real, boost) + : this->flush_timer(real, client, boost); + } + + HRESULT Resampler::flush_event(IAudioRenderClient *real, float boost) { + + // event-driven exclusive streams must hand the device a full buffer every period and may + // not push partial counts. resample the whole input block into exactly the device buffer + // size. + const UINT32 produced = this->produce_exact(this->device_buffer_frames); + if (produced == 0) { + return S_OK; + } + + BYTE *dev = nullptr; + HRESULT ret = real->GetBuffer(produced, &dev); + if (FAILED(ret) || dev == nullptr) { + return ret; + } + + // mute the first few buffers to avoid a pop on stream start + float gain = boost; + if (this->buffers_to_mute > 0) { + gain = 0.0f; + this->buffers_to_mute--; + } + + this->write_output(dev, produced, gain); + + return real->ReleaseBuffer(produced, 0); + } + + HRESULT Resampler::flush_timer(IAudioRenderClient *real, IAudioClient *client, float boost) { + + // convert everything currently queued into the pending output FIFO (out_float). timer- + // driven games write variable partial chunks, so produce only what the queued input can + // fully support and keep the remainder for the next call. + this->produce_variable(); + + const int ch = this->channels; + if (ch <= 0) { + return S_OK; + } + + const UINT32 pending = (UINT32) (this->out_float.size() / ch); + if (pending == 0) { + return S_OK; + } + + // push as many frames as the device currently has free, keeping the rest queued for the + // next call. timer-driven games poll padding and write whenever there is room, so matching + // the device's free space here avoids overflowing the ring while staying device-paced. + UINT32 padding = 0; + if (FAILED(client->GetCurrentPadding(&padding))) { + return S_OK; + } + const UINT32 device_free = this->device_buffer_frames > padding + ? this->device_buffer_frames - padding + : 0; + if (device_free == 0) { + return S_OK; + } + + const UINT32 to_write = std::min(pending, device_free); + + BYTE *dev = nullptr; + HRESULT ret = real->GetBuffer(to_write, &dev); + if (FAILED(ret) || dev == nullptr) { + return ret; + } + + // mute the first few buffers to avoid a pop on stream start + float gain = boost; + if (this->buffers_to_mute > 0) { + gain = 0.0f; + this->buffers_to_mute--; + } + + this->write_output(dev, to_write, gain); + ret = real->ReleaseBuffer(to_write, 0); + + // drop the frames just written from the front of the pending FIFO + this->out_float.erase(this->out_float.begin(), + this->out_float.begin() + (size_t) to_write * ch); + + return ret; + } +} diff --git a/src/spice2x/hooks/audio/backends/wasapi/resample.h b/src/spice2x/hooks/audio/backends/wasapi/resample.h index 314ae26..32bb2d7 100644 --- a/src/spice2x/hooks/audio/backends/wasapi/resample.h +++ b/src/spice2x/hooks/audio/backends/wasapi/resample.h @@ -1,149 +1,149 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include - -#include "hooks/audio/audio.h" - -struct IAudioClient; -struct IAudioRenderClient; - -namespace hooks::audio { - - // Streaming sample-rate converter for the WASAPI render path. The real device is opened at the - // target rate while the game keeps writing its native-rate audio into a scratch buffer; on - // release that buffer is converted with a windowed-sinc kernel and pushed to the device. - // Channel count and sample format are preserved; only the sample rate changes. - // - // Frame counts differ between the two rates, so unlike the per-frame downmix this is stateful: - // a fractional read position and a window of input history carry across ReleaseBuffer calls, - // and the device buffer is only filled up to the space the device currently has free. - struct Resampler { - - // whether the resampler is active for the current stream - bool enabled = false; - - // whether the stream is event-driven (AUDCLNT_STREAMFLAGS_EVENTCALLBACK). timer-driven - // streams instead poll padding and write variable partial chunks, so they drain the - // pending output to the device's free space rather than pushing a full buffer per period. - bool event_driven = true; - - // decide whether the stream should be resampled and to which rate. returns the target rate - // when RESAMPLE_RATE is set and differs from the game's rate, otherwise nullopt. - static std::optional resolve(const WAVEFORMATEX *game_format); - - // enable resampling for game_format and fill device_out with the equivalent format at the - // target rate to open the real device with. - void setup(const WAVEFORMATEX *game_format, WAVEFORMATEXTENSIBLE *device_out, - uint32_t target_rate); - - // build the device format equivalent to game_format at target_rate (same channels/depth). - static void make_device_format(const WAVEFORMATEX *game_format, - WAVEFORMATEXTENSIBLE *device_out, uint32_t target_rate); - - // initialize the real device at the target rate, performing the standard WASAPI buffer - // realignment retry on AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED. - HRESULT initialize(IAudioClient *real, AUDCLNT_SHAREMODE share_mode, DWORD stream_flags, - REFERENCE_TIME buffer_duration, REFERENCE_TIME periodicity, - const WAVEFORMATEX *device_format, LPCGUID session_guid); - - // translate a device-rate frame count to the equivalent game-rate count, so the buffer-size - // and padding values reported to the game stay paced at the game's native rate. - UINT32 frames_device_to_game(UINT32 device_frames) const; - UINT32 padding_device_to_game(UINT32 device_padding) const; - - // hand the game a scratch buffer sized for `frames` of its native format to write into. - HRESULT get_buffer(UINT32 frames, BYTE **ppData); - - // pointer to the input scratch (sized by get_buffer). when chained after the downmix, the - // downmix writes its stereo output here for the resampler to consume on the next flush. - BYTE *input_data() { return this->scratch.data(); } - - // convert the `frames` the game wrote and push output to the real render client. `boost` - // is applied to the converted output. event-driven streams fill exactly one device buffer - // per period; timer-driven streams push as many converted frames as the device has free. - HRESULT flush(IAudioRenderClient *real, IAudioClient *client, UINT32 frames, DWORD flags, - float boost); - - private: - - // append `frames` of the scratch buffer (native format), or silence, to the input queue - void enqueue_input(UINT32 frames, bool silent); - - // event-driven path: produce exactly one full device buffer and push it. - HRESULT flush_event(IAudioRenderClient *real, float boost); - - // timer-driven path: convert all queued input into the pending output FIFO, then push as - // many frames as the device currently has free, keeping the remainder for the next call. - HRESULT flush_timer(IAudioRenderClient *real, IAudioClient *client, float boost); - - // produce exactly out_frames output frames using the fixed src/dst ratio. event-driven - // exclusive streams must fill the whole device buffer every period; a small input cushion - // is buffered first (see priming) so the sinc kernel always has lookahead. - UINT32 produce_exact(UINT32 out_frames); - - // convert all input the kernel can fully support into the pending output FIFO (out_float), - // appending without clearing. returns the number of frames produced. used by the - // timer-driven path where output is drained to the device in device-paced chunks. - UINT32 produce_variable(); - - // convolve the windowed-sinc kernel at the current in_pos and append the resulting frame - // (one sample per channel) to out_float - void emit_frame(); - - // precompute the windowed-sinc kernel sampled at kernel_phases sub-sample positions, so - // emit_frame is a table lookup instead of recomputing sin/cos per tap (which is far too - // expensive to run per sample on the audio callback thread and causes underrun crackle). - void build_kernel(); - - // drop input frames that in_pos has advanced past, keeping a window of history for the - // next block's left context - void drop_consumed(); - - // convert the first `frames` of out_float to the device format, scaled by `gain` - void write_output(BYTE *dst, UINT32 frames, float gain) const; - - // sample format of the stream - int channels = 0; - int bytes_per_sample = 0; - bool is_float = false; - int game_frame_size = 0; - - uint32_t src_rate = 0; - uint32_t dst_rate = 0; - - // sinc low-pass cutoff (1.0 when upsampling, dst/src when downsampling) and window radius - double cutoff = 1.0; - int half_taps = 16; - - // precomputed kernel: (kernel_phases + 1) rows of 2*half_taps weights, indexed by the - // fractional sample position (linearly interpolated between adjacent rows in emit_frame) - std::vector kernel_table; - int kernel_phases = 1024; - - // interleaved float input queue and the fractional read position within it (in frames) - std::vector in_queue; - double in_pos = 0.0; - - // emit silence until a full block of input lookahead has accumulated, so the sinc kernel - // never reads past the end of the queue (which would distort the tail of every buffer) - bool priming = true; - - // interleaved float scratch for produced output - std::vector out_float; - - // buffer the game writes its native-rate audio into between get_buffer / flush - std::vector scratch; - - // cached device buffer size (frames); a full buffer is produced every period - UINT32 device_buffer_frames = 0; - - // leading buffers to silence to avoid a pop on stream start - int buffers_to_mute = 16; - }; -} +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include "hooks/audio/audio.h" + +struct IAudioClient; +struct IAudioRenderClient; + +namespace hooks::audio { + + // Streaming sample-rate converter for the WASAPI render path. The real device is opened at the + // target rate while the game keeps writing its native-rate audio into a scratch buffer; on + // release that buffer is converted with a windowed-sinc kernel and pushed to the device. + // Channel count and sample format are preserved; only the sample rate changes. + // + // Frame counts differ between the two rates, so unlike the per-frame downmix this is stateful: + // a fractional read position and a window of input history carry across ReleaseBuffer calls, + // and the device buffer is only filled up to the space the device currently has free. + struct Resampler { + + // whether the resampler is active for the current stream + bool enabled = false; + + // whether the stream is event-driven (AUDCLNT_STREAMFLAGS_EVENTCALLBACK). timer-driven + // streams instead poll padding and write variable partial chunks, so they drain the + // pending output to the device's free space rather than pushing a full buffer per period. + bool event_driven = true; + + // decide whether the stream should be resampled and to which rate. returns the target rate + // when RESAMPLE_RATE is set and differs from the game's rate, otherwise nullopt. + static std::optional resolve(const WAVEFORMATEX *game_format); + + // enable resampling for game_format and fill device_out with the equivalent format at the + // target rate to open the real device with. + void setup(const WAVEFORMATEX *game_format, WAVEFORMATEXTENSIBLE *device_out, + uint32_t target_rate); + + // build the device format equivalent to game_format at target_rate (same channels/depth). + static void make_device_format(const WAVEFORMATEX *game_format, + WAVEFORMATEXTENSIBLE *device_out, uint32_t target_rate); + + // initialize the real device at the target rate, performing the standard WASAPI buffer + // realignment retry on AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED. + HRESULT initialize(IAudioClient *real, AUDCLNT_SHAREMODE share_mode, DWORD stream_flags, + REFERENCE_TIME buffer_duration, REFERENCE_TIME periodicity, + const WAVEFORMATEX *device_format, LPCGUID session_guid); + + // translate a device-rate frame count to the equivalent game-rate count, so the buffer-size + // and padding values reported to the game stay paced at the game's native rate. + UINT32 frames_device_to_game(UINT32 device_frames) const; + UINT32 padding_device_to_game(UINT32 device_padding) const; + + // hand the game a scratch buffer sized for `frames` of its native format to write into. + HRESULT get_buffer(UINT32 frames, BYTE **ppData); + + // pointer to the input scratch (sized by get_buffer). when chained after the downmix, the + // downmix writes its stereo output here for the resampler to consume on the next flush. + BYTE *input_data() { return this->scratch.data(); } + + // convert the `frames` the game wrote and push output to the real render client. `boost` + // is applied to the converted output. event-driven streams fill exactly one device buffer + // per period; timer-driven streams push as many converted frames as the device has free. + HRESULT flush(IAudioRenderClient *real, IAudioClient *client, UINT32 frames, DWORD flags, + float boost); + + private: + + // append `frames` of the scratch buffer (native format), or silence, to the input queue + void enqueue_input(UINT32 frames, bool silent); + + // event-driven path: produce exactly one full device buffer and push it. + HRESULT flush_event(IAudioRenderClient *real, float boost); + + // timer-driven path: convert all queued input into the pending output FIFO, then push as + // many frames as the device currently has free, keeping the remainder for the next call. + HRESULT flush_timer(IAudioRenderClient *real, IAudioClient *client, float boost); + + // produce exactly out_frames output frames using the fixed src/dst ratio. event-driven + // exclusive streams must fill the whole device buffer every period; a small input cushion + // is buffered first (see priming) so the sinc kernel always has lookahead. + UINT32 produce_exact(UINT32 out_frames); + + // convert all input the kernel can fully support into the pending output FIFO (out_float), + // appending without clearing. returns the number of frames produced. used by the + // timer-driven path where output is drained to the device in device-paced chunks. + UINT32 produce_variable(); + + // convolve the windowed-sinc kernel at the current in_pos and append the resulting frame + // (one sample per channel) to out_float + void emit_frame(); + + // precompute the windowed-sinc kernel sampled at kernel_phases sub-sample positions, so + // emit_frame is a table lookup instead of recomputing sin/cos per tap (which is far too + // expensive to run per sample on the audio callback thread and causes underrun crackle). + void build_kernel(); + + // drop input frames that in_pos has advanced past, keeping a window of history for the + // next block's left context + void drop_consumed(); + + // convert the first `frames` of out_float to the device format, scaled by `gain` + void write_output(BYTE *dst, UINT32 frames, float gain) const; + + // sample format of the stream + int channels = 0; + int bytes_per_sample = 0; + bool is_float = false; + int game_frame_size = 0; + + uint32_t src_rate = 0; + uint32_t dst_rate = 0; + + // sinc low-pass cutoff (1.0 when upsampling, dst/src when downsampling) and window radius + double cutoff = 1.0; + int half_taps = 16; + + // precomputed kernel: (kernel_phases + 1) rows of 2*half_taps weights, indexed by the + // fractional sample position (linearly interpolated between adjacent rows in emit_frame) + std::vector kernel_table; + int kernel_phases = 1024; + + // interleaved float input queue and the fractional read position within it (in frames) + std::vector in_queue; + double in_pos = 0.0; + + // emit silence until a full block of input lookahead has accumulated, so the sinc kernel + // never reads past the end of the queue (which would distort the tail of every buffer) + bool priming = true; + + // interleaved float scratch for produced output + std::vector out_float; + + // buffer the game writes its native-rate audio into between get_buffer / flush + std::vector scratch; + + // cached device buffer size (frames); a full buffer is produced every period + UINT32 device_buffer_frames = 0; + + // leading buffers to silence to avoid a pop on stream start + int buffers_to_mute = 16; + }; +} diff --git a/src/spice2x/hooks/audio/backends/wasapi/shared.cpp b/src/spice2x/hooks/audio/backends/wasapi/shared.cpp index 17a0a4b..a688cd4 100644 --- a/src/spice2x/hooks/audio/backends/wasapi/shared.cpp +++ b/src/spice2x/hooks/audio/backends/wasapi/shared.cpp @@ -1,187 +1,187 @@ -#include "shared.h" - -#include - -#include - -#include "hooks/audio/audio.h" -#include "util/logging.h" - -#include "util.h" -#include "defs.h" - -namespace hooks::audio { - - // whether the engine's PCM converter can handle this format. PCM / float only; non-PCM - // bitstream (AC-3 / DTS passthrough) must be left alone. - static bool is_pcm_or_float(const WAVEFORMATEX *format) { - if (format == nullptr) { - return false; - } - - switch (format->wFormatTag) { - case WAVE_FORMAT_PCM: - case WAVE_FORMAT_IEEE_FLOAT: - return true; - case WAVE_FORMAT_EXTENSIBLE: { - - // SubFormat is only valid when the extra-bytes block is large enough - if (format->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) { - return false; - } - const auto *ext = reinterpret_cast(format); - return ext->SubFormat == GUID_KSDATAFORMAT_SUBTYPE_PCM - || ext->SubFormat == GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } - default: - return false; - } - } - - bool SharedRedirect::wants(AUDCLNT_SHAREMODE share_mode, const WAVEFORMATEX *format) { - - // only redirect PCM / float exclusive streams: the engine converter (AUTOCONVERTPCM) can - // handle those, but non-PCM bitstream (AC-3 / DTS passthrough) would fail in shared mode, - // so leave it in exclusive untouched. - return hooks::audio::WASAPI_COMPATIBILITY_MODE - && share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE - && is_pcm_or_float(format); - } - - void SharedRedirect::apply(AUDCLNT_SHAREMODE *share_mode, DWORD *stream_flags, - REFERENCE_TIME *periodicity) { - - // shared mode requires periodicity == 0; AUTOCONVERTPCM lets the engine accept the game's - // native format (else shared Initialize returns AUDCLNT_E_UNSUPPORTED_FORMAT). - log_info("audio::wasapi", "redirecting exclusive WASAPI to shared mode"); - *share_mode = AUDCLNT_SHAREMODE_SHARED; - *periodicity = 0; - *stream_flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; - this->redirected_from_exclusive = true; - } - - UINT32 SharedRedirect::clamp_buffer_size(IAudioClient *real, uint32_t sample_rate, - UINT32 device_frames) { - if (!this->redirected_from_exclusive || real == nullptr || sample_rate == 0 || device_frames == 0) { - this->reported_frames = device_frames; - return device_frames; - } - - // GetDevicePeriod returns REFERENCE_TIME units (100 ns), 10^7 per second, so - // period_frames = period * sample_rate / 10^7. - REFERENCE_TIME period = 0; - if (SUCCEEDED(real->GetDevicePeriod(&period, nullptr)) && period > 0) { - const UINT32 period_frames = (UINT32) ((period * sample_rate) / 10000000); - if (period_frames > 0 && period_frames < device_frames) { - this->reported_frames = period_frames; - return period_frames; - } - } - - this->reported_frames = device_frames; - return device_frames; - } - - void SharedRedirect::enable_bridge(int frame_bytes) { - if (!this->redirected_from_exclusive || frame_bytes <= 0) { - return; - } - - this->frame_bytes = frame_bytes; - this->device_buffer_frames = 0; - this->fifo.clear(); - - log_info("audio::wasapi", "shared-mode buffer bridge enabled (frame size {} bytes)", - frame_bytes); - } - - BYTE *SharedRedirect::begin_write(UINT32 frames) { - // reserve space at the FIFO tail and let the game write straight into it - no scratch copy. - this->pending_write_offset = this->fifo.size(); - this->fifo.resize(this->pending_write_offset + (size_t) frames * this->frame_bytes); - - return this->fifo.data() + this->pending_write_offset; - } - - void SharedRedirect::commit_write(UINT32 frames, bool silent) { - // trim the tail reservation to the frames actually written; zero it in place if silent. - const size_t end = this->pending_write_offset + (size_t) frames * this->frame_bytes; - if (silent) { - std::fill(this->fifo.begin() + this->pending_write_offset, - this->fifo.begin() + end, (BYTE) 0); - } - this->fifo.resize(end); - } - - UINT32 SharedRedirect::pending_frames() const { - if (this->frame_bytes <= 0) { - return 0; - } - - return (UINT32) (this->fifo.size() / this->frame_bytes); - } - - UINT32 SharedRedirect::virtual_padding() const { - const UINT32 pending = this->pending_frames(); - return this->reported_frames > 0 ? std::min(pending, this->reported_frames) : pending; - } - - HRESULT SharedRedirect::drain(IAudioRenderClient *real, IAudioClient *client, - const WAVEFORMATEXTENSIBLE &device_format, float boost) { - if (!this->bridge_enabled()) { - return S_OK; - } - - // cache the real device buffer size once; it is fixed for the life of the stream. - if (this->device_buffer_frames == 0) { - if (FAILED(client->GetBufferSize(&this->device_buffer_frames)) - || this->device_buffer_frames == 0) { - return S_OK; - } - } - - const UINT32 pending = this->pending_frames(); - if (pending == 0) { - return S_OK; - } - - // push only as many frames as the device currently has free, keeping the rest queued. this - // self-paces to the engine's real consumption so a full-buffer write never overflows. - UINT32 padding = 0; - if (FAILED(client->GetCurrentPadding(&padding))) { - return S_OK; - } - const UINT32 device_free = this->device_buffer_frames > padding - ? this->device_buffer_frames - padding - : 0; - if (device_free == 0) { - return S_OK; - } - - const UINT32 to_write = std::min(pending, device_free); - - BYTE *dev = nullptr; - HRESULT ret = real->GetBuffer(to_write, &dev); - if (FAILED(ret) || dev == nullptr) { - return ret; - } - - const size_t bytes = (size_t) to_write * this->frame_bytes; - std::copy(this->fifo.begin(), this->fifo.begin() + bytes, dev); - - // mute the first few buffers to avoid a startup pop, then apply the volume boost. - if (this->buffers_to_mute > 0) { - std::fill(dev, dev + bytes, (BYTE) 0); - this->buffers_to_mute--; - } else if (boost != 1.0f) { - apply_gain(dev, to_write, device_format, boost); - } - - ret = real->ReleaseBuffer(to_write, 0); - - // drop the frames just handed to the device from the front of the FIFO. - this->fifo.erase(this->fifo.begin(), this->fifo.begin() + bytes); - - return ret; - } -} +#include "shared.h" + +#include + +#include + +#include "hooks/audio/audio.h" +#include "util/logging.h" + +#include "util.h" +#include "defs.h" + +namespace hooks::audio { + + // whether the engine's PCM converter can handle this format. PCM / float only; non-PCM + // bitstream (AC-3 / DTS passthrough) must be left alone. + static bool is_pcm_or_float(const WAVEFORMATEX *format) { + if (format == nullptr) { + return false; + } + + switch (format->wFormatTag) { + case WAVE_FORMAT_PCM: + case WAVE_FORMAT_IEEE_FLOAT: + return true; + case WAVE_FORMAT_EXTENSIBLE: { + + // SubFormat is only valid when the extra-bytes block is large enough + if (format->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) { + return false; + } + const auto *ext = reinterpret_cast(format); + return ext->SubFormat == GUID_KSDATAFORMAT_SUBTYPE_PCM + || ext->SubFormat == GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } + default: + return false; + } + } + + bool SharedRedirect::wants(AUDCLNT_SHAREMODE share_mode, const WAVEFORMATEX *format) { + + // only redirect PCM / float exclusive streams: the engine converter (AUTOCONVERTPCM) can + // handle those, but non-PCM bitstream (AC-3 / DTS passthrough) would fail in shared mode, + // so leave it in exclusive untouched. + return hooks::audio::WASAPI_COMPATIBILITY_MODE + && share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE + && is_pcm_or_float(format); + } + + void SharedRedirect::apply(AUDCLNT_SHAREMODE *share_mode, DWORD *stream_flags, + REFERENCE_TIME *periodicity) { + + // shared mode requires periodicity == 0; AUTOCONVERTPCM lets the engine accept the game's + // native format (else shared Initialize returns AUDCLNT_E_UNSUPPORTED_FORMAT). + log_info("audio::wasapi", "redirecting exclusive WASAPI to shared mode"); + *share_mode = AUDCLNT_SHAREMODE_SHARED; + *periodicity = 0; + *stream_flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; + this->redirected_from_exclusive = true; + } + + UINT32 SharedRedirect::clamp_buffer_size(IAudioClient *real, uint32_t sample_rate, + UINT32 device_frames) { + if (!this->redirected_from_exclusive || real == nullptr || sample_rate == 0 || device_frames == 0) { + this->reported_frames = device_frames; + return device_frames; + } + + // GetDevicePeriod returns REFERENCE_TIME units (100 ns), 10^7 per second, so + // period_frames = period * sample_rate / 10^7. + REFERENCE_TIME period = 0; + if (SUCCEEDED(real->GetDevicePeriod(&period, nullptr)) && period > 0) { + const UINT32 period_frames = (UINT32) ((period * sample_rate) / 10000000); + if (period_frames > 0 && period_frames < device_frames) { + this->reported_frames = period_frames; + return period_frames; + } + } + + this->reported_frames = device_frames; + return device_frames; + } + + void SharedRedirect::enable_bridge(int frame_bytes) { + if (!this->redirected_from_exclusive || frame_bytes <= 0) { + return; + } + + this->frame_bytes = frame_bytes; + this->device_buffer_frames = 0; + this->fifo.clear(); + + log_info("audio::wasapi", "shared-mode buffer bridge enabled (frame size {} bytes)", + frame_bytes); + } + + BYTE *SharedRedirect::begin_write(UINT32 frames) { + // reserve space at the FIFO tail and let the game write straight into it - no scratch copy. + this->pending_write_offset = this->fifo.size(); + this->fifo.resize(this->pending_write_offset + (size_t) frames * this->frame_bytes); + + return this->fifo.data() + this->pending_write_offset; + } + + void SharedRedirect::commit_write(UINT32 frames, bool silent) { + // trim the tail reservation to the frames actually written; zero it in place if silent. + const size_t end = this->pending_write_offset + (size_t) frames * this->frame_bytes; + if (silent) { + std::fill(this->fifo.begin() + this->pending_write_offset, + this->fifo.begin() + end, (BYTE) 0); + } + this->fifo.resize(end); + } + + UINT32 SharedRedirect::pending_frames() const { + if (this->frame_bytes <= 0) { + return 0; + } + + return (UINT32) (this->fifo.size() / this->frame_bytes); + } + + UINT32 SharedRedirect::virtual_padding() const { + const UINT32 pending = this->pending_frames(); + return this->reported_frames > 0 ? std::min(pending, this->reported_frames) : pending; + } + + HRESULT SharedRedirect::drain(IAudioRenderClient *real, IAudioClient *client, + const WAVEFORMATEXTENSIBLE &device_format, float boost) { + if (!this->bridge_enabled()) { + return S_OK; + } + + // cache the real device buffer size once; it is fixed for the life of the stream. + if (this->device_buffer_frames == 0) { + if (FAILED(client->GetBufferSize(&this->device_buffer_frames)) + || this->device_buffer_frames == 0) { + return S_OK; + } + } + + const UINT32 pending = this->pending_frames(); + if (pending == 0) { + return S_OK; + } + + // push only as many frames as the device currently has free, keeping the rest queued. this + // self-paces to the engine's real consumption so a full-buffer write never overflows. + UINT32 padding = 0; + if (FAILED(client->GetCurrentPadding(&padding))) { + return S_OK; + } + const UINT32 device_free = this->device_buffer_frames > padding + ? this->device_buffer_frames - padding + : 0; + if (device_free == 0) { + return S_OK; + } + + const UINT32 to_write = std::min(pending, device_free); + + BYTE *dev = nullptr; + HRESULT ret = real->GetBuffer(to_write, &dev); + if (FAILED(ret) || dev == nullptr) { + return ret; + } + + const size_t bytes = (size_t) to_write * this->frame_bytes; + std::copy(this->fifo.begin(), this->fifo.begin() + bytes, dev); + + // mute the first few buffers to avoid a startup pop, then apply the volume boost. + if (this->buffers_to_mute > 0) { + std::fill(dev, dev + bytes, (BYTE) 0); + this->buffers_to_mute--; + } else if (boost != 1.0f) { + apply_gain(dev, to_write, device_format, boost); + } + + ret = real->ReleaseBuffer(to_write, 0); + + // drop the frames just handed to the device from the front of the FIFO. + this->fifo.erase(this->fifo.begin(), this->fifo.begin() + bytes); + + return ret; + } +} diff --git a/src/spice2x/hooks/audio/backends/wasapi/shared.h b/src/spice2x/hooks/audio/backends/wasapi/shared.h index 6fcc557..27d678f 100644 --- a/src/spice2x/hooks/audio/backends/wasapi/shared.h +++ b/src/spice2x/hooks/audio/backends/wasapi/shared.h @@ -1,83 +1,83 @@ -#pragma once - -#include -#include - -#include -#include -#include - -struct IAudioRenderClient; - -namespace hooks::audio { - - // The -wasapishared option redirects an exclusive WASAPI stream to shared mode, so other apps - // can play sound and devices that can't open the exclusive format still work, at the cost of - // some latency. Only PCM / float is converted; bitstream (AC-3 / DTS) is left alone. - struct SharedRedirect { - - // true once apply() has redirected an exclusive request. gates the buffer clamp; stays false - // for a natively-shared stream (it paces itself, so must not be clamped). - bool redirected_from_exclusive = false; - - // whether an exclusive-mode request should be redirected, given the -wasapishared option. - // only PCM / float is eligible; bitstream (AC-3 / DTS) is left in exclusive mode. - static bool wants(AUDCLNT_SHAREMODE share_mode, const WAVEFORMATEX *format); - - // redirect an exclusive request to shared mode. caller must have checked wants() first. - void apply(AUDCLNT_SHAREMODE *share_mode, DWORD *stream_flags, REFERENCE_TIME *periodicity); - - // clamp a reported buffer size to one device period. the FIFO bridge below is what prevents - // the overflow; this just keeps the game's per-event writes small so the bridge adds minimal - // latency. caches the chosen value for virtual_padding. a no-op unless redirected. - UINT32 clamp_buffer_size(IAudioClient *real, uint32_t sample_rate, UINT32 device_frames); - - // FIFO bridge: the redirected game writes a whole reported buffer per event paced by its own - // callback, not the shared engine clock, so a full-buffer write can intermittently exceed the - // double-buffered shared free space (AUDCLNT_E_BUFFER_TOO_LARGE). The game instead writes - // directly into a FIFO that is drained to the device only as fast as it frees space - the - // same free-space-clamped approach the timer-driven resampler uses. - - // arm the bridge once the redirected stream is initialized. frame_bytes is one frame's size - // in the game's (== device, via AUTOCONVERTPCM) format. - void enable_bridge(int frame_bytes); - - // whether the FIFO bridge is active (a redirect was applied and armed). - bool bridge_enabled() const { return this->frame_bytes > 0; } - - // reserve `frames` at the FIFO tail and hand the game a pointer into it to write in place. - // must be paired with commit_write, which trims the reservation to the frames written. - BYTE *begin_write(UINT32 frames); - - // trim the reservation from begin_write to the `frames` actually written (zeroing if silent). - void commit_write(UINT32 frames, bool silent); - - // padding to report to a game that polls GetCurrentPadding while the bridge is active: the - // FIFO fill level, capped to the reported buffer size so the game's free-space calculation - // (reported_buffer - padding) reflects room in the virtual buffer rather than the device's. - UINT32 virtual_padding() const; - - // push as many queued frames as the real device has free, applying `boost`, keeping the rest - // for the next call. `real` is the wrapped render client's underlying interface; `client` is - // the underlying audio client used to query the device's free space. - HRESULT drain(IAudioRenderClient *real, IAudioClient *client, - const WAVEFORMATEXTENSIBLE &device_format, float boost); - - private: - - // frames currently queued in the FIFO and not yet handed to the device. - UINT32 pending_frames() const; - - // FIFO bridge state (see enable_bridge). fifo holds audio queued for the device in the - // game's interleaved frame format; the game writes new frames directly into its tail between - // begin_write and commit_write. frame_bytes > 0 doubles as the "bridge armed" flag (see - // bridge_enabled). pending_write_offset marks the tail reservation handed to begin_write. - int frame_bytes = 0; - UINT32 device_buffer_frames = 0; - UINT32 reported_frames = 0; - int buffers_to_mute = 4; - size_t pending_write_offset = 0; - std::vector fifo; - }; -} - +#pragma once + +#include +#include + +#include +#include +#include + +struct IAudioRenderClient; + +namespace hooks::audio { + + // The -wasapishared option redirects an exclusive WASAPI stream to shared mode, so other apps + // can play sound and devices that can't open the exclusive format still work, at the cost of + // some latency. Only PCM / float is converted; bitstream (AC-3 / DTS) is left alone. + struct SharedRedirect { + + // true once apply() has redirected an exclusive request. gates the buffer clamp; stays false + // for a natively-shared stream (it paces itself, so must not be clamped). + bool redirected_from_exclusive = false; + + // whether an exclusive-mode request should be redirected, given the -wasapishared option. + // only PCM / float is eligible; bitstream (AC-3 / DTS) is left in exclusive mode. + static bool wants(AUDCLNT_SHAREMODE share_mode, const WAVEFORMATEX *format); + + // redirect an exclusive request to shared mode. caller must have checked wants() first. + void apply(AUDCLNT_SHAREMODE *share_mode, DWORD *stream_flags, REFERENCE_TIME *periodicity); + + // clamp a reported buffer size to one device period. the FIFO bridge below is what prevents + // the overflow; this just keeps the game's per-event writes small so the bridge adds minimal + // latency. caches the chosen value for virtual_padding. a no-op unless redirected. + UINT32 clamp_buffer_size(IAudioClient *real, uint32_t sample_rate, UINT32 device_frames); + + // FIFO bridge: the redirected game writes a whole reported buffer per event paced by its own + // callback, not the shared engine clock, so a full-buffer write can intermittently exceed the + // double-buffered shared free space (AUDCLNT_E_BUFFER_TOO_LARGE). The game instead writes + // directly into a FIFO that is drained to the device only as fast as it frees space - the + // same free-space-clamped approach the timer-driven resampler uses. + + // arm the bridge once the redirected stream is initialized. frame_bytes is one frame's size + // in the game's (== device, via AUTOCONVERTPCM) format. + void enable_bridge(int frame_bytes); + + // whether the FIFO bridge is active (a redirect was applied and armed). + bool bridge_enabled() const { return this->frame_bytes > 0; } + + // reserve `frames` at the FIFO tail and hand the game a pointer into it to write in place. + // must be paired with commit_write, which trims the reservation to the frames written. + BYTE *begin_write(UINT32 frames); + + // trim the reservation from begin_write to the `frames` actually written (zeroing if silent). + void commit_write(UINT32 frames, bool silent); + + // padding to report to a game that polls GetCurrentPadding while the bridge is active: the + // FIFO fill level, capped to the reported buffer size so the game's free-space calculation + // (reported_buffer - padding) reflects room in the virtual buffer rather than the device's. + UINT32 virtual_padding() const; + + // push as many queued frames as the real device has free, applying `boost`, keeping the rest + // for the next call. `real` is the wrapped render client's underlying interface; `client` is + // the underlying audio client used to query the device's free space. + HRESULT drain(IAudioRenderClient *real, IAudioClient *client, + const WAVEFORMATEXTENSIBLE &device_format, float boost); + + private: + + // frames currently queued in the FIFO and not yet handed to the device. + UINT32 pending_frames() const; + + // FIFO bridge state (see enable_bridge). fifo holds audio queued for the device in the + // game's interleaved frame format; the game writes new frames directly into its tail between + // begin_write and commit_write. frame_bytes > 0 doubles as the "bridge armed" flag (see + // bridge_enabled). pending_write_offset marks the tail reservation handed to begin_write. + int frame_bytes = 0; + UINT32 device_buffer_frames = 0; + UINT32 reported_frames = 0; + int buffers_to_mute = 4; + size_t pending_write_offset = 0; + std::vector fifo; + }; +} + diff --git a/src/spice2x/hooks/audio/xact.cpp b/src/spice2x/hooks/audio/xact.cpp index 0fc40c0..5630233 100644 --- a/src/spice2x/hooks/audio/xact.cpp +++ b/src/spice2x/hooks/audio/xact.cpp @@ -1,393 +1,393 @@ -#include "xact.h" - -#include -#include - -#include -#include -#include -#include - -#include "util/deferlog.h" -#include "util/detour.h" -#include "util/logging.h" -#include "util/utils.h" - -namespace hooks::audio::xact { - - // XAudio 2.7 is a COM API. Newer Windows SDKs expose a different IXAudio2 - // layout, so keep this proxy pinned to the legacy ABI used by libxact. - struct XAudio2DeviceDetails { - WCHAR device_id[256]; - WCHAR display_name[256]; - DWORD role; - WAVEFORMATEXTENSIBLE output_format; - }; - - struct XAudio2EffectChain { - UINT32 effect_count; - const void *effect_descriptors; - }; - - struct IXAudio2_27 { - virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **object) = 0; - virtual ULONG STDMETHODCALLTYPE AddRef() = 0; - virtual ULONG STDMETHODCALLTYPE Release() = 0; - virtual HRESULT STDMETHODCALLTYPE GetDeviceCount(UINT32 *device_count) = 0; - virtual HRESULT STDMETHODCALLTYPE GetDeviceDetails( - UINT32 device_index, - XAudio2DeviceDetails *device_details) = 0; - virtual HRESULT STDMETHODCALLTYPE Initialize(UINT32 flags, UINT32 processor) = 0; - virtual HRESULT STDMETHODCALLTYPE RegisterForCallbacks(void *callback) = 0; - virtual void STDMETHODCALLTYPE UnregisterForCallbacks(void *callback) = 0; - virtual HRESULT STDMETHODCALLTYPE CreateSourceVoice( - void **source_voice, - const WAVEFORMATEX *source_format, - UINT32 flags, - float max_frequency_ratio, - void *callback, - const void *send_list, - const XAudio2EffectChain *effect_chain) = 0; - virtual HRESULT STDMETHODCALLTYPE CreateSubmixVoice( - void **submix_voice, - UINT32 input_channels, - UINT32 input_sample_rate, - UINT32 flags, - UINT32 processing_stage, - const void *send_list, - const XAudio2EffectChain *effect_chain) = 0; - virtual HRESULT STDMETHODCALLTYPE CreateMasteringVoice( - void **mastering_voice, - UINT32 input_channels, - UINT32 input_sample_rate, - UINT32 flags, - UINT32 device_index, - const XAudio2EffectChain *effect_chain) = 0; - virtual HRESULT STDMETHODCALLTYPE StartEngine() = 0; - virtual void STDMETHODCALLTYPE StopEngine() = 0; - virtual HRESULT STDMETHODCALLTYPE CommitChanges(UINT32 operation_set) = 0; - virtual void STDMETHODCALLTYPE GetPerformanceData(void *performance_data) = 0; - virtual void STDMETHODCALLTYPE SetDebugConfiguration( - const void *debug_configuration, - void *reserved) = 0; - }; - - // XAudio2 2.7 COM class and interface. - DEFINE_GUID(CLSID_XAudio2_7_LEGACY, - 0x5a508685, 0xa254, 0x4fba, - 0x9b, 0x82, 0x9a, 0x24, 0xb0, 0x03, 0x06, 0xaf); - DEFINE_GUID(IID_IXAudio2_7_LEGACY, - 0x8bcf1f58, 0x9fe7, 0x4583, - 0x8a, 0xc6, 0xe2, 0xad, 0xc4, 0x65, 0xc8, 0xbb); - - static decltype(CoCreateInstance) *CoCreateInstance_orig = nullptr; - - using CreateFX_t = HRESULT (WINAPI *)(REFCLSID, IUnknown **, const void *, UINT32); - static CreateFX_t CreateFX_orig = nullptr; - - static std::string describe_wave_format(const WAVEFORMATEX *format) { - if (format == nullptr) { - return "null"; - } - - DWORD channel_mask = 0; - if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE && - format->cbSize >= sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) { - channel_mask = reinterpret_cast(format)->dwChannelMask; - } - - return fmt::format( - "tag=0x{:04x}, channels={}, rate={} Hz, bits={}, valid_block={} B, avg={} B/s, mask=0x{:08x}", - format->wFormatTag, - format->nChannels, - format->nSamplesPerSec, - format->wBitsPerSample, - format->nBlockAlign, - format->nAvgBytesPerSec, - channel_mask); - } - - template - static std::string narrow_fixed(const WCHAR (&value)[Size]) { - size_t length = 0; - while (length < Size && value[length] != L'\0') { - length++; - } - return ws2s(std::wstring(value, length)); - } - - class WrappedXAudio2 final : public IXAudio2_27 { - public: - explicit WrappedXAudio2(IXAudio2_27 *real) : real(real) { - log_info("audio::xaudio2", "wrapping IXAudio2 2.7 engine {}", static_cast(real)); - } - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **object) override { - if (object == nullptr) { - return E_POINTER; - } - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IXAudio2_7_LEGACY)) { - *object = this; - AddRef(); - log_info("audio::xaudio2", "IXAudio2::QueryInterface({}) -> proxy", guid2s(riid)); - return S_OK; - } - - const auto result = real->QueryInterface(riid, object); - log_info( - "audio::xaudio2", - "IXAudio2::QueryInterface({}) -> {}, object={}", - guid2s(riid), - FMT_HRESULT(result), - object != nullptr ? *object : nullptr); - return result; - } - - ULONG STDMETHODCALLTYPE AddRef() override { - return ++ref_count; - } - - ULONG STDMETHODCALLTYPE Release() override { - const auto remaining = --ref_count; - if (remaining == 0) { - log_info("audio::xaudio2", "destroying IXAudio2 2.7 proxy"); - real->Release(); - delete this; - } - return remaining; - } - - HRESULT STDMETHODCALLTYPE GetDeviceCount(UINT32 *device_count) override { - const auto result = real->GetDeviceCount(device_count); - log_info( - "audio::xaudio2", - "IXAudio2::GetDeviceCount -> {}, count={}", - FMT_HRESULT(result), - SUCCEEDED(result) && device_count != nullptr ? *device_count : 0); - return result; - } - - HRESULT STDMETHODCALLTYPE GetDeviceDetails( - UINT32 device_index, - XAudio2DeviceDetails *device_details) override { - const auto result = real->GetDeviceDetails(device_index, device_details); - if (SUCCEEDED(result) && device_details != nullptr) { - const auto device_name = narrow_fixed(device_details->display_name); - if (!device_details_logged.exchange(true, std::memory_order_relaxed)) { - log_info( - "audio::xaudio2", - "IXAudio2::GetDeviceDetails({}) -> {}, id='{}', name='{}', role=0x{:08x}, {}", - device_index, - FMT_HRESULT(result), - narrow_fixed(device_details->device_id), - device_name, - device_details->role, - describe_wave_format(&device_details->output_format.Format)); - } - const auto channels = device_details->output_format.Format.nChannels; - if (channels != 2 && channels != 6 && - !channel_warning_logged.exchange(true, std::memory_order_relaxed)) { - log_warning( - "audio::xaudio2", - "output device '{}' has {} channels; Nostalgia requires stereo or 5.1 output", - device_name, - channels); - deferredlogs::defer_error_messages({ - "unsupported audio output channel count detected!", - fmt::format(" device: {}", device_name), - fmt::format(" detected {} channels; Nostalgia requires 2 (stereo) or 6 (5.1)", channels), - " * configure the default Windows playback device for stereo or 5.1 output", - " * disable 7.1 surround sound or spatial audio for this device", - }); - } - } else { - log_warning( - "audio::xaudio2", - "IXAudio2::GetDeviceDetails({}) -> {}", - device_index, - FMT_HRESULT(result)); - } - return result; - } - - HRESULT STDMETHODCALLTYPE Initialize(UINT32 flags, UINT32 processor) override { - const auto result = real->Initialize(flags, processor); - log_info( - "audio::xaudio2", - "IXAudio2::Initialize(flags=0x{:08x}, processor=0x{:08x}) -> {}", - flags, - processor, - FMT_HRESULT(result)); - return result; - } - - HRESULT STDMETHODCALLTYPE RegisterForCallbacks(void *callback) override { - const auto result = real->RegisterForCallbacks(callback); - log_info( - "audio::xaudio2", - "IXAudio2::RegisterForCallbacks({}) -> {}", - callback, - FMT_HRESULT(result)); - return result; - } - - void STDMETHODCALLTYPE UnregisterForCallbacks(void *callback) override { - log_info("audio::xaudio2", "IXAudio2::UnregisterForCallbacks({})", callback); - real->UnregisterForCallbacks(callback); - } - - HRESULT STDMETHODCALLTYPE CreateSourceVoice( - void **source_voice, - const WAVEFORMATEX *source_format, - UINT32 flags, - float max_frequency_ratio, - void *callback, - const void *send_list, - const XAudio2EffectChain *effect_chain) override { - return real->CreateSourceVoice( - source_voice, - source_format, - flags, - max_frequency_ratio, - callback, - send_list, - effect_chain); - } - - HRESULT STDMETHODCALLTYPE CreateSubmixVoice( - void **submix_voice, - UINT32 input_channels, - UINT32 input_sample_rate, - UINT32 flags, - UINT32 processing_stage, - const void *send_list, - const XAudio2EffectChain *effect_chain) override { - return real->CreateSubmixVoice( - submix_voice, - input_channels, - input_sample_rate, - flags, - processing_stage, - send_list, - effect_chain); - } - - HRESULT STDMETHODCALLTYPE CreateMasteringVoice( - void **mastering_voice, - UINT32 input_channels, - UINT32 input_sample_rate, - UINT32 flags, - UINT32 device_index, - const XAudio2EffectChain *effect_chain) override { - const auto result = real->CreateMasteringVoice( - mastering_voice, - input_channels, - input_sample_rate, - flags, - device_index, - effect_chain); - log_info( - "audio::xaudio2", - "IXAudio2::CreateMasteringVoice(channels={}, rate={} Hz, flags=0x{:08x}, device={}, effects={}) -> {}, voice={}", - input_channels, - input_sample_rate, - flags, - device_index, - effect_chain != nullptr ? effect_chain->effect_count : 0, - FMT_HRESULT(result), - mastering_voice != nullptr ? *mastering_voice : nullptr); - return result; - } - - HRESULT STDMETHODCALLTYPE StartEngine() override { - const auto result = real->StartEngine(); - log_info("audio::xaudio2", "IXAudio2::StartEngine -> {}", FMT_HRESULT(result)); - return result; - } - - void STDMETHODCALLTYPE StopEngine() override { - log_info("audio::xaudio2", "IXAudio2::StopEngine"); - real->StopEngine(); - } - - HRESULT STDMETHODCALLTYPE CommitChanges(UINT32 operation_set) override { - return real->CommitChanges(operation_set); - } - - void STDMETHODCALLTYPE GetPerformanceData(void *performance_data) override { - real->GetPerformanceData(performance_data); - } - - void STDMETHODCALLTYPE SetDebugConfiguration( - const void *debug_configuration, - void *reserved) override { - log_info("audio::xaudio2", "IXAudio2::SetDebugConfiguration({})", debug_configuration); - real->SetDebugConfiguration(debug_configuration, reserved); - } - - private: - std::atomic ref_count = 1; - std::atomic_bool device_details_logged = false; - std::atomic_bool channel_warning_logged = false; - IXAudio2_27 *real; - }; - - static HRESULT STDAPICALLTYPE CoCreateInstance_hook( - REFCLSID clsid, - LPUNKNOWN outer, - DWORD class_context, - REFIID iid, - LPVOID *object) { - const auto result = CoCreateInstance_orig(clsid, outer, class_context, iid, object); - log_info( - "audio::xact", - "CoCreateInstance(clsid={}, iid={}, context=0x{:08x}) -> {}, object={}", - guid2s(clsid), - guid2s(iid), - class_context, - FMT_HRESULT(result), - object != nullptr ? *object : nullptr); - - if (SUCCEEDED(result) && object != nullptr && *object != nullptr && - IsEqualCLSID(clsid, CLSID_XAudio2_7_LEGACY) && - IsEqualIID(iid, IID_IXAudio2_7_LEGACY)) { - *object = static_cast( - new WrappedXAudio2(static_cast(*object))); - } - return result; - } - - static HRESULT WINAPI CreateFX_hook( - REFCLSID clsid, - IUnknown **effect, - const void *init_data, - UINT32 init_data_size) { - const auto result = CreateFX_orig(clsid, effect, init_data, init_data_size); - log_info( - "audio::xapofx", - "CreateFX(clsid={}, init_data={}, size={}) -> {}, effect={}", - guid2s(clsid), - init_data, - init_data_size, - FMT_HRESULT(result), - effect != nullptr ? static_cast(*effect) : nullptr); - return result; - } - - void init() { - const auto libxact = GetModuleHandleW(L"libxact.dll"); - if (libxact == nullptr) { - return; - } - - CoCreateInstance_orig = detour::iat_try( - "CoCreateInstance", CoCreateInstance_hook, libxact); - CreateFX_orig = detour::iat_try("CreateFX", CreateFX_hook, libxact); - - log_info( - "audio::xact", - "libxact hooks installed: CoCreateInstance={}, CreateFX={}", - CoCreateInstance_orig != nullptr, - CreateFX_orig != nullptr); - } +#include "xact.h" + +#include +#include + +#include +#include +#include +#include + +#include "util/deferlog.h" +#include "util/detour.h" +#include "util/logging.h" +#include "util/utils.h" + +namespace hooks::audio::xact { + + // XAudio 2.7 is a COM API. Newer Windows SDKs expose a different IXAudio2 + // layout, so keep this proxy pinned to the legacy ABI used by libxact. + struct XAudio2DeviceDetails { + WCHAR device_id[256]; + WCHAR display_name[256]; + DWORD role; + WAVEFORMATEXTENSIBLE output_format; + }; + + struct XAudio2EffectChain { + UINT32 effect_count; + const void *effect_descriptors; + }; + + struct IXAudio2_27 { + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **object) = 0; + virtual ULONG STDMETHODCALLTYPE AddRef() = 0; + virtual ULONG STDMETHODCALLTYPE Release() = 0; + virtual HRESULT STDMETHODCALLTYPE GetDeviceCount(UINT32 *device_count) = 0; + virtual HRESULT STDMETHODCALLTYPE GetDeviceDetails( + UINT32 device_index, + XAudio2DeviceDetails *device_details) = 0; + virtual HRESULT STDMETHODCALLTYPE Initialize(UINT32 flags, UINT32 processor) = 0; + virtual HRESULT STDMETHODCALLTYPE RegisterForCallbacks(void *callback) = 0; + virtual void STDMETHODCALLTYPE UnregisterForCallbacks(void *callback) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateSourceVoice( + void **source_voice, + const WAVEFORMATEX *source_format, + UINT32 flags, + float max_frequency_ratio, + void *callback, + const void *send_list, + const XAudio2EffectChain *effect_chain) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateSubmixVoice( + void **submix_voice, + UINT32 input_channels, + UINT32 input_sample_rate, + UINT32 flags, + UINT32 processing_stage, + const void *send_list, + const XAudio2EffectChain *effect_chain) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateMasteringVoice( + void **mastering_voice, + UINT32 input_channels, + UINT32 input_sample_rate, + UINT32 flags, + UINT32 device_index, + const XAudio2EffectChain *effect_chain) = 0; + virtual HRESULT STDMETHODCALLTYPE StartEngine() = 0; + virtual void STDMETHODCALLTYPE StopEngine() = 0; + virtual HRESULT STDMETHODCALLTYPE CommitChanges(UINT32 operation_set) = 0; + virtual void STDMETHODCALLTYPE GetPerformanceData(void *performance_data) = 0; + virtual void STDMETHODCALLTYPE SetDebugConfiguration( + const void *debug_configuration, + void *reserved) = 0; + }; + + // XAudio2 2.7 COM class and interface. + DEFINE_GUID(CLSID_XAudio2_7_LEGACY, + 0x5a508685, 0xa254, 0x4fba, + 0x9b, 0x82, 0x9a, 0x24, 0xb0, 0x03, 0x06, 0xaf); + DEFINE_GUID(IID_IXAudio2_7_LEGACY, + 0x8bcf1f58, 0x9fe7, 0x4583, + 0x8a, 0xc6, 0xe2, 0xad, 0xc4, 0x65, 0xc8, 0xbb); + + static decltype(CoCreateInstance) *CoCreateInstance_orig = nullptr; + + using CreateFX_t = HRESULT (WINAPI *)(REFCLSID, IUnknown **, const void *, UINT32); + static CreateFX_t CreateFX_orig = nullptr; + + static std::string describe_wave_format(const WAVEFORMATEX *format) { + if (format == nullptr) { + return "null"; + } + + DWORD channel_mask = 0; + if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE && + format->cbSize >= sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) { + channel_mask = reinterpret_cast(format)->dwChannelMask; + } + + return fmt::format( + "tag=0x{:04x}, channels={}, rate={} Hz, bits={}, valid_block={} B, avg={} B/s, mask=0x{:08x}", + format->wFormatTag, + format->nChannels, + format->nSamplesPerSec, + format->wBitsPerSample, + format->nBlockAlign, + format->nAvgBytesPerSec, + channel_mask); + } + + template + static std::string narrow_fixed(const WCHAR (&value)[Size]) { + size_t length = 0; + while (length < Size && value[length] != L'\0') { + length++; + } + return ws2s(std::wstring(value, length)); + } + + class WrappedXAudio2 final : public IXAudio2_27 { + public: + explicit WrappedXAudio2(IXAudio2_27 *real) : real(real) { + log_info("audio::xaudio2", "wrapping IXAudio2 2.7 engine {}", static_cast(real)); + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **object) override { + if (object == nullptr) { + return E_POINTER; + } + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IXAudio2_7_LEGACY)) { + *object = this; + AddRef(); + log_info("audio::xaudio2", "IXAudio2::QueryInterface({}) -> proxy", guid2s(riid)); + return S_OK; + } + + const auto result = real->QueryInterface(riid, object); + log_info( + "audio::xaudio2", + "IXAudio2::QueryInterface({}) -> {}, object={}", + guid2s(riid), + FMT_HRESULT(result), + object != nullptr ? *object : nullptr); + return result; + } + + ULONG STDMETHODCALLTYPE AddRef() override { + return ++ref_count; + } + + ULONG STDMETHODCALLTYPE Release() override { + const auto remaining = --ref_count; + if (remaining == 0) { + log_info("audio::xaudio2", "destroying IXAudio2 2.7 proxy"); + real->Release(); + delete this; + } + return remaining; + } + + HRESULT STDMETHODCALLTYPE GetDeviceCount(UINT32 *device_count) override { + const auto result = real->GetDeviceCount(device_count); + log_info( + "audio::xaudio2", + "IXAudio2::GetDeviceCount -> {}, count={}", + FMT_HRESULT(result), + SUCCEEDED(result) && device_count != nullptr ? *device_count : 0); + return result; + } + + HRESULT STDMETHODCALLTYPE GetDeviceDetails( + UINT32 device_index, + XAudio2DeviceDetails *device_details) override { + const auto result = real->GetDeviceDetails(device_index, device_details); + if (SUCCEEDED(result) && device_details != nullptr) { + const auto device_name = narrow_fixed(device_details->display_name); + if (!device_details_logged.exchange(true, std::memory_order_relaxed)) { + log_info( + "audio::xaudio2", + "IXAudio2::GetDeviceDetails({}) -> {}, id='{}', name='{}', role=0x{:08x}, {}", + device_index, + FMT_HRESULT(result), + narrow_fixed(device_details->device_id), + device_name, + device_details->role, + describe_wave_format(&device_details->output_format.Format)); + } + const auto channels = device_details->output_format.Format.nChannels; + if (channels != 2 && channels != 6 && + !channel_warning_logged.exchange(true, std::memory_order_relaxed)) { + log_warning( + "audio::xaudio2", + "output device '{}' has {} channels; Nostalgia requires stereo or 5.1 output", + device_name, + channels); + deferredlogs::defer_error_messages({ + "unsupported audio output channel count detected!", + fmt::format(" device: {}", device_name), + fmt::format(" detected {} channels; Nostalgia requires 2 (stereo) or 6 (5.1)", channels), + " * configure the default Windows playback device for stereo or 5.1 output", + " * disable 7.1 surround sound or spatial audio for this device", + }); + } + } else { + log_warning( + "audio::xaudio2", + "IXAudio2::GetDeviceDetails({}) -> {}", + device_index, + FMT_HRESULT(result)); + } + return result; + } + + HRESULT STDMETHODCALLTYPE Initialize(UINT32 flags, UINT32 processor) override { + const auto result = real->Initialize(flags, processor); + log_info( + "audio::xaudio2", + "IXAudio2::Initialize(flags=0x{:08x}, processor=0x{:08x}) -> {}", + flags, + processor, + FMT_HRESULT(result)); + return result; + } + + HRESULT STDMETHODCALLTYPE RegisterForCallbacks(void *callback) override { + const auto result = real->RegisterForCallbacks(callback); + log_info( + "audio::xaudio2", + "IXAudio2::RegisterForCallbacks({}) -> {}", + callback, + FMT_HRESULT(result)); + return result; + } + + void STDMETHODCALLTYPE UnregisterForCallbacks(void *callback) override { + log_info("audio::xaudio2", "IXAudio2::UnregisterForCallbacks({})", callback); + real->UnregisterForCallbacks(callback); + } + + HRESULT STDMETHODCALLTYPE CreateSourceVoice( + void **source_voice, + const WAVEFORMATEX *source_format, + UINT32 flags, + float max_frequency_ratio, + void *callback, + const void *send_list, + const XAudio2EffectChain *effect_chain) override { + return real->CreateSourceVoice( + source_voice, + source_format, + flags, + max_frequency_ratio, + callback, + send_list, + effect_chain); + } + + HRESULT STDMETHODCALLTYPE CreateSubmixVoice( + void **submix_voice, + UINT32 input_channels, + UINT32 input_sample_rate, + UINT32 flags, + UINT32 processing_stage, + const void *send_list, + const XAudio2EffectChain *effect_chain) override { + return real->CreateSubmixVoice( + submix_voice, + input_channels, + input_sample_rate, + flags, + processing_stage, + send_list, + effect_chain); + } + + HRESULT STDMETHODCALLTYPE CreateMasteringVoice( + void **mastering_voice, + UINT32 input_channels, + UINT32 input_sample_rate, + UINT32 flags, + UINT32 device_index, + const XAudio2EffectChain *effect_chain) override { + const auto result = real->CreateMasteringVoice( + mastering_voice, + input_channels, + input_sample_rate, + flags, + device_index, + effect_chain); + log_info( + "audio::xaudio2", + "IXAudio2::CreateMasteringVoice(channels={}, rate={} Hz, flags=0x{:08x}, device={}, effects={}) -> {}, voice={}", + input_channels, + input_sample_rate, + flags, + device_index, + effect_chain != nullptr ? effect_chain->effect_count : 0, + FMT_HRESULT(result), + mastering_voice != nullptr ? *mastering_voice : nullptr); + return result; + } + + HRESULT STDMETHODCALLTYPE StartEngine() override { + const auto result = real->StartEngine(); + log_info("audio::xaudio2", "IXAudio2::StartEngine -> {}", FMT_HRESULT(result)); + return result; + } + + void STDMETHODCALLTYPE StopEngine() override { + log_info("audio::xaudio2", "IXAudio2::StopEngine"); + real->StopEngine(); + } + + HRESULT STDMETHODCALLTYPE CommitChanges(UINT32 operation_set) override { + return real->CommitChanges(operation_set); + } + + void STDMETHODCALLTYPE GetPerformanceData(void *performance_data) override { + real->GetPerformanceData(performance_data); + } + + void STDMETHODCALLTYPE SetDebugConfiguration( + const void *debug_configuration, + void *reserved) override { + log_info("audio::xaudio2", "IXAudio2::SetDebugConfiguration({})", debug_configuration); + real->SetDebugConfiguration(debug_configuration, reserved); + } + + private: + std::atomic ref_count = 1; + std::atomic_bool device_details_logged = false; + std::atomic_bool channel_warning_logged = false; + IXAudio2_27 *real; + }; + + static HRESULT STDAPICALLTYPE CoCreateInstance_hook( + REFCLSID clsid, + LPUNKNOWN outer, + DWORD class_context, + REFIID iid, + LPVOID *object) { + const auto result = CoCreateInstance_orig(clsid, outer, class_context, iid, object); + log_info( + "audio::xact", + "CoCreateInstance(clsid={}, iid={}, context=0x{:08x}) -> {}, object={}", + guid2s(clsid), + guid2s(iid), + class_context, + FMT_HRESULT(result), + object != nullptr ? *object : nullptr); + + if (SUCCEEDED(result) && object != nullptr && *object != nullptr && + IsEqualCLSID(clsid, CLSID_XAudio2_7_LEGACY) && + IsEqualIID(iid, IID_IXAudio2_7_LEGACY)) { + *object = static_cast( + new WrappedXAudio2(static_cast(*object))); + } + return result; + } + + static HRESULT WINAPI CreateFX_hook( + REFCLSID clsid, + IUnknown **effect, + const void *init_data, + UINT32 init_data_size) { + const auto result = CreateFX_orig(clsid, effect, init_data, init_data_size); + log_info( + "audio::xapofx", + "CreateFX(clsid={}, init_data={}, size={}) -> {}, effect={}", + guid2s(clsid), + init_data, + init_data_size, + FMT_HRESULT(result), + effect != nullptr ? static_cast(*effect) : nullptr); + return result; + } + + void init() { + const auto libxact = GetModuleHandleW(L"libxact.dll"); + if (libxact == nullptr) { + return; + } + + CoCreateInstance_orig = detour::iat_try( + "CoCreateInstance", CoCreateInstance_hook, libxact); + CreateFX_orig = detour::iat_try("CreateFX", CreateFX_hook, libxact); + + log_info( + "audio::xact", + "libxact hooks installed: CoCreateInstance={}, CreateFX={}", + CoCreateInstance_orig != nullptr, + CreateFX_orig != nullptr); + } } \ No newline at end of file diff --git a/src/spice2x/hooks/audio/xact.h b/src/spice2x/hooks/audio/xact.h index 0c0cb89..06094ed 100644 --- a/src/spice2x/hooks/audio/xact.h +++ b/src/spice2x/hooks/audio/xact.h @@ -1,5 +1,5 @@ -#pragma once - -namespace hooks::audio::xact { - void init(); +#pragma once + +namespace hooks::audio::xact { + void init(); } \ No newline at end of file diff --git a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_backend.cpp b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_backend.cpp index c680fc4..f5d0c8f 100644 --- a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_backend.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_backend.cpp @@ -1,304 +1,304 @@ -// dx11 / dxgi hook entrypoint. trampolines d3d11.dll / dxgi.dll exports -// the moment those DLLs appear (LDR notification + poll-thread fallback), -// then drives proactive vtable capture so we don't lose the race against -// the execexe loader. per-vtable hook implementations live in the sibling -// files (d3d11_swapchain / d3d11_factory / d3d11_vtable_capture / -// d3d11_screenshot). -// -// note: never LoadLibrary d3d11/dxgi -- execexe pre-loads them itself and -// fails (error 0xa) if they're already in the loader's module list. -// -// 64-bit only. - -#include "d3d11_backend.h" - -#ifndef SPICE_D3D11 - -void graphics_d3d11_init() {} -void graphics_d3d11_shutdown() {} - -#else - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "d3d11_internal.h" -#include "util/nt_loader.h" - -namespace { - -using D3D11CreateDeviceAndSwapChain_t = HRESULT(WINAPI *)( - IDXGIAdapter *, D3D_DRIVER_TYPE, HMODULE, UINT, - const D3D_FEATURE_LEVEL *, UINT, UINT, - const DXGI_SWAP_CHAIN_DESC *, IDXGISwapChain **, - ID3D11Device **, D3D_FEATURE_LEVEL *, ID3D11DeviceContext **); -using CreateDXGIFactory_t = HRESULT(WINAPI *)(REFIID, void **); -using CreateDXGIFactory1_t = HRESULT(WINAPI *)(REFIID, void **); -using CreateDXGIFactory2_t = HRESULT(WINAPI *)(UINT, REFIID, void **); - -D3D11CreateDeviceAndSwapChain_t D3D11CreateDeviceAndSwapChain_orig = nullptr; -CreateDXGIFactory_t CreateDXGIFactory_orig = nullptr; -CreateDXGIFactory1_t CreateDXGIFactory1_orig = nullptr; -CreateDXGIFactory2_t CreateDXGIFactory2_orig = nullptr; - -std::atomic g_d3d11_exports_hooked { false }; -std::atomic g_dxgi_exports_hooked { false }; - -// ---------------------------------------------------------------------- -// top-level export hooks - -HRESULT WINAPI D3D11CreateDeviceAndSwapChain_hook( - IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, - const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, - const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, IDXGISwapChain **ppSwapChain, - ID3D11Device **ppDevice, D3D_FEATURE_LEVEL *pFeatureLevel, - ID3D11DeviceContext **ppImmediateContext) -{ - HRESULT res = D3D11CreateDeviceAndSwapChain_orig( - pAdapter, DriverType, Software, Flags, - pFeatureLevels, FeatureLevels, SDKVersion, - pSwapChainDesc, ppSwapChain, ppDevice, pFeatureLevel, ppImmediateContext); - if (SUCCEEDED(res) && ppSwapChain && *ppSwapChain) { - if (pSwapChainDesc) { - d3d11_hooks::note_main_hwnd(pSwapChainDesc->OutputWindow); - } - d3d11_hooks::install_swapchain_hooks(*ppSwapChain); - } - return res; -} - -#define DEFINE_FACTORY_HOOK(NAME, SIG_PARAMS, ORIG_ARGS) \ - HRESULT WINAPI NAME##_hook SIG_PARAMS { \ - HRESULT res = NAME##_orig ORIG_ARGS; \ - if (SUCCEEDED(res) && ppFactory && *ppFactory) { \ - d3d11_hooks::install_factory_hooks( \ - reinterpret_cast(*ppFactory)); \ - } \ - return res; \ - } - -DEFINE_FACTORY_HOOK(CreateDXGIFactory, - (REFIID riid, void **ppFactory), - (riid, ppFactory)) -DEFINE_FACTORY_HOOK(CreateDXGIFactory1, - (REFIID riid, void **ppFactory), - (riid, ppFactory)) -DEFINE_FACTORY_HOOK(CreateDXGIFactory2, - (UINT Flags, REFIID riid, void **ppFactory), - (Flags, riid, ppFactory)) - -#undef DEFINE_FACTORY_HOOK - -// ---------------------------------------------------------------------- -// export trampoline plumbing - -// serializes trampoline_export() so the LDR notification callback and the -// poll thread don't race each other into MinHook against the same target. -std::mutex g_export_mutex; - -bool trampoline_export(const char *dll, const char *name, void *hook, void **orig) { - std::lock_guard lock(g_export_mutex); - if (*orig) { - return true; - } - HMODULE mod = GetModuleHandleA(dll); - if (!mod) { - return false; - } - void *addr = reinterpret_cast(GetProcAddress(mod, name)); - if (!addr) { - return false; - } - *orig = addr; // trampoline_try reads *orig before overwriting it. - if (!detour::trampoline_try(addr, hook, orig)) { - *orig = nullptr; - return false; - } - log_info("graphics::d3d11", "trampolined {}!{}", dll, name); - return true; -} - -void try_install_d3d11_exports() { - if (g_d3d11_exports_hooked) { - return; - } - if (trampoline_export("d3d11.dll", "D3D11CreateDeviceAndSwapChain", - (void *) D3D11CreateDeviceAndSwapChain_hook, - (void **) &D3D11CreateDeviceAndSwapChain_orig)) { - g_d3d11_exports_hooked = true; - } -} - -void try_install_dxgi_exports() { - if (g_dxgi_exports_hooked) { - return; - } - struct entry { const char *name; void *hook; void **orig; }; - const entry entries[] = { - { "CreateDXGIFactory", (void *) CreateDXGIFactory_hook, - (void **) &CreateDXGIFactory_orig }, - { "CreateDXGIFactory1", (void *) CreateDXGIFactory1_hook, - (void **) &CreateDXGIFactory1_orig }, - { "CreateDXGIFactory2", (void *) CreateDXGIFactory2_hook, - (void **) &CreateDXGIFactory2_orig }, - }; - bool any = false; - for (auto &e : entries) { - any |= trampoline_export("dxgi.dll", e.name, e.hook, e.orig); - } - if (any) { - g_dxgi_exports_hooked = true; - } -} - -void try_capture_if_ready() { - if (g_d3d11_exports_hooked && g_dxgi_exports_hooked) { - d3d11_hooks::try_capture_vtables(); - } -} - -// ---------------------------------------------------------------------- -// LDR notification + polling fallback - -bool dll_name_ends_with(PCUNICODE_STRING name, const wchar_t *suffix) { - if (!name || !name->Buffer) { - return false; - } - const size_t n = name->Length / sizeof(WCHAR); - const size_t s = wcslen(suffix); - return n >= s && _wcsnicmp(name->Buffer + n - s, suffix, s) == 0; -} - -VOID CALLBACK ldr_dll_notification( - ULONG reason, PCLDR_DLL_NOTIFICATION_DATA data, PVOID /*context*/) -{ - if (reason != LDR_DLL_NOTIFICATION_REASON_LOADED || !data) { - return; - } - if (dll_name_ends_with(data->Loaded.BaseDllName, L"d3d11.dll")) { - try_install_d3d11_exports(); - } else if (dll_name_ends_with(data->Loaded.BaseDllName, L"dxgi.dll")) { - try_install_dxgi_exports(); - } -} - -// execexe maps d3d11/dxgi via a path that bypasses LdrLoadDll, so the -// notification above never fires for those DLLs and we have to poll. -std::atomic g_stop { false }; -std::thread g_poll_thread; -std::mutex g_init_mutex; -PVOID g_ldr_cookie = nullptr; - -void poll_thread() { - using namespace std::chrono_literals; - for (int32_t i = 0; i < 120 && !g_stop.load(); ++i) { - try_install_d3d11_exports(); - try_install_dxgi_exports(); - if (g_d3d11_exports_hooked && g_dxgi_exports_hooked) { - d3d11_hooks::try_capture_vtables(); - return; - } - // sliced so shutdown doesn't have to wait a full second. - for (int32_t s = 0; s < 10 && !g_stop.load(); ++s) { - std::this_thread::sleep_for(100ms); - } - } -} - -// the overlay's imgui dx11 backend needs D3DCompile (d3dcompiler_XX.dll) to -// build its shaders. _43 ships with the DX June 2010 redist on stock Win7; -// _46/_47 come with newer Windows. -bool d3dcompiler_available() { - static const wchar_t *names[] = { - L"d3dcompiler_47.dll", - L"d3dcompiler_46.dll", - L"d3dcompiler_43.dll", - }; - for (auto name : names) { - HMODULE mod = GetModuleHandleW(name); - if (!mod) { - mod = LoadLibraryW(name); - } - if (mod && GetProcAddress(mod, "D3DCompile")) { - return true; - } - } - return false; -} - -} // namespace - -void graphics_d3d11_init() { - // dx11 titles always run under execexe. skipping on pure-dx9 games keeps - // their startup path completely untouched (no exports patched, no poll - // thread, no LDR callback). - if (!GetModuleHandleW(L"execexe.dll")) { - return; - } - - // no d3dcompiler -> overlay can't build shaders; skip dx11 overlay - if (!d3dcompiler_available()) { - log_warning( - "graphics::d3d11", - "d3dcompiler not found; dx11 overlay disabled"); - return; - } - - std::lock_guard lock(g_init_mutex); - if (g_poll_thread.joinable()) { - return; // already initialized - } - - log_info("graphics::d3d11", "initializing"); - - // trampoline now if either DLL is already in the PEB. - try_install_d3d11_exports(); - try_install_dxgi_exports(); - try_capture_if_ready(); - - // catches standard LdrLoadDll loads. - auto reg = reinterpret_cast( - GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "LdrRegisterDllNotification")); - if (reg) { - NTSTATUS st = reg(0, ldr_dll_notification, nullptr, &g_ldr_cookie); - if (NT_SUCCESS(st)) { - log_info("graphics::d3d11", "registered LDR DLL notification"); - } else { - g_ldr_cookie = nullptr; - log_warning("graphics::d3d11", - "LdrRegisterDllNotification failed: {:#x}", (unsigned long)st); - } - } - - // catches the execexe loader path that bypasses LdrLoadDll. - g_poll_thread = std::thread(poll_thread); -} - -void graphics_d3d11_shutdown() { - std::lock_guard lock(g_init_mutex); - - // unregister first so the callback can't fire mid-teardown. - if (g_ldr_cookie) { - auto unreg = reinterpret_cast( - GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "LdrUnregisterDllNotification")); - if (unreg) { - unreg(g_ldr_cookie); - } - g_ldr_cookie = nullptr; - } - - g_stop.store(true); - if (g_poll_thread.joinable()) { - g_poll_thread.join(); - } -} - -#endif // SPICE_D3D11 +// dx11 / dxgi hook entrypoint. trampolines d3d11.dll / dxgi.dll exports +// the moment those DLLs appear (LDR notification + poll-thread fallback), +// then drives proactive vtable capture so we don't lose the race against +// the execexe loader. per-vtable hook implementations live in the sibling +// files (d3d11_swapchain / d3d11_factory / d3d11_vtable_capture / +// d3d11_screenshot). +// +// note: never LoadLibrary d3d11/dxgi -- execexe pre-loads them itself and +// fails (error 0xa) if they're already in the loader's module list. +// +// 64-bit only. + +#include "d3d11_backend.h" + +#ifndef SPICE_D3D11 + +void graphics_d3d11_init() {} +void graphics_d3d11_shutdown() {} + +#else + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "d3d11_internal.h" +#include "util/nt_loader.h" + +namespace { + +using D3D11CreateDeviceAndSwapChain_t = HRESULT(WINAPI *)( + IDXGIAdapter *, D3D_DRIVER_TYPE, HMODULE, UINT, + const D3D_FEATURE_LEVEL *, UINT, UINT, + const DXGI_SWAP_CHAIN_DESC *, IDXGISwapChain **, + ID3D11Device **, D3D_FEATURE_LEVEL *, ID3D11DeviceContext **); +using CreateDXGIFactory_t = HRESULT(WINAPI *)(REFIID, void **); +using CreateDXGIFactory1_t = HRESULT(WINAPI *)(REFIID, void **); +using CreateDXGIFactory2_t = HRESULT(WINAPI *)(UINT, REFIID, void **); + +D3D11CreateDeviceAndSwapChain_t D3D11CreateDeviceAndSwapChain_orig = nullptr; +CreateDXGIFactory_t CreateDXGIFactory_orig = nullptr; +CreateDXGIFactory1_t CreateDXGIFactory1_orig = nullptr; +CreateDXGIFactory2_t CreateDXGIFactory2_orig = nullptr; + +std::atomic g_d3d11_exports_hooked { false }; +std::atomic g_dxgi_exports_hooked { false }; + +// ---------------------------------------------------------------------- +// top-level export hooks + +HRESULT WINAPI D3D11CreateDeviceAndSwapChain_hook( + IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, + const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, + const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, IDXGISwapChain **ppSwapChain, + ID3D11Device **ppDevice, D3D_FEATURE_LEVEL *pFeatureLevel, + ID3D11DeviceContext **ppImmediateContext) +{ + HRESULT res = D3D11CreateDeviceAndSwapChain_orig( + pAdapter, DriverType, Software, Flags, + pFeatureLevels, FeatureLevels, SDKVersion, + pSwapChainDesc, ppSwapChain, ppDevice, pFeatureLevel, ppImmediateContext); + if (SUCCEEDED(res) && ppSwapChain && *ppSwapChain) { + if (pSwapChainDesc) { + d3d11_hooks::note_main_hwnd(pSwapChainDesc->OutputWindow); + } + d3d11_hooks::install_swapchain_hooks(*ppSwapChain); + } + return res; +} + +#define DEFINE_FACTORY_HOOK(NAME, SIG_PARAMS, ORIG_ARGS) \ + HRESULT WINAPI NAME##_hook SIG_PARAMS { \ + HRESULT res = NAME##_orig ORIG_ARGS; \ + if (SUCCEEDED(res) && ppFactory && *ppFactory) { \ + d3d11_hooks::install_factory_hooks( \ + reinterpret_cast(*ppFactory)); \ + } \ + return res; \ + } + +DEFINE_FACTORY_HOOK(CreateDXGIFactory, + (REFIID riid, void **ppFactory), + (riid, ppFactory)) +DEFINE_FACTORY_HOOK(CreateDXGIFactory1, + (REFIID riid, void **ppFactory), + (riid, ppFactory)) +DEFINE_FACTORY_HOOK(CreateDXGIFactory2, + (UINT Flags, REFIID riid, void **ppFactory), + (Flags, riid, ppFactory)) + +#undef DEFINE_FACTORY_HOOK + +// ---------------------------------------------------------------------- +// export trampoline plumbing + +// serializes trampoline_export() so the LDR notification callback and the +// poll thread don't race each other into MinHook against the same target. +std::mutex g_export_mutex; + +bool trampoline_export(const char *dll, const char *name, void *hook, void **orig) { + std::lock_guard lock(g_export_mutex); + if (*orig) { + return true; + } + HMODULE mod = GetModuleHandleA(dll); + if (!mod) { + return false; + } + void *addr = reinterpret_cast(GetProcAddress(mod, name)); + if (!addr) { + return false; + } + *orig = addr; // trampoline_try reads *orig before overwriting it. + if (!detour::trampoline_try(addr, hook, orig)) { + *orig = nullptr; + return false; + } + log_info("graphics::d3d11", "trampolined {}!{}", dll, name); + return true; +} + +void try_install_d3d11_exports() { + if (g_d3d11_exports_hooked) { + return; + } + if (trampoline_export("d3d11.dll", "D3D11CreateDeviceAndSwapChain", + (void *) D3D11CreateDeviceAndSwapChain_hook, + (void **) &D3D11CreateDeviceAndSwapChain_orig)) { + g_d3d11_exports_hooked = true; + } +} + +void try_install_dxgi_exports() { + if (g_dxgi_exports_hooked) { + return; + } + struct entry { const char *name; void *hook; void **orig; }; + const entry entries[] = { + { "CreateDXGIFactory", (void *) CreateDXGIFactory_hook, + (void **) &CreateDXGIFactory_orig }, + { "CreateDXGIFactory1", (void *) CreateDXGIFactory1_hook, + (void **) &CreateDXGIFactory1_orig }, + { "CreateDXGIFactory2", (void *) CreateDXGIFactory2_hook, + (void **) &CreateDXGIFactory2_orig }, + }; + bool any = false; + for (auto &e : entries) { + any |= trampoline_export("dxgi.dll", e.name, e.hook, e.orig); + } + if (any) { + g_dxgi_exports_hooked = true; + } +} + +void try_capture_if_ready() { + if (g_d3d11_exports_hooked && g_dxgi_exports_hooked) { + d3d11_hooks::try_capture_vtables(); + } +} + +// ---------------------------------------------------------------------- +// LDR notification + polling fallback + +bool dll_name_ends_with(PCUNICODE_STRING name, const wchar_t *suffix) { + if (!name || !name->Buffer) { + return false; + } + const size_t n = name->Length / sizeof(WCHAR); + const size_t s = wcslen(suffix); + return n >= s && _wcsnicmp(name->Buffer + n - s, suffix, s) == 0; +} + +VOID CALLBACK ldr_dll_notification( + ULONG reason, PCLDR_DLL_NOTIFICATION_DATA data, PVOID /*context*/) +{ + if (reason != LDR_DLL_NOTIFICATION_REASON_LOADED || !data) { + return; + } + if (dll_name_ends_with(data->Loaded.BaseDllName, L"d3d11.dll")) { + try_install_d3d11_exports(); + } else if (dll_name_ends_with(data->Loaded.BaseDllName, L"dxgi.dll")) { + try_install_dxgi_exports(); + } +} + +// execexe maps d3d11/dxgi via a path that bypasses LdrLoadDll, so the +// notification above never fires for those DLLs and we have to poll. +std::atomic g_stop { false }; +std::thread g_poll_thread; +std::mutex g_init_mutex; +PVOID g_ldr_cookie = nullptr; + +void poll_thread() { + using namespace std::chrono_literals; + for (int32_t i = 0; i < 120 && !g_stop.load(); ++i) { + try_install_d3d11_exports(); + try_install_dxgi_exports(); + if (g_d3d11_exports_hooked && g_dxgi_exports_hooked) { + d3d11_hooks::try_capture_vtables(); + return; + } + // sliced so shutdown doesn't have to wait a full second. + for (int32_t s = 0; s < 10 && !g_stop.load(); ++s) { + std::this_thread::sleep_for(100ms); + } + } +} + +// the overlay's imgui dx11 backend needs D3DCompile (d3dcompiler_XX.dll) to +// build its shaders. _43 ships with the DX June 2010 redist on stock Win7; +// _46/_47 come with newer Windows. +bool d3dcompiler_available() { + static const wchar_t *names[] = { + L"d3dcompiler_47.dll", + L"d3dcompiler_46.dll", + L"d3dcompiler_43.dll", + }; + for (auto name : names) { + HMODULE mod = GetModuleHandleW(name); + if (!mod) { + mod = LoadLibraryW(name); + } + if (mod && GetProcAddress(mod, "D3DCompile")) { + return true; + } + } + return false; +} + +} // namespace + +void graphics_d3d11_init() { + // dx11 titles always run under execexe. skipping on pure-dx9 games keeps + // their startup path completely untouched (no exports patched, no poll + // thread, no LDR callback). + if (!GetModuleHandleW(L"execexe.dll")) { + return; + } + + // no d3dcompiler -> overlay can't build shaders; skip dx11 overlay + if (!d3dcompiler_available()) { + log_warning( + "graphics::d3d11", + "d3dcompiler not found; dx11 overlay disabled"); + return; + } + + std::lock_guard lock(g_init_mutex); + if (g_poll_thread.joinable()) { + return; // already initialized + } + + log_info("graphics::d3d11", "initializing"); + + // trampoline now if either DLL is already in the PEB. + try_install_d3d11_exports(); + try_install_dxgi_exports(); + try_capture_if_ready(); + + // catches standard LdrLoadDll loads. + auto reg = reinterpret_cast( + GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "LdrRegisterDllNotification")); + if (reg) { + NTSTATUS st = reg(0, ldr_dll_notification, nullptr, &g_ldr_cookie); + if (NT_SUCCESS(st)) { + log_info("graphics::d3d11", "registered LDR DLL notification"); + } else { + g_ldr_cookie = nullptr; + log_warning("graphics::d3d11", + "LdrRegisterDllNotification failed: {:#x}", (unsigned long)st); + } + } + + // catches the execexe loader path that bypasses LdrLoadDll. + g_poll_thread = std::thread(poll_thread); +} + +void graphics_d3d11_shutdown() { + std::lock_guard lock(g_init_mutex); + + // unregister first so the callback can't fire mid-teardown. + if (g_ldr_cookie) { + auto unreg = reinterpret_cast( + GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "LdrUnregisterDllNotification")); + if (unreg) { + unreg(g_ldr_cookie); + } + g_ldr_cookie = nullptr; + } + + g_stop.store(true); + if (g_poll_thread.joinable()) { + g_poll_thread.join(); + } +} + +#endif // SPICE_D3D11 diff --git a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_backend.h b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_backend.h index 6e06788..4288bad 100644 --- a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_backend.h +++ b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_backend.h @@ -1,24 +1,24 @@ -#pragma once - -#include "overlay/overlay.h" - -void graphics_d3d11_init(); -void graphics_d3d11_shutdown(); - -#ifdef SPICE_D3D11 - -struct ID3D11Device; -struct ID3D11DeviceContext; -struct ID3D11RenderTargetView; -struct IDXGISwapChain; - -namespace overlay::d3d11 { - - void render(ID3D11Device *device, - ID3D11DeviceContext *context, - IDXGISwapChain *swapchain, - ID3D11RenderTargetView **rtv); - -} - -#endif +#pragma once + +#include "overlay/overlay.h" + +void graphics_d3d11_init(); +void graphics_d3d11_shutdown(); + +#ifdef SPICE_D3D11 + +struct ID3D11Device; +struct ID3D11DeviceContext; +struct ID3D11RenderTargetView; +struct IDXGISwapChain; + +namespace overlay::d3d11 { + + void render(ID3D11Device *device, + ID3D11DeviceContext *context, + IDXGISwapChain *swapchain, + ID3D11RenderTargetView **rtv); + +} + +#endif diff --git a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_factory.cpp b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_factory.cpp index 381d3b3..d11f7f7 100644 --- a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_factory.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_factory.cpp @@ -1,102 +1,102 @@ -// dx11 factory vtable hooks. patches CreateSwapChain / CreateSwapChainForHwnd -// so we can install_swapchain_hooks against every newly-created swapchain. - -#include "d3d11_backend.h" - -#ifdef SPICE_D3D11 - -#include - -#include -#include -#include -#include - -#include "d3d11_internal.h" - -namespace { - -using CreateSwapChain_t = HRESULT(STDMETHODCALLTYPE *)( - IDXGIFactory *, IUnknown *, DXGI_SWAP_CHAIN_DESC *, IDXGISwapChain **); -using CreateSwapChainForHwnd_t = HRESULT(STDMETHODCALLTYPE *)( - IDXGIFactory2 *, IUnknown *, HWND, - const DXGI_SWAP_CHAIN_DESC1 *, - const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *, - IDXGIOutput *, IDXGISwapChain1 **); - -CreateSwapChain_t CreateSwapChain_orig = nullptr; -CreateSwapChainForHwnd_t CreateSwapChainForHwnd_orig = nullptr; - -bool g_factory_hooked = false; -bool g_factory2_hooked = false; -std::mutex g_hook_mutex; - -HRESULT STDMETHODCALLTYPE CreateSwapChain_hook( - IDXGIFactory *factory, IUnknown *pDevice, - DXGI_SWAP_CHAIN_DESC *pDesc, IDXGISwapChain **ppSwapChain) -{ - HRESULT res = CreateSwapChain_orig(factory, pDevice, pDesc, ppSwapChain); - if (SUCCEEDED(res) && ppSwapChain && *ppSwapChain) { - if (pDesc) { - d3d11_hooks::note_main_hwnd(pDesc->OutputWindow); - } - d3d11_hooks::install_swapchain_hooks(*ppSwapChain); - } - return res; -} - -HRESULT STDMETHODCALLTYPE CreateSwapChainForHwnd_hook( - IDXGIFactory2 *factory, IUnknown *pDevice, HWND hWnd, - const DXGI_SWAP_CHAIN_DESC1 *pDesc, - const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc, - IDXGIOutput *pRestrictToOutput, IDXGISwapChain1 **ppSwapChain) -{ - HRESULT res = CreateSwapChainForHwnd_orig( - factory, pDevice, hWnd, pDesc, pFullscreenDesc, pRestrictToOutput, ppSwapChain); - if (SUCCEEDED(res) && ppSwapChain && *ppSwapChain) { - d3d11_hooks::note_main_hwnd(hWnd); - d3d11_hooks::install_swapchain_hooks(*ppSwapChain); - } - return res; -} - -// QI-and-hook helper: dedupes the IDXGIFactory / IDXGIFactory2 install paths. -template -void install_on(IUnknown *factory, bool &flag, - size_t vtbl_index, void *hook, void **orig, const char *name) -{ - if (flag) { - return; - } - Iface *f = nullptr; - if (FAILED(factory->QueryInterface(IID_PPV_ARGS(&f))) || !f) { - return; - } - if (d3d11_hooks::hook_vtbl(f, vtbl_index, hook, orig, name)) { - flag = true; - } - f->Release(); -} - -} // namespace - -namespace d3d11_hooks { - -void install_factory_hooks(IUnknown *factory) { - if (!factory) { - return; - } - std::lock_guard lock(g_hook_mutex); - - install_on(factory, g_factory_hooked, 10, - (void *) CreateSwapChain_hook, (void **) &CreateSwapChain_orig, - "IDXGIFactory::CreateSwapChain"); - - install_on(factory, g_factory2_hooked, 15, - (void *) CreateSwapChainForHwnd_hook, (void **) &CreateSwapChainForHwnd_orig, - "IDXGIFactory2::CreateSwapChainForHwnd"); -} - -} - -#endif // SPICE_D3D11 +// dx11 factory vtable hooks. patches CreateSwapChain / CreateSwapChainForHwnd +// so we can install_swapchain_hooks against every newly-created swapchain. + +#include "d3d11_backend.h" + +#ifdef SPICE_D3D11 + +#include + +#include +#include +#include +#include + +#include "d3d11_internal.h" + +namespace { + +using CreateSwapChain_t = HRESULT(STDMETHODCALLTYPE *)( + IDXGIFactory *, IUnknown *, DXGI_SWAP_CHAIN_DESC *, IDXGISwapChain **); +using CreateSwapChainForHwnd_t = HRESULT(STDMETHODCALLTYPE *)( + IDXGIFactory2 *, IUnknown *, HWND, + const DXGI_SWAP_CHAIN_DESC1 *, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *, + IDXGIOutput *, IDXGISwapChain1 **); + +CreateSwapChain_t CreateSwapChain_orig = nullptr; +CreateSwapChainForHwnd_t CreateSwapChainForHwnd_orig = nullptr; + +bool g_factory_hooked = false; +bool g_factory2_hooked = false; +std::mutex g_hook_mutex; + +HRESULT STDMETHODCALLTYPE CreateSwapChain_hook( + IDXGIFactory *factory, IUnknown *pDevice, + DXGI_SWAP_CHAIN_DESC *pDesc, IDXGISwapChain **ppSwapChain) +{ + HRESULT res = CreateSwapChain_orig(factory, pDevice, pDesc, ppSwapChain); + if (SUCCEEDED(res) && ppSwapChain && *ppSwapChain) { + if (pDesc) { + d3d11_hooks::note_main_hwnd(pDesc->OutputWindow); + } + d3d11_hooks::install_swapchain_hooks(*ppSwapChain); + } + return res; +} + +HRESULT STDMETHODCALLTYPE CreateSwapChainForHwnd_hook( + IDXGIFactory2 *factory, IUnknown *pDevice, HWND hWnd, + const DXGI_SWAP_CHAIN_DESC1 *pDesc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc, + IDXGIOutput *pRestrictToOutput, IDXGISwapChain1 **ppSwapChain) +{ + HRESULT res = CreateSwapChainForHwnd_orig( + factory, pDevice, hWnd, pDesc, pFullscreenDesc, pRestrictToOutput, ppSwapChain); + if (SUCCEEDED(res) && ppSwapChain && *ppSwapChain) { + d3d11_hooks::note_main_hwnd(hWnd); + d3d11_hooks::install_swapchain_hooks(*ppSwapChain); + } + return res; +} + +// QI-and-hook helper: dedupes the IDXGIFactory / IDXGIFactory2 install paths. +template +void install_on(IUnknown *factory, bool &flag, + size_t vtbl_index, void *hook, void **orig, const char *name) +{ + if (flag) { + return; + } + Iface *f = nullptr; + if (FAILED(factory->QueryInterface(IID_PPV_ARGS(&f))) || !f) { + return; + } + if (d3d11_hooks::hook_vtbl(f, vtbl_index, hook, orig, name)) { + flag = true; + } + f->Release(); +} + +} // namespace + +namespace d3d11_hooks { + +void install_factory_hooks(IUnknown *factory) { + if (!factory) { + return; + } + std::lock_guard lock(g_hook_mutex); + + install_on(factory, g_factory_hooked, 10, + (void *) CreateSwapChain_hook, (void **) &CreateSwapChain_orig, + "IDXGIFactory::CreateSwapChain"); + + install_on(factory, g_factory2_hooked, 15, + (void *) CreateSwapChainForHwnd_hook, (void **) &CreateSwapChainForHwnd_orig, + "IDXGIFactory2::CreateSwapChainForHwnd"); +} + +} + +#endif // SPICE_D3D11 diff --git a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_internal.h b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_internal.h index 165288d..c0e5489 100644 --- a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_internal.h +++ b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_internal.h @@ -1,59 +1,59 @@ -#pragma once - -// internal glue for the dx11 backend. all symbols gated on SPICE_D3D11. - -#include "overlay/overlay.h" - -#ifdef SPICE_D3D11 - -#include - -#include "util/detour.h" -#include "util/logging.h" - -struct HWND__; typedef HWND__ *HWND; -struct IUnknown; -struct IDXGISwapChain; - -namespace d3d11_hooks { - - void install_swapchain_hooks(IDXGISwapChain *swapchain); - void install_factory_hooks(IUnknown *factory); - void try_capture_vtables(); - - // first non-null swapchain HWND wins; later ones (sub-screens, IME - // helpers) are ignored. the dummy capture window is exempted via - // ignore_hwnd. - void note_main_hwnd(HWND hwnd); - HWND main_hwnd(); - void ignore_hwnd(HWND hwnd); - - // capture backbuffer to PNG if a screenshot was requested. - void try_screenshot(IDXGISwapChain *swapchain); - - // trampoline a virtual method by vtable index. on failure *orig is null. - inline bool hook_vtbl(void *iface, size_t index, - void *hook, void **orig, const char *name) - { - void **vtbl = *reinterpret_cast(iface); - void *target = vtbl[index]; - // trampoline_try reads *orig before overwriting it. - *orig = target; - if (!detour::trampoline_try(target, hook, orig)) { - *orig = nullptr; - log_warning("graphics::d3d11", "failed to hook {}", name); - return false; - } - log_info("graphics::d3d11", "hooked {}", name); - return true; - } - - // minimal COM RAII used by capture / screenshot paths. - struct com_release { - void operator()(IUnknown *p) const { if (p) p->Release(); } - }; - template using com_ptr = std::unique_ptr; - -} - -#endif +#pragma once + +// internal glue for the dx11 backend. all symbols gated on SPICE_D3D11. + +#include "overlay/overlay.h" + +#ifdef SPICE_D3D11 + +#include + +#include "util/detour.h" +#include "util/logging.h" + +struct HWND__; typedef HWND__ *HWND; +struct IUnknown; +struct IDXGISwapChain; + +namespace d3d11_hooks { + + void install_swapchain_hooks(IDXGISwapChain *swapchain); + void install_factory_hooks(IUnknown *factory); + void try_capture_vtables(); + + // first non-null swapchain HWND wins; later ones (sub-screens, IME + // helpers) are ignored. the dummy capture window is exempted via + // ignore_hwnd. + void note_main_hwnd(HWND hwnd); + HWND main_hwnd(); + void ignore_hwnd(HWND hwnd); + + // capture backbuffer to PNG if a screenshot was requested. + void try_screenshot(IDXGISwapChain *swapchain); + + // trampoline a virtual method by vtable index. on failure *orig is null. + inline bool hook_vtbl(void *iface, size_t index, + void *hook, void **orig, const char *name) + { + void **vtbl = *reinterpret_cast(iface); + void *target = vtbl[index]; + // trampoline_try reads *orig before overwriting it. + *orig = target; + if (!detour::trampoline_try(target, hook, orig)) { + *orig = nullptr; + log_warning("graphics::d3d11", "failed to hook {}", name); + return false; + } + log_info("graphics::d3d11", "hooked {}", name); + return true; + } + + // minimal COM RAII used by capture / screenshot paths. + struct com_release { + void operator()(IUnknown *p) const { if (p) p->Release(); } + }; + template using com_ptr = std::unique_ptr; + +} + +#endif diff --git a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_screenshot.cpp b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_screenshot.cpp index af4ceac..bb204f0 100644 --- a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_screenshot.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_screenshot.cpp @@ -1,165 +1,165 @@ -// dx11 screenshot capture. mirrors the d3d9 backend: copy the current -// backbuffer into a staging texture, force alpha=255, write PNG via -// stb_image_write, push to clipboard and notify. - -#include "d3d11_backend.h" - -#ifdef SPICE_D3D11 - -#include - -#include -#include -#include - -#include "d3d11_internal.h" - -#include "external/stb_image_write.h" -#include "hooks/graphics/graphics.h" -#include "misc/clipboard.h" -#include "overlay/notifications.h" -#include "util/fileutils.h" - -using d3d11_hooks::com_ptr; - -namespace { - -// copy the swapchain backbuffer into a CPU-readable staging texture and -// flatten it into an RGBA8 buffer (BGRA backbuffers are swizzled, -// alpha is forced to 255). -bool copy_backbuffer_to_rgba(IDXGISwapChain *swapchain, - ID3D11Device *device, - ID3D11DeviceContext *context, - std::vector &out, - uint32_t &out_w, uint32_t &out_h) -{ - ID3D11Texture2D *raw_bb = nullptr; - if (FAILED(swapchain->GetBuffer(0, IID_PPV_ARGS(&raw_bb))) || !raw_bb) { - return false; - } - com_ptr backbuffer(raw_bb); - - D3D11_TEXTURE2D_DESC desc {}; - backbuffer->GetDesc(&desc); - - // MSAA backbuffers can't be CopyResource'd into a non-MS staging target. - com_ptr resolved; - ID3D11Texture2D *source = backbuffer.get(); - if (desc.SampleDesc.Count > 1) { - D3D11_TEXTURE2D_DESC rd = desc; - rd.SampleDesc.Count = 1; - rd.SampleDesc.Quality = 0; - rd.Usage = D3D11_USAGE_DEFAULT; - rd.BindFlags = D3D11_BIND_RENDER_TARGET; - rd.CPUAccessFlags = 0; - rd.MiscFlags = 0; - ID3D11Texture2D *r = nullptr; - if (FAILED(device->CreateTexture2D(&rd, nullptr, &r)) || !r) { - return false; - } - resolved.reset(r); - context->ResolveSubresource(resolved.get(), 0, backbuffer.get(), 0, desc.Format); - source = resolved.get(); - } - - D3D11_TEXTURE2D_DESC sd {}; - sd.Width = desc.Width; - sd.Height = desc.Height; - sd.MipLevels = 1; - sd.ArraySize = 1; - sd.Format = desc.Format; - sd.SampleDesc.Count = 1; - sd.Usage = D3D11_USAGE_STAGING; - sd.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - - ID3D11Texture2D *raw_staging = nullptr; - if (FAILED(device->CreateTexture2D(&sd, nullptr, &raw_staging)) || !raw_staging) { - return false; - } - com_ptr staging(raw_staging); - context->CopyResource(staging.get(), source); - - D3D11_MAPPED_SUBRESOURCE mapped {}; - if (FAILED(context->Map(staging.get(), 0, D3D11_MAP_READ, 0, &mapped))) { - return false; - } - - // backbuffers from GetDesc are always fully-typed (never _TYPELESS). - const bool is_bgra = desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM - || desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; - - out.resize(static_cast(desc.Width) * desc.Height * 4); - const uint8_t *src_base = reinterpret_cast(mapped.pData); - for (uint32_t y = 0; y < desc.Height; ++y) { - const uint8_t *row = src_base + static_cast(y) * mapped.RowPitch; - uint8_t *dst = out.data() + static_cast(y) * desc.Width * 4; - for (uint32_t x = 0; x < desc.Width; ++x) { - dst[x * 4 + 0] = row[x * 4 + (is_bgra ? 2 : 0)]; - dst[x * 4 + 1] = row[x * 4 + 1]; - dst[x * 4 + 2] = row[x * 4 + (is_bgra ? 0 : 2)]; - dst[x * 4 + 3] = 255; - } - } - - context->Unmap(staging.get(), 0); - - out_w = desc.Width; - out_h = desc.Height; - return true; -} - -} // namespace - -namespace d3d11_hooks { - -void try_screenshot(IDXGISwapChain *swapchain) { - if (!swapchain || !graphics_screenshot_consume()) { - return; - } - - auto file_path = graphics_screenshot_genpath(); - if (file_path.empty()) { - return; - } - - ID3D11Device *raw_device = nullptr; - if (FAILED(swapchain->GetDevice(IID_PPV_ARGS(&raw_device))) || !raw_device) { - return; - } - com_ptr device(raw_device); - ID3D11DeviceContext *raw_ctx = nullptr; - device->GetImmediateContext(&raw_ctx); - if (!raw_ctx) { - return; - } - com_ptr context(raw_ctx); - - std::vector pixels; - uint32_t w = 0, h = 0; - if (!copy_backbuffer_to_rgba(swapchain, device.get(), context.get(), pixels, w, h)) { - log_warning("graphics::d3d11", "screenshot: failed to capture backbuffer"); - overlay::notifications::add( - overlay::notifications::Severity::Error, - "Screenshot failed to capture"); - return; - } - - log_info("graphics::d3d11", "saving screenshot to {}", file_path); - if (stbi_write_png(file_path.c_str(), (int) w, (int) h, 4, - pixels.data(), (int) w * 4)) - { - clipboard::copy_image(file_path); - overlay::notifications::add( - overlay::notifications::Severity::Success, - fmt::format("Screenshot saved: {}", fileutils::basename(file_path))); - } else { - log_warning("graphics::d3d11", "screenshot: stbi_write_png failed"); - overlay::notifications::add( - overlay::notifications::Severity::Error, - "Screenshot failed to save"); - } -} - -} - -#endif // SPICE_D3D11 +// dx11 screenshot capture. mirrors the d3d9 backend: copy the current +// backbuffer into a staging texture, force alpha=255, write PNG via +// stb_image_write, push to clipboard and notify. + +#include "d3d11_backend.h" + +#ifdef SPICE_D3D11 + +#include + +#include +#include +#include + +#include "d3d11_internal.h" + +#include "external/stb_image_write.h" +#include "hooks/graphics/graphics.h" +#include "misc/clipboard.h" +#include "overlay/notifications.h" +#include "util/fileutils.h" + +using d3d11_hooks::com_ptr; + +namespace { + +// copy the swapchain backbuffer into a CPU-readable staging texture and +// flatten it into an RGBA8 buffer (BGRA backbuffers are swizzled, +// alpha is forced to 255). +bool copy_backbuffer_to_rgba(IDXGISwapChain *swapchain, + ID3D11Device *device, + ID3D11DeviceContext *context, + std::vector &out, + uint32_t &out_w, uint32_t &out_h) +{ + ID3D11Texture2D *raw_bb = nullptr; + if (FAILED(swapchain->GetBuffer(0, IID_PPV_ARGS(&raw_bb))) || !raw_bb) { + return false; + } + com_ptr backbuffer(raw_bb); + + D3D11_TEXTURE2D_DESC desc {}; + backbuffer->GetDesc(&desc); + + // MSAA backbuffers can't be CopyResource'd into a non-MS staging target. + com_ptr resolved; + ID3D11Texture2D *source = backbuffer.get(); + if (desc.SampleDesc.Count > 1) { + D3D11_TEXTURE2D_DESC rd = desc; + rd.SampleDesc.Count = 1; + rd.SampleDesc.Quality = 0; + rd.Usage = D3D11_USAGE_DEFAULT; + rd.BindFlags = D3D11_BIND_RENDER_TARGET; + rd.CPUAccessFlags = 0; + rd.MiscFlags = 0; + ID3D11Texture2D *r = nullptr; + if (FAILED(device->CreateTexture2D(&rd, nullptr, &r)) || !r) { + return false; + } + resolved.reset(r); + context->ResolveSubresource(resolved.get(), 0, backbuffer.get(), 0, desc.Format); + source = resolved.get(); + } + + D3D11_TEXTURE2D_DESC sd {}; + sd.Width = desc.Width; + sd.Height = desc.Height; + sd.MipLevels = 1; + sd.ArraySize = 1; + sd.Format = desc.Format; + sd.SampleDesc.Count = 1; + sd.Usage = D3D11_USAGE_STAGING; + sd.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + + ID3D11Texture2D *raw_staging = nullptr; + if (FAILED(device->CreateTexture2D(&sd, nullptr, &raw_staging)) || !raw_staging) { + return false; + } + com_ptr staging(raw_staging); + context->CopyResource(staging.get(), source); + + D3D11_MAPPED_SUBRESOURCE mapped {}; + if (FAILED(context->Map(staging.get(), 0, D3D11_MAP_READ, 0, &mapped))) { + return false; + } + + // backbuffers from GetDesc are always fully-typed (never _TYPELESS). + const bool is_bgra = desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM + || desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + + out.resize(static_cast(desc.Width) * desc.Height * 4); + const uint8_t *src_base = reinterpret_cast(mapped.pData); + for (uint32_t y = 0; y < desc.Height; ++y) { + const uint8_t *row = src_base + static_cast(y) * mapped.RowPitch; + uint8_t *dst = out.data() + static_cast(y) * desc.Width * 4; + for (uint32_t x = 0; x < desc.Width; ++x) { + dst[x * 4 + 0] = row[x * 4 + (is_bgra ? 2 : 0)]; + dst[x * 4 + 1] = row[x * 4 + 1]; + dst[x * 4 + 2] = row[x * 4 + (is_bgra ? 0 : 2)]; + dst[x * 4 + 3] = 255; + } + } + + context->Unmap(staging.get(), 0); + + out_w = desc.Width; + out_h = desc.Height; + return true; +} + +} // namespace + +namespace d3d11_hooks { + +void try_screenshot(IDXGISwapChain *swapchain) { + if (!swapchain || !graphics_screenshot_consume()) { + return; + } + + auto file_path = graphics_screenshot_genpath(); + if (file_path.empty()) { + return; + } + + ID3D11Device *raw_device = nullptr; + if (FAILED(swapchain->GetDevice(IID_PPV_ARGS(&raw_device))) || !raw_device) { + return; + } + com_ptr device(raw_device); + ID3D11DeviceContext *raw_ctx = nullptr; + device->GetImmediateContext(&raw_ctx); + if (!raw_ctx) { + return; + } + com_ptr context(raw_ctx); + + std::vector pixels; + uint32_t w = 0, h = 0; + if (!copy_backbuffer_to_rgba(swapchain, device.get(), context.get(), pixels, w, h)) { + log_warning("graphics::d3d11", "screenshot: failed to capture backbuffer"); + overlay::notifications::add( + overlay::notifications::Severity::Error, + "Screenshot failed to capture"); + return; + } + + log_info("graphics::d3d11", "saving screenshot to {}", file_path); + if (stbi_write_png(file_path.c_str(), (int) w, (int) h, 4, + pixels.data(), (int) w * 4)) + { + clipboard::copy_image(file_path); + overlay::notifications::add( + overlay::notifications::Severity::Success, + fmt::format("Screenshot saved: {}", fileutils::basename(file_path))); + } else { + log_warning("graphics::d3d11", "screenshot: stbi_write_png failed"); + overlay::notifications::add( + overlay::notifications::Severity::Error, + "Screenshot failed to save"); + } +} + +} + +#endif // SPICE_D3D11 diff --git a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_swapchain.cpp b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_swapchain.cpp index e38f8d6..92abb78 100644 --- a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_swapchain.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_swapchain.cpp @@ -1,343 +1,343 @@ -// dx11 swapchain vtable hooks + per-frame overlay pump. -// -// dxgi shares vtables across swapchain instances, so we only need to patch -// Present / Present1 / ResizeBuffers once on the first instance we see. -// each frame we lazily attach the overlay to whichever swapchain is -// presenting, then drive its imgui update / new_frame / render cycle. - -#include "d3d11_backend.h" - -#ifdef SPICE_D3D11 - -#include -#include - -#include -#include -#include -#include - -#include "d3d11_internal.h" - -#include "external/imgui/imgui.h" -#include "external/imgui/backends/imgui_impl_dx11.h" -#include "overlay/imgui/impl_spice.h" - -#include "hooks/graphics/graphics.h" -#include "util/utils.h" - -// -------------------------------------------------------------------------- -// overlay render bridge - -namespace overlay::d3d11 { - - // sRGB backbuffers need a UNORM view: ImGui vertex colors are already - // sRGB-encoded, so an extra linear->sRGB conversion would wash the - // overlay out white. - static DXGI_FORMAT to_unorm_view(DXGI_FORMAT fmt) { - switch (fmt) { - case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: return DXGI_FORMAT_R8G8B8A8_UNORM; - case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: return DXGI_FORMAT_B8G8R8A8_UNORM; - default: return fmt; - } - } - - static void ensure_rtv(ID3D11Device *device, - IDXGISwapChain *swapchain, - ID3D11RenderTargetView **rtv) - { - if (*rtv || !device || !swapchain) { - return; - } - ID3D11Texture2D *backbuffer = nullptr; - if (FAILED(swapchain->GetBuffer(0, IID_PPV_ARGS(&backbuffer))) || !backbuffer) { - return; - } - D3D11_TEXTURE2D_DESC td {}; - backbuffer->GetDesc(&td); - const DXGI_FORMAT view_fmt = to_unorm_view(td.Format); - if (view_fmt != td.Format) { - D3D11_RENDER_TARGET_VIEW_DESC rtvd {}; - rtvd.Format = view_fmt; - rtvd.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - device->CreateRenderTargetView(backbuffer, &rtvd, rtv); - } else { - device->CreateRenderTargetView(backbuffer, nullptr, rtv); - } - backbuffer->Release(); - } - - // bind the backbuffer (lazily creating the RTV) and draw the imgui - // frame on top. reset_invalidate releases *rtv on ResizeBuffers. - void render(ID3D11Device *device, - ID3D11DeviceContext *context, - IDXGISwapChain *swapchain, - ID3D11RenderTargetView **rtv) - { - ensure_rtv(device, swapchain, rtv); - if (!*rtv || !context) { - return; - } - // present happens immediately after, so no need to save the previous - // RT binding (flip-model resets it anyway). - context->OMSetRenderTargets(1, rtv, nullptr); - ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - } - -} - -// -------------------------------------------------------------------------- -// file-local state + per-frame helpers - -namespace { - -using Present_t = HRESULT(STDMETHODCALLTYPE *)( - IDXGISwapChain *, UINT, UINT); -using ResizeBuffers_t = HRESULT(STDMETHODCALLTYPE *)( - IDXGISwapChain *, UINT, UINT, UINT, DXGI_FORMAT, UINT); -using Present1_t = HRESULT(STDMETHODCALLTYPE *)( - IDXGISwapChain1 *, UINT, UINT, const DXGI_PRESENT_PARAMETERS *); - -Present_t Present_orig = nullptr; -ResizeBuffers_t ResizeBuffers_orig = nullptr; -Present1_t Present1_orig = nullptr; - -bool g_swapchain_hooked = false; -bool g_swapchain1_hooked = false; - -// sub-screens / IME helpers are usually child or zero-sized windows. -// visibility isn't checked - the game may present before showing the window. -bool looks_like_game_window(HWND hwnd) { - RECT client {}; - return GetAncestor(hwnd, GA_ROOT) == hwnd - && GetClientRect(hwnd, &client) - && client.right > client.left - && client.bottom > client.top; -} - -// only the main game window; ignore sub-screens / IME helpers. -bool is_main_game_swapchain(IDXGISwapChain *swapchain) { - DXGI_SWAP_CHAIN_DESC desc {}; - if (!swapchain || FAILED(swapchain->GetDesc(&desc)) || !desc.OutputWindow) { - return false; - } - - HWND main = d3d11_hooks::main_hwnd(); - if (!main) { - // no creation hook recorded a window, so fall back to the presenting one; - // the choice is permanent, so require a plausible game window - if (!looks_like_game_window(desc.OutputWindow)) { - return false; - } - - log_misc( - "graphics::d3d11", - "try to notemain hwnd from swapchain present: 0x{:x}", - (uintptr_t)desc.OutputWindow); - - d3d11_hooks::note_main_hwnd(desc.OutputWindow); - - // it may have been ignored, or another thread may have won the slot - main = d3d11_hooks::main_hwnd(); - } - return desc.OutputWindow == main; -} - -// checks are ordered cheapest first, since this runs on every present -void try_create_overlay(IDXGISwapChain *swapchain) { - if (!swapchain) { - return; - } - - // overlay is disabled by user - if (!overlay::ENABLED) { - return; - } - - // overlay is already enabled and attached - if (overlay::OVERLAY) { - return; - } - - // ignore sub windows - if (!is_main_game_swapchain(swapchain)) { - return; - } - - DXGI_SWAP_CHAIN_DESC desc {}; - if (FAILED(swapchain->GetDesc(&desc)) || !desc.OutputWindow) { - return; - } - - // theme the native title bar; first present is the only reliable point for - // windows whose swapchain bypasses our factory hooks (e.g. UnityPlayer.dll) - set_window_dark_titlebar(desc.OutputWindow); - - ID3D11Device *device = nullptr; - if (FAILED(swapchain->GetDevice(IID_PPV_ARGS(&device))) || !device) { - return; - } - ID3D11DeviceContext *context = nullptr; - device->GetImmediateContext(&context); - - if (context) { - overlay::create_d3d11(desc.OutputWindow, device, context, swapchain); - RECT cr {}; - ::GetClientRect(desc.OutputWindow, &cr); - log_info("graphics::d3d11", - "attached overlay to swapchain hwnd=0x{:x} backbuffer={}x{} client={}x{}", - (uintptr_t) desc.OutputWindow, - desc.BufferDesc.Width, desc.BufferDesc.Height, - cr.right - cr.left, cr.bottom - cr.top); - context->Release(); - } - device->Release(); -} - -// screenshots have to keep working with the overlay disabled, so they are not gated on it -void pump_frame(IDXGISwapChain *swapchain) { - const bool has_overlay = - overlay::OVERLAY && overlay::OVERLAY->uses_swapchain(swapchain); - if (!has_overlay && !is_main_game_swapchain(swapchain)) { - return; - } - - graphics_poll_screenshot_hotkey(); - - // before the overlay render so the screenshot excludes it - if (!GRAPHICS_SCREENSHOT_INCLUDE_OVERLAY) { - d3d11_hooks::try_screenshot(swapchain); - } - - if (has_overlay) { - - // size imgui to the backbuffer (not window client). dxgi may upscale - // a small backbuffer into a larger client rect; without this override - // imgui would draw past the RTV and the mouse mapping would be off. - DXGI_SWAP_CHAIN_DESC desc {}; - if (SUCCEEDED(swapchain->GetDesc(&desc))) { - ImGui_ImplSpice_SetDisplaySizeOverride( - (float) desc.BufferDesc.Width, - (float) desc.BufferDesc.Height); - } - - overlay::OVERLAY->update(); - overlay::OVERLAY->new_frame(); - overlay::OVERLAY->render(); - } - - // after the overlay render so the screenshot includes toasts / menus - if (GRAPHICS_SCREENSHOT_INCLUDE_OVERLAY) { - d3d11_hooks::try_screenshot(swapchain); - } -} - -// ---------------------------------------------------------------------- -// swapchain method hooks - -HRESULT STDMETHODCALLTYPE Present_hook( - IDXGISwapChain *swapchain, UINT SyncInterval, UINT Flags) -{ - // a test present doesn't display anything; don't pick a window or take a screenshot off it - if (!(Flags & DXGI_PRESENT_TEST)) { - try_create_overlay(swapchain); - pump_frame(swapchain); - } - return Present_orig(swapchain, SyncInterval, Flags); -} - -HRESULT STDMETHODCALLTYPE Present1_hook( - IDXGISwapChain1 *swapchain, UINT SyncInterval, UINT Flags, - const DXGI_PRESENT_PARAMETERS *pParams) -{ - if (!(Flags & DXGI_PRESENT_TEST)) { - try_create_overlay(swapchain); - pump_frame(swapchain); - } - return Present1_orig(swapchain, SyncInterval, Flags, pParams); -} - -HRESULT STDMETHODCALLTYPE ResizeBuffers_hook( - IDXGISwapChain *swapchain, UINT BufferCount, UINT Width, UINT Height, - DXGI_FORMAT NewFormat, UINT SwapChainFlags) -{ - const bool ours = overlay::OVERLAY && overlay::OVERLAY->uses_swapchain(swapchain); - if (ours) { - log_info("graphics::d3d11", "ResizeBuffers {}x{} fmt={}", - Width, Height, (int32_t) NewFormat); - overlay::OVERLAY->reset_invalidate(); - } - HRESULT res = ResizeBuffers_orig( - swapchain, BufferCount, Width, Height, NewFormat, SwapChainFlags); - if (ours && SUCCEEDED(res)) { - overlay::OVERLAY->reset_recreate(); - } - return res; -} - -} // namespace - -// -------------------------------------------------------------------------- -// d3d11_hooks public surface: main-window tracking + vtable install. - -namespace d3d11_hooks { - -namespace { - std::atomic g_main_hwnd { nullptr }; - std::atomic g_ignored_hwnd { nullptr }; -} - -void note_main_hwnd(HWND hwnd) { - if (!hwnd || hwnd == g_ignored_hwnd.load()) { - return; - } - HWND expected = nullptr; - if (g_main_hwnd.compare_exchange_strong(expected, hwnd)) { - log_info("graphics::d3d11", "main hwnd recorded: 0x{:x}", - (uintptr_t) hwnd); - } -} - -HWND main_hwnd() { - return g_main_hwnd.load(); -} - -void ignore_hwnd(HWND hwnd) { - g_ignored_hwnd.store(hwnd); -} - -// patch IDXGISwapChain::Present + ResizeBuffers and (if implemented) -// IDXGISwapChain1::Present1. idempotent; flag is set only after success -// so failed attempts can be retried on the next swapchain. -void install_swapchain_hooks(IDXGISwapChain *swapchain) { - if (!swapchain) { - return; - } - static std::mutex s_hook_mutex; - std::lock_guard lock(s_hook_mutex); - - if (!g_swapchain_hooked) { - const bool a = hook_vtbl(swapchain, 8, (void *) Present_hook, - (void **) &Present_orig, "IDXGISwapChain::Present"); - const bool b = hook_vtbl(swapchain, 13, (void *) ResizeBuffers_hook, - (void **) &ResizeBuffers_orig, "IDXGISwapChain::ResizeBuffers"); - if (a && b) { - g_swapchain_hooked = true; - } - } - - if (!g_swapchain1_hooked) { - IDXGISwapChain1 *sc1 = nullptr; - if (SUCCEEDED(swapchain->QueryInterface(IID_PPV_ARGS(&sc1))) && sc1) { - if (hook_vtbl(sc1, 22, (void *) Present1_hook, - (void **) &Present1_orig, "IDXGISwapChain1::Present1")) { - g_swapchain1_hooked = true; - } - sc1->Release(); - } - } -} - -} - -#endif // SPICE_D3D11 +// dx11 swapchain vtable hooks + per-frame overlay pump. +// +// dxgi shares vtables across swapchain instances, so we only need to patch +// Present / Present1 / ResizeBuffers once on the first instance we see. +// each frame we lazily attach the overlay to whichever swapchain is +// presenting, then drive its imgui update / new_frame / render cycle. + +#include "d3d11_backend.h" + +#ifdef SPICE_D3D11 + +#include +#include + +#include +#include +#include +#include + +#include "d3d11_internal.h" + +#include "external/imgui/imgui.h" +#include "external/imgui/backends/imgui_impl_dx11.h" +#include "overlay/imgui/impl_spice.h" + +#include "hooks/graphics/graphics.h" +#include "util/utils.h" + +// -------------------------------------------------------------------------- +// overlay render bridge + +namespace overlay::d3d11 { + + // sRGB backbuffers need a UNORM view: ImGui vertex colors are already + // sRGB-encoded, so an extra linear->sRGB conversion would wash the + // overlay out white. + static DXGI_FORMAT to_unorm_view(DXGI_FORMAT fmt) { + switch (fmt) { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: return DXGI_FORMAT_R8G8B8A8_UNORM; + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: return DXGI_FORMAT_B8G8R8A8_UNORM; + default: return fmt; + } + } + + static void ensure_rtv(ID3D11Device *device, + IDXGISwapChain *swapchain, + ID3D11RenderTargetView **rtv) + { + if (*rtv || !device || !swapchain) { + return; + } + ID3D11Texture2D *backbuffer = nullptr; + if (FAILED(swapchain->GetBuffer(0, IID_PPV_ARGS(&backbuffer))) || !backbuffer) { + return; + } + D3D11_TEXTURE2D_DESC td {}; + backbuffer->GetDesc(&td); + const DXGI_FORMAT view_fmt = to_unorm_view(td.Format); + if (view_fmt != td.Format) { + D3D11_RENDER_TARGET_VIEW_DESC rtvd {}; + rtvd.Format = view_fmt; + rtvd.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + device->CreateRenderTargetView(backbuffer, &rtvd, rtv); + } else { + device->CreateRenderTargetView(backbuffer, nullptr, rtv); + } + backbuffer->Release(); + } + + // bind the backbuffer (lazily creating the RTV) and draw the imgui + // frame on top. reset_invalidate releases *rtv on ResizeBuffers. + void render(ID3D11Device *device, + ID3D11DeviceContext *context, + IDXGISwapChain *swapchain, + ID3D11RenderTargetView **rtv) + { + ensure_rtv(device, swapchain, rtv); + if (!*rtv || !context) { + return; + } + // present happens immediately after, so no need to save the previous + // RT binding (flip-model resets it anyway). + context->OMSetRenderTargets(1, rtv, nullptr); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + } + +} + +// -------------------------------------------------------------------------- +// file-local state + per-frame helpers + +namespace { + +using Present_t = HRESULT(STDMETHODCALLTYPE *)( + IDXGISwapChain *, UINT, UINT); +using ResizeBuffers_t = HRESULT(STDMETHODCALLTYPE *)( + IDXGISwapChain *, UINT, UINT, UINT, DXGI_FORMAT, UINT); +using Present1_t = HRESULT(STDMETHODCALLTYPE *)( + IDXGISwapChain1 *, UINT, UINT, const DXGI_PRESENT_PARAMETERS *); + +Present_t Present_orig = nullptr; +ResizeBuffers_t ResizeBuffers_orig = nullptr; +Present1_t Present1_orig = nullptr; + +bool g_swapchain_hooked = false; +bool g_swapchain1_hooked = false; + +// sub-screens / IME helpers are usually child or zero-sized windows. +// visibility isn't checked - the game may present before showing the window. +bool looks_like_game_window(HWND hwnd) { + RECT client {}; + return GetAncestor(hwnd, GA_ROOT) == hwnd + && GetClientRect(hwnd, &client) + && client.right > client.left + && client.bottom > client.top; +} + +// only the main game window; ignore sub-screens / IME helpers. +bool is_main_game_swapchain(IDXGISwapChain *swapchain) { + DXGI_SWAP_CHAIN_DESC desc {}; + if (!swapchain || FAILED(swapchain->GetDesc(&desc)) || !desc.OutputWindow) { + return false; + } + + HWND main = d3d11_hooks::main_hwnd(); + if (!main) { + // no creation hook recorded a window, so fall back to the presenting one; + // the choice is permanent, so require a plausible game window + if (!looks_like_game_window(desc.OutputWindow)) { + return false; + } + + log_misc( + "graphics::d3d11", + "try to notemain hwnd from swapchain present: 0x{:x}", + (uintptr_t)desc.OutputWindow); + + d3d11_hooks::note_main_hwnd(desc.OutputWindow); + + // it may have been ignored, or another thread may have won the slot + main = d3d11_hooks::main_hwnd(); + } + return desc.OutputWindow == main; +} + +// checks are ordered cheapest first, since this runs on every present +void try_create_overlay(IDXGISwapChain *swapchain) { + if (!swapchain) { + return; + } + + // overlay is disabled by user + if (!overlay::ENABLED) { + return; + } + + // overlay is already enabled and attached + if (overlay::OVERLAY) { + return; + } + + // ignore sub windows + if (!is_main_game_swapchain(swapchain)) { + return; + } + + DXGI_SWAP_CHAIN_DESC desc {}; + if (FAILED(swapchain->GetDesc(&desc)) || !desc.OutputWindow) { + return; + } + + // theme the native title bar; first present is the only reliable point for + // windows whose swapchain bypasses our factory hooks (e.g. UnityPlayer.dll) + set_window_dark_titlebar(desc.OutputWindow); + + ID3D11Device *device = nullptr; + if (FAILED(swapchain->GetDevice(IID_PPV_ARGS(&device))) || !device) { + return; + } + ID3D11DeviceContext *context = nullptr; + device->GetImmediateContext(&context); + + if (context) { + overlay::create_d3d11(desc.OutputWindow, device, context, swapchain); + RECT cr {}; + ::GetClientRect(desc.OutputWindow, &cr); + log_info("graphics::d3d11", + "attached overlay to swapchain hwnd=0x{:x} backbuffer={}x{} client={}x{}", + (uintptr_t) desc.OutputWindow, + desc.BufferDesc.Width, desc.BufferDesc.Height, + cr.right - cr.left, cr.bottom - cr.top); + context->Release(); + } + device->Release(); +} + +// screenshots have to keep working with the overlay disabled, so they are not gated on it +void pump_frame(IDXGISwapChain *swapchain) { + const bool has_overlay = + overlay::OVERLAY && overlay::OVERLAY->uses_swapchain(swapchain); + if (!has_overlay && !is_main_game_swapchain(swapchain)) { + return; + } + + graphics_poll_screenshot_hotkey(); + + // before the overlay render so the screenshot excludes it + if (!GRAPHICS_SCREENSHOT_INCLUDE_OVERLAY) { + d3d11_hooks::try_screenshot(swapchain); + } + + if (has_overlay) { + + // size imgui to the backbuffer (not window client). dxgi may upscale + // a small backbuffer into a larger client rect; without this override + // imgui would draw past the RTV and the mouse mapping would be off. + DXGI_SWAP_CHAIN_DESC desc {}; + if (SUCCEEDED(swapchain->GetDesc(&desc))) { + ImGui_ImplSpice_SetDisplaySizeOverride( + (float) desc.BufferDesc.Width, + (float) desc.BufferDesc.Height); + } + + overlay::OVERLAY->update(); + overlay::OVERLAY->new_frame(); + overlay::OVERLAY->render(); + } + + // after the overlay render so the screenshot includes toasts / menus + if (GRAPHICS_SCREENSHOT_INCLUDE_OVERLAY) { + d3d11_hooks::try_screenshot(swapchain); + } +} + +// ---------------------------------------------------------------------- +// swapchain method hooks + +HRESULT STDMETHODCALLTYPE Present_hook( + IDXGISwapChain *swapchain, UINT SyncInterval, UINT Flags) +{ + // a test present doesn't display anything; don't pick a window or take a screenshot off it + if (!(Flags & DXGI_PRESENT_TEST)) { + try_create_overlay(swapchain); + pump_frame(swapchain); + } + return Present_orig(swapchain, SyncInterval, Flags); +} + +HRESULT STDMETHODCALLTYPE Present1_hook( + IDXGISwapChain1 *swapchain, UINT SyncInterval, UINT Flags, + const DXGI_PRESENT_PARAMETERS *pParams) +{ + if (!(Flags & DXGI_PRESENT_TEST)) { + try_create_overlay(swapchain); + pump_frame(swapchain); + } + return Present1_orig(swapchain, SyncInterval, Flags, pParams); +} + +HRESULT STDMETHODCALLTYPE ResizeBuffers_hook( + IDXGISwapChain *swapchain, UINT BufferCount, UINT Width, UINT Height, + DXGI_FORMAT NewFormat, UINT SwapChainFlags) +{ + const bool ours = overlay::OVERLAY && overlay::OVERLAY->uses_swapchain(swapchain); + if (ours) { + log_info("graphics::d3d11", "ResizeBuffers {}x{} fmt={}", + Width, Height, (int32_t) NewFormat); + overlay::OVERLAY->reset_invalidate(); + } + HRESULT res = ResizeBuffers_orig( + swapchain, BufferCount, Width, Height, NewFormat, SwapChainFlags); + if (ours && SUCCEEDED(res)) { + overlay::OVERLAY->reset_recreate(); + } + return res; +} + +} // namespace + +// -------------------------------------------------------------------------- +// d3d11_hooks public surface: main-window tracking + vtable install. + +namespace d3d11_hooks { + +namespace { + std::atomic g_main_hwnd { nullptr }; + std::atomic g_ignored_hwnd { nullptr }; +} + +void note_main_hwnd(HWND hwnd) { + if (!hwnd || hwnd == g_ignored_hwnd.load()) { + return; + } + HWND expected = nullptr; + if (g_main_hwnd.compare_exchange_strong(expected, hwnd)) { + log_info("graphics::d3d11", "main hwnd recorded: 0x{:x}", + (uintptr_t) hwnd); + } +} + +HWND main_hwnd() { + return g_main_hwnd.load(); +} + +void ignore_hwnd(HWND hwnd) { + g_ignored_hwnd.store(hwnd); +} + +// patch IDXGISwapChain::Present + ResizeBuffers and (if implemented) +// IDXGISwapChain1::Present1. idempotent; flag is set only after success +// so failed attempts can be retried on the next swapchain. +void install_swapchain_hooks(IDXGISwapChain *swapchain) { + if (!swapchain) { + return; + } + static std::mutex s_hook_mutex; + std::lock_guard lock(s_hook_mutex); + + if (!g_swapchain_hooked) { + const bool a = hook_vtbl(swapchain, 8, (void *) Present_hook, + (void **) &Present_orig, "IDXGISwapChain::Present"); + const bool b = hook_vtbl(swapchain, 13, (void *) ResizeBuffers_hook, + (void **) &ResizeBuffers_orig, "IDXGISwapChain::ResizeBuffers"); + if (a && b) { + g_swapchain_hooked = true; + } + } + + if (!g_swapchain1_hooked) { + IDXGISwapChain1 *sc1 = nullptr; + if (SUCCEEDED(swapchain->QueryInterface(IID_PPV_ARGS(&sc1))) && sc1) { + if (hook_vtbl(sc1, 22, (void *) Present1_hook, + (void **) &Present1_orig, "IDXGISwapChain1::Present1")) { + g_swapchain1_hooked = true; + } + sc1->Release(); + } + } +} + +} + +#endif // SPICE_D3D11 diff --git a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_vtable_capture.cpp b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_vtable_capture.cpp index 28601c2..c6d5da2 100644 --- a/src/spice2x/hooks/graphics/backends/d3d11/d3d11_vtable_capture.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d11/d3d11_vtable_capture.cpp @@ -1,175 +1,175 @@ -// proactive vtable capture for the dx11 backend. -// -// titles under the execexe loader routinely race past our export-level -// trampolines, so the game's first real swapchain never goes through us. -// we sidestep that by creating a throwaway device + swapchain ourselves -// the moment d3d11.dll + dxgi.dll appear, which patches the shared -// IDXGISwapChain[1] / IDXGIFactory[2] vtables ahead of the game. - -#include "d3d11_backend.h" - -#ifdef SPICE_D3D11 - -#include -#include - -#include -#include -#include -#include - -#include "d3d11_internal.h" - -using d3d11_hooks::com_ptr; - -namespace { - -using D3D11CreateDevice_t = HRESULT(WINAPI *)( - IDXGIAdapter *, D3D_DRIVER_TYPE, HMODULE, UINT, - const D3D_FEATURE_LEVEL *, UINT, UINT, - ID3D11Device **, D3D_FEATURE_LEVEL *, ID3D11DeviceContext **); -using CreateDXGIFactory1_t = HRESULT(WINAPI *)(REFIID, void **); -using CreateDXGIFactory2_t = HRESULT(WINAPI *)(UINT, REFIID, void **); - -std::atomic g_vtables_captured { false }; - -template -Fn resolve(HMODULE mod, const char *name) { - return reinterpret_cast(GetProcAddress(mod, name)); -} - -com_ptr create_factory2(CreateDXGIFactory2_t f2, - CreateDXGIFactory1_t f1) -{ - IDXGIFactory2 *raw = nullptr; - if (f2 && SUCCEEDED(f2(0, IID_PPV_ARGS(&raw))) && raw) { - return com_ptr(raw); - } - IDXGIFactory1 *factory1 = nullptr; - if (f1 && SUCCEEDED(f1(IID_PPV_ARGS(&factory1))) && factory1) { - factory1->QueryInterface(IID_PPV_ARGS(&raw)); - factory1->Release(); - } - return com_ptr(raw); -} - -bool create_dummy_device(D3D11CreateDevice_t create, - com_ptr &device, - com_ptr &context) -{ - static constexpr D3D_FEATURE_LEVEL levels[] = { - D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, - }; - // hardware first, then WARP so headless / unusual configs still work. - for (auto type : { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP }) { - ID3D11Device *d = nullptr; - ID3D11DeviceContext *c = nullptr; - D3D_FEATURE_LEVEL got; - if (SUCCEEDED(create(nullptr, type, nullptr, 0, - levels, ARRAYSIZE(levels), D3D11_SDK_VERSION, - &d, &got, &c)) && d) { - device.reset(d); - context.reset(c); - return true; - } - } - return false; -} - -} // namespace - -namespace d3d11_hooks { - -// create a throwaway device + swapchain to patch the shared vtables before -// the game's loader races past our export trampolines. safe to call -// repeatedly; runs at most once. -void try_capture_vtables() { - if (g_vtables_captured.load()) { - return; - } - - HMODULE d3d11 = GetModuleHandleW(L"d3d11.dll"); - HMODULE dxgi = GetModuleHandleW(L"dxgi.dll"); - if (!d3d11 || !dxgi) { - return; - } - - auto create_device = resolve(d3d11, "D3D11CreateDevice"); - auto f2 = resolve(dxgi, "CreateDXGIFactory2"); - auto f1 = resolve(dxgi, "CreateDXGIFactory1"); - if (!create_device || (!f1 && !f2)) { - return; - } - - // serialize concurrent calls (poll thread + LDR notification). only - // flip g_vtables_captured after success so failed attempts remain - // retriable on the next tick. - static std::atomic in_progress { false }; - if (in_progress.exchange(true)) { - return; - } - struct scope_clear { - std::atomic &flag; - ~scope_clear() { flag.store(false); } - } clear { in_progress }; - - // hidden message-only window; STATIC is always registered by user32. - HWND dummy_hwnd = CreateWindowExW( - 0, L"STATIC", L"", 0, 0, 0, 1, 1, - HWND_MESSAGE, nullptr, GetModuleHandleW(nullptr), nullptr); - if (!dummy_hwnd) { - log_warning("graphics::d3d11", - "vtable capture: CreateWindowExW failed (gle={})", (unsigned long)GetLastError()); - return; - } - auto destroy_hwnd = std::unique_ptr( - dummy_hwnd, &DestroyWindow); - - // if the game's CreateDXGIFactory_hook already raced us, our - // CreateSwapChainForHwnd call below would trip the hook and try to - // record dummy_hwnd as the main window. block that. - ignore_hwnd(dummy_hwnd); - - auto factory2 = create_factory2(f2, f1); - if (!factory2) { - log_warning("graphics::d3d11", "vtable capture: CreateDXGIFactory* failed"); - return; - } - - com_ptr device; - com_ptr context; - if (!create_dummy_device(create_device, device, context)) { - log_warning("graphics::d3d11", "vtable capture: D3D11CreateDevice failed"); - return; - } - - DXGI_SWAP_CHAIN_DESC1 desc {}; - desc.Width = 1; - desc.Height = 1; - desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - desc.SampleDesc.Count = 1; - desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - desc.BufferCount = 2; - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - - IDXGISwapChain1 *raw_sc = nullptr; - HRESULT hr = factory2->CreateSwapChainForHwnd( - device.get(), dummy_hwnd, &desc, nullptr, nullptr, &raw_sc); - if (FAILED(hr) || !raw_sc) { - log_warning("graphics::d3d11", - "vtable capture: CreateSwapChainForHwnd failed (hr={:#x})", (unsigned long)hr); - return; - } - com_ptr swapchain(raw_sc); - - install_swapchain_hooks(swapchain.get()); - install_factory_hooks(factory2.get()); - - g_vtables_captured.store(true); - log_info("graphics::d3d11", "vtable capture complete (via dummy swapchain)"); -} - -} - -#endif // SPICE_D3D11 +// proactive vtable capture for the dx11 backend. +// +// titles under the execexe loader routinely race past our export-level +// trampolines, so the game's first real swapchain never goes through us. +// we sidestep that by creating a throwaway device + swapchain ourselves +// the moment d3d11.dll + dxgi.dll appear, which patches the shared +// IDXGISwapChain[1] / IDXGIFactory[2] vtables ahead of the game. + +#include "d3d11_backend.h" + +#ifdef SPICE_D3D11 + +#include +#include + +#include +#include +#include +#include + +#include "d3d11_internal.h" + +using d3d11_hooks::com_ptr; + +namespace { + +using D3D11CreateDevice_t = HRESULT(WINAPI *)( + IDXGIAdapter *, D3D_DRIVER_TYPE, HMODULE, UINT, + const D3D_FEATURE_LEVEL *, UINT, UINT, + ID3D11Device **, D3D_FEATURE_LEVEL *, ID3D11DeviceContext **); +using CreateDXGIFactory1_t = HRESULT(WINAPI *)(REFIID, void **); +using CreateDXGIFactory2_t = HRESULT(WINAPI *)(UINT, REFIID, void **); + +std::atomic g_vtables_captured { false }; + +template +Fn resolve(HMODULE mod, const char *name) { + return reinterpret_cast(GetProcAddress(mod, name)); +} + +com_ptr create_factory2(CreateDXGIFactory2_t f2, + CreateDXGIFactory1_t f1) +{ + IDXGIFactory2 *raw = nullptr; + if (f2 && SUCCEEDED(f2(0, IID_PPV_ARGS(&raw))) && raw) { + return com_ptr(raw); + } + IDXGIFactory1 *factory1 = nullptr; + if (f1 && SUCCEEDED(f1(IID_PPV_ARGS(&factory1))) && factory1) { + factory1->QueryInterface(IID_PPV_ARGS(&raw)); + factory1->Release(); + } + return com_ptr(raw); +} + +bool create_dummy_device(D3D11CreateDevice_t create, + com_ptr &device, + com_ptr &context) +{ + static constexpr D3D_FEATURE_LEVEL levels[] = { + D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, + }; + // hardware first, then WARP so headless / unusual configs still work. + for (auto type : { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP }) { + ID3D11Device *d = nullptr; + ID3D11DeviceContext *c = nullptr; + D3D_FEATURE_LEVEL got; + if (SUCCEEDED(create(nullptr, type, nullptr, 0, + levels, ARRAYSIZE(levels), D3D11_SDK_VERSION, + &d, &got, &c)) && d) { + device.reset(d); + context.reset(c); + return true; + } + } + return false; +} + +} // namespace + +namespace d3d11_hooks { + +// create a throwaway device + swapchain to patch the shared vtables before +// the game's loader races past our export trampolines. safe to call +// repeatedly; runs at most once. +void try_capture_vtables() { + if (g_vtables_captured.load()) { + return; + } + + HMODULE d3d11 = GetModuleHandleW(L"d3d11.dll"); + HMODULE dxgi = GetModuleHandleW(L"dxgi.dll"); + if (!d3d11 || !dxgi) { + return; + } + + auto create_device = resolve(d3d11, "D3D11CreateDevice"); + auto f2 = resolve(dxgi, "CreateDXGIFactory2"); + auto f1 = resolve(dxgi, "CreateDXGIFactory1"); + if (!create_device || (!f1 && !f2)) { + return; + } + + // serialize concurrent calls (poll thread + LDR notification). only + // flip g_vtables_captured after success so failed attempts remain + // retriable on the next tick. + static std::atomic in_progress { false }; + if (in_progress.exchange(true)) { + return; + } + struct scope_clear { + std::atomic &flag; + ~scope_clear() { flag.store(false); } + } clear { in_progress }; + + // hidden message-only window; STATIC is always registered by user32. + HWND dummy_hwnd = CreateWindowExW( + 0, L"STATIC", L"", 0, 0, 0, 1, 1, + HWND_MESSAGE, nullptr, GetModuleHandleW(nullptr), nullptr); + if (!dummy_hwnd) { + log_warning("graphics::d3d11", + "vtable capture: CreateWindowExW failed (gle={})", (unsigned long)GetLastError()); + return; + } + auto destroy_hwnd = std::unique_ptr( + dummy_hwnd, &DestroyWindow); + + // if the game's CreateDXGIFactory_hook already raced us, our + // CreateSwapChainForHwnd call below would trip the hook and try to + // record dummy_hwnd as the main window. block that. + ignore_hwnd(dummy_hwnd); + + auto factory2 = create_factory2(f2, f1); + if (!factory2) { + log_warning("graphics::d3d11", "vtable capture: CreateDXGIFactory* failed"); + return; + } + + com_ptr device; + com_ptr context; + if (!create_dummy_device(create_device, device, context)) { + log_warning("graphics::d3d11", "vtable capture: D3D11CreateDevice failed"); + return; + } + + DXGI_SWAP_CHAIN_DESC1 desc {}; + desc.Width = 1; + desc.Height = 1; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.BufferCount = 2; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + + IDXGISwapChain1 *raw_sc = nullptr; + HRESULT hr = factory2->CreateSwapChainForHwnd( + device.get(), dummy_hwnd, &desc, nullptr, nullptr, &raw_sc); + if (FAILED(hr) || !raw_sc) { + log_warning("graphics::d3d11", + "vtable capture: CreateSwapChainForHwnd failed (hr={:#x})", (unsigned long)hr); + return; + } + com_ptr swapchain(raw_sc); + + install_swapchain_hooks(swapchain.get()); + install_factory_hooks(factory2.get()); + + g_vtables_captured.store(true); + log_info("graphics::d3d11", "vtable capture complete (via dummy swapchain)"); +} + +} + +#endif // SPICE_D3D11 diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_live2d.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_live2d.cpp index a5f81d4..0bd8fcb 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_live2d.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_live2d.cpp @@ -1,144 +1,144 @@ -#include "d3d9_live2d.h" - -// only the Live2D-capable SDVX versions are 64-bit, so the entire implementation -// is compiled out of 32-bit builds (the header supplies inline no-op stubs there). -#ifdef SPICE64 - -#include -#include - -#include "hooks/graphics/graphics.h" - -// how the Live2D draw filtering works -// ------------------------------------ -// SDVX draws its Live2D characters with a small, fixed set of pixel and -// vertex shaders. to skip those draws (and save GPU) we have to recognise them at -// the exact moment the game issues a draw call. the d3d9 device hooks feed three -// kinds of events into this module: -// -// 1. shader creation (on_create_pixel_shader / on_create_vertex_shader) -// the game compiles its shaders once at load. we can't trust the shader -// *object pointer* to identify a shader (it's just a heap address that -// varies per run and can be recycled), so instead we hash the shader's -// D3D9 *bytecode* - that fingerprint is stable across runs because the -// game ships the same shaders. if the hash matches a known Live2D shader -// we remember that object pointer in g_live2d_shaders. -// -// 2. shader binding (on_set_pixel_shader / on_set_vertex_shader) -// whenever the game binds a shader we look it up in that set once and cache -// the yes/no answer in g_cur_ps_is_live2d / g_cur_vs_is_live2d. binds happen -// far less often than draws, so this is where the lookup cost lives. -// -// 3. draw call (should_skip_draw, called from every Draw* hook) -// the per-draw question "is this a Live2D draw?" is then just reading those -// two cached bools - no hashing, no map lookups. if the skip is currently -// active (see graphics_sdvx_live2d_should_skip) and either bound shader is -// Live2D, the Draw* hook drops the call instead of forwarding it. -// -// everything is gated on the feature being enabled (mode != Off); when it's Off -// every entry point is a single predicted-not-taken branch. d3d9 rendering for a -// device is single-threaded, so none of this state needs locking. - -namespace { - -// shader state is tracked whenever the feature might act (mode != Off) so the -// known-shader set is populated before a song starts. when Off, every entry -// point is a single cheap branch. -bool tracking_enabled() { - return GRAPHICS_SDVX_LIVE2D_MODE != SdvxLive2dMode::Off; -} - -// the set of shader objects (pixel or vertex) whose bytecode matched a known -// Live2D fingerprint. only matching shaders are stored, so this stays tiny. -std::unordered_set g_live2d_shaders; - -// whether the currently-bound shaders are known Live2D shaders. cached at set -// time so the per-draw check is just two bool reads. -bool g_cur_ps_is_live2d = false; -bool g_cur_vs_is_live2d = false; - -// FNV-1a 64 over a D3D9 shader token stream (ends with D3DSIO_END = 0x0000FFFF) -uint64_t bytecode_hash(const DWORD *func) { - if (func == nullptr) { - return 0; - } - const DWORD *p = func; - const DWORD *cap = func + 65536; // safety bound - while (p < cap && *p != 0x0000FFFF) { - p++; - } - const size_t n_bytes = ((size_t)(p - func) + 1) * sizeof(DWORD); - uint64_t h = 1469598103934665603ULL; - const auto *bytes = reinterpret_cast(func); - for (size_t i = 0; i < n_bytes; i++) { - h ^= bytes[i]; - h *= 1099511628211ULL; - } - return h; -} - -// known SDVX Live2D shader bytecode hashes (4 pixel + 3 vertex). stable -// across runs because the game ships fixed shaders. the two sets are disjoint so -// a single shader can be classified by its own hash alone. -bool hash_is_live2d(uint64_t hash) { - switch (hash) { - case 0x75c89951817421a4ULL: // pixel: dominant model draw (~4.9M prims/120f in-song) - case 0x2d7ce428c6b4775dULL: // pixel: masked model draw - case 0x3ce00cc6111c10e7ULL: // pixel: mask generation - case 0x8bb3a2f37150ac34ULL: // pixel: mask generation (variant) - case 0xe9cf898c331e2a51ULL: // vertex - case 0x94dc84e7b7c0f437ULL: // vertex - case 0xc872937c5cc04309ULL: // vertex - return true; - } - return false; -} - -// classify a shader at creation time and record it if it is Live2D. erasing on a -// miss keeps the set correct if the runtime reuses a freed shader pointer. -void classify_shader(void *shader, const DWORD *func) { - if (hash_is_live2d(bytecode_hash(func))) { - g_live2d_shaders.insert(shader); - } else { - g_live2d_shaders.erase(shader); - } -} - -} // namespace - -namespace d3d9_live2d { - -// stage 1: fingerprint each shader as the game creates it -void on_create_vertex_shader(IDirect3DVertexShader9 *shader, const DWORD *func) { - if (tracking_enabled() && shader != nullptr) [[unlikely]] { - classify_shader(shader, func); - } -} - -void on_create_pixel_shader(IDirect3DPixelShader9 *shader, const DWORD *func) { - if (tracking_enabled() && shader != nullptr) [[unlikely]] { - classify_shader(shader, func); - } -} - -// stage 2: remember whether the just-bound shader is a Live2D one -void on_set_vertex_shader(IDirect3DVertexShader9 *shader) { - if (tracking_enabled()) [[unlikely]] { - g_cur_vs_is_live2d = g_live2d_shaders.count(shader) != 0; - } -} - -void on_set_pixel_shader(IDirect3DPixelShader9 *shader) { - if (tracking_enabled()) [[unlikely]] { - g_cur_ps_is_live2d = g_live2d_shaders.count(shader) != 0; - } -} - -// stage 3: drop the draw if the skip is active and a Live2D shader is bound -bool should_skip_draw() { - return graphics_sdvx_live2d_should_skip() && (g_cur_ps_is_live2d || g_cur_vs_is_live2d); -} - -} // namespace d3d9_live2d - -#endif // SPICE64 +#include "d3d9_live2d.h" + +// only the Live2D-capable SDVX versions are 64-bit, so the entire implementation +// is compiled out of 32-bit builds (the header supplies inline no-op stubs there). +#ifdef SPICE64 + +#include +#include + +#include "hooks/graphics/graphics.h" + +// how the Live2D draw filtering works +// ------------------------------------ +// SDVX draws its Live2D characters with a small, fixed set of pixel and +// vertex shaders. to skip those draws (and save GPU) we have to recognise them at +// the exact moment the game issues a draw call. the d3d9 device hooks feed three +// kinds of events into this module: +// +// 1. shader creation (on_create_pixel_shader / on_create_vertex_shader) +// the game compiles its shaders once at load. we can't trust the shader +// *object pointer* to identify a shader (it's just a heap address that +// varies per run and can be recycled), so instead we hash the shader's +// D3D9 *bytecode* - that fingerprint is stable across runs because the +// game ships the same shaders. if the hash matches a known Live2D shader +// we remember that object pointer in g_live2d_shaders. +// +// 2. shader binding (on_set_pixel_shader / on_set_vertex_shader) +// whenever the game binds a shader we look it up in that set once and cache +// the yes/no answer in g_cur_ps_is_live2d / g_cur_vs_is_live2d. binds happen +// far less often than draws, so this is where the lookup cost lives. +// +// 3. draw call (should_skip_draw, called from every Draw* hook) +// the per-draw question "is this a Live2D draw?" is then just reading those +// two cached bools - no hashing, no map lookups. if the skip is currently +// active (see graphics_sdvx_live2d_should_skip) and either bound shader is +// Live2D, the Draw* hook drops the call instead of forwarding it. +// +// everything is gated on the feature being enabled (mode != Off); when it's Off +// every entry point is a single predicted-not-taken branch. d3d9 rendering for a +// device is single-threaded, so none of this state needs locking. + +namespace { + +// shader state is tracked whenever the feature might act (mode != Off) so the +// known-shader set is populated before a song starts. when Off, every entry +// point is a single cheap branch. +bool tracking_enabled() { + return GRAPHICS_SDVX_LIVE2D_MODE != SdvxLive2dMode::Off; +} + +// the set of shader objects (pixel or vertex) whose bytecode matched a known +// Live2D fingerprint. only matching shaders are stored, so this stays tiny. +std::unordered_set g_live2d_shaders; + +// whether the currently-bound shaders are known Live2D shaders. cached at set +// time so the per-draw check is just two bool reads. +bool g_cur_ps_is_live2d = false; +bool g_cur_vs_is_live2d = false; + +// FNV-1a 64 over a D3D9 shader token stream (ends with D3DSIO_END = 0x0000FFFF) +uint64_t bytecode_hash(const DWORD *func) { + if (func == nullptr) { + return 0; + } + const DWORD *p = func; + const DWORD *cap = func + 65536; // safety bound + while (p < cap && *p != 0x0000FFFF) { + p++; + } + const size_t n_bytes = ((size_t)(p - func) + 1) * sizeof(DWORD); + uint64_t h = 1469598103934665603ULL; + const auto *bytes = reinterpret_cast(func); + for (size_t i = 0; i < n_bytes; i++) { + h ^= bytes[i]; + h *= 1099511628211ULL; + } + return h; +} + +// known SDVX Live2D shader bytecode hashes (4 pixel + 3 vertex). stable +// across runs because the game ships fixed shaders. the two sets are disjoint so +// a single shader can be classified by its own hash alone. +bool hash_is_live2d(uint64_t hash) { + switch (hash) { + case 0x75c89951817421a4ULL: // pixel: dominant model draw (~4.9M prims/120f in-song) + case 0x2d7ce428c6b4775dULL: // pixel: masked model draw + case 0x3ce00cc6111c10e7ULL: // pixel: mask generation + case 0x8bb3a2f37150ac34ULL: // pixel: mask generation (variant) + case 0xe9cf898c331e2a51ULL: // vertex + case 0x94dc84e7b7c0f437ULL: // vertex + case 0xc872937c5cc04309ULL: // vertex + return true; + } + return false; +} + +// classify a shader at creation time and record it if it is Live2D. erasing on a +// miss keeps the set correct if the runtime reuses a freed shader pointer. +void classify_shader(void *shader, const DWORD *func) { + if (hash_is_live2d(bytecode_hash(func))) { + g_live2d_shaders.insert(shader); + } else { + g_live2d_shaders.erase(shader); + } +} + +} // namespace + +namespace d3d9_live2d { + +// stage 1: fingerprint each shader as the game creates it +void on_create_vertex_shader(IDirect3DVertexShader9 *shader, const DWORD *func) { + if (tracking_enabled() && shader != nullptr) [[unlikely]] { + classify_shader(shader, func); + } +} + +void on_create_pixel_shader(IDirect3DPixelShader9 *shader, const DWORD *func) { + if (tracking_enabled() && shader != nullptr) [[unlikely]] { + classify_shader(shader, func); + } +} + +// stage 2: remember whether the just-bound shader is a Live2D one +void on_set_vertex_shader(IDirect3DVertexShader9 *shader) { + if (tracking_enabled()) [[unlikely]] { + g_cur_vs_is_live2d = g_live2d_shaders.count(shader) != 0; + } +} + +void on_set_pixel_shader(IDirect3DPixelShader9 *shader) { + if (tracking_enabled()) [[unlikely]] { + g_cur_ps_is_live2d = g_live2d_shaders.count(shader) != 0; + } +} + +// stage 3: drop the draw if the skip is active and a Live2D shader is bound +bool should_skip_draw() { + return graphics_sdvx_live2d_should_skip() && (g_cur_ps_is_live2d || g_cur_vs_is_live2d); +} + +} // namespace d3d9_live2d + +#endif // SPICE64 diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_live2d.h b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_live2d.h index b20431f..4760577 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_live2d.h +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_live2d.h @@ -1,44 +1,44 @@ -#pragma once - -#include -#include - -// SDVX Live2D draw-skip support for the D3D9 backend. -// -// SDVX renders its Live2D navigator / in-song character through a fixed set of -// shaders. when the skip is active (see graphics_sdvx_live2d_should_skip) -// the matching draw calls are dropped to save GPU. shaders are identified by a -// stable hash of their D3D9 bytecode (object pointers vary per run, the bytecode -// does not). the hashes were captured with the draw-call fingerprinting tool. -// -// every entry point is a no-op unless the feature is enabled (mode != Off), and -// d3d9 rendering for a device is single-threaded, so none of this needs locking. -namespace d3d9_live2d { - -#ifdef SPICE64 - - // record a shader's bytecode fingerprint at creation time - void on_create_vertex_shader(IDirect3DVertexShader9 *shader, const DWORD *func); - void on_create_pixel_shader(IDirect3DPixelShader9 *shader, const DWORD *func); - - // remember the currently-bound shaders - void on_set_vertex_shader(IDirect3DVertexShader9 *shader); - void on_set_pixel_shader(IDirect3DPixelShader9 *shader); - - // true if the current draw call should be dropped (skip active AND the bound - // shaders identify it as SDVX Live2D) - bool should_skip_draw(); - -#else // !SPICE64 - - // only the Live2D-capable SDVX versions are 64-bit; on 32-bit every entry point - // compiles away to nothing, so the d3d9 device hooks need no #ifdefs at their - // call sites. - inline void on_create_vertex_shader(IDirect3DVertexShader9 *, const DWORD *) {} - inline void on_create_pixel_shader(IDirect3DPixelShader9 *, const DWORD *) {} - inline void on_set_vertex_shader(IDirect3DVertexShader9 *) {} - inline void on_set_pixel_shader(IDirect3DPixelShader9 *) {} - inline bool should_skip_draw() { return false; } - -#endif // SPICE64 -} +#pragma once + +#include +#include + +// SDVX Live2D draw-skip support for the D3D9 backend. +// +// SDVX renders its Live2D navigator / in-song character through a fixed set of +// shaders. when the skip is active (see graphics_sdvx_live2d_should_skip) +// the matching draw calls are dropped to save GPU. shaders are identified by a +// stable hash of their D3D9 bytecode (object pointers vary per run, the bytecode +// does not). the hashes were captured with the draw-call fingerprinting tool. +// +// every entry point is a no-op unless the feature is enabled (mode != Off), and +// d3d9 rendering for a device is single-threaded, so none of this needs locking. +namespace d3d9_live2d { + +#ifdef SPICE64 + + // record a shader's bytecode fingerprint at creation time + void on_create_vertex_shader(IDirect3DVertexShader9 *shader, const DWORD *func); + void on_create_pixel_shader(IDirect3DPixelShader9 *shader, const DWORD *func); + + // remember the currently-bound shaders + void on_set_vertex_shader(IDirect3DVertexShader9 *shader); + void on_set_pixel_shader(IDirect3DPixelShader9 *shader); + + // true if the current draw call should be dropped (skip active AND the bound + // shaders identify it as SDVX Live2D) + bool should_skip_draw(); + +#else // !SPICE64 + + // only the Live2D-capable SDVX versions are 64-bit; on 32-bit every entry point + // compiles away to nothing, so the d3d9 device hooks need no #ifdefs at their + // call sites. + inline void on_create_vertex_shader(IDirect3DVertexShader9 *, const DWORD *) {} + inline void on_create_pixel_shader(IDirect3DPixelShader9 *, const DWORD *) {} + inline void on_set_vertex_shader(IDirect3DVertexShader9 *) {} + inline void on_set_pixel_shader(IDirect3DPixelShader9 *) {} + inline bool should_skip_draw() { return false; } + +#endif // SPICE64 +} diff --git a/src/spice2x/hooks/graphics/nvapi_impl.cpp b/src/spice2x/hooks/graphics/nvapi_impl.cpp index dbe6b08..25cfb1c 100644 --- a/src/spice2x/hooks/graphics/nvapi_impl.cpp +++ b/src/spice2x/hooks/graphics/nvapi_impl.cpp @@ -1,480 +1,480 @@ -#include "nvapi_impl.h" - -#ifdef SPICE64 - -#include -#include - -#include - -#include "external/nvapi/nvapi.h" -#include "hooks/libraryhook.h" -#include "util/logging.h" -#include "util/sysutils.h" - -namespace nvapi_impl { - -namespace { - -constexpr unsigned int NVAPI_INITIALIZE_ID = 0x0150E828; -constexpr unsigned int NVAPI_INITIALIZE_EX_ID = 0xAD298D3F; -constexpr unsigned int NVAPI_UNLOAD_ID = 0xD22BDD7E; -constexpr unsigned int NVAPI_ENUM_PHYSICAL_GPUS_ID = 0xE5AC921F; -constexpr unsigned int NVAPI_GPU_GET_CONNECTED_DISPLAY_IDS_ID = 0x0078DBA2; -constexpr unsigned int NVAPI_DISP_GET_GDI_PRIMARY_DISPLAY_ID = 0x1E9D8A31; -constexpr unsigned int NVAPI_DISP_GET_DISPLAY_CONFIG_ID = 0x11ABCCF8; -constexpr unsigned int NVAPI_DISP_SET_DISPLAY_CONFIG_ID = 0x5D8CF8DE; - -constexpr char NVAPI_DLL_NAME_A[] = "nvapi64.dll"; - -struct SyntheticDisplay { - NvU32 display_id; - NvU32 width; - NvU32 height; - NvU32 color_depth; - NvS32 x; - NvS32 y; - NvU32 refresh_rate_1k; - NV_ROTATE rotation; - bool primary; -}; - -static bool provider_initialized = false; -static bool nvapi_initialized = false; -static int gpu_handle_storage = 0; -// snapshot of the Win32 display state exposed through synthetic NVAPI -static std::vector displays; - -static NvPhysicalGpuHandle get_gpu_handle() { - return reinterpret_cast(&gpu_handle_storage); -} - -static NV_ROTATE get_rotation(DWORD orientation) { - switch (orientation) { - case DMDO_90: - return NV_ROTATE_90; - case DMDO_180: - return NV_ROTATE_180; - case DMDO_270: - return NV_ROTATE_270; - default: - return NV_ROTATE_0; - } -} - -static std::vector enumerate_displays( - uint32_t main_refresh_hz, - uint32_t sub_refresh_hz) { - - std::vector result; - - // reuse the active monitor list, then read live modes after -mainmonitor changes - for (const auto &monitor : sysutils::enumerate_monitors()) { - DEVMODEA mode {}; - mode.dmSize = sizeof(mode); - if (!EnumDisplaySettingsExA( - monitor.display_name.c_str(), - ENUM_CURRENT_SETTINGS, - &mode, - 0)) { - continue; - } - - const bool primary = mode.dmPosition.x == 0 && mode.dmPosition.y == 0; - result.push_back({ - .display_id = 0, - .width = mode.dmPelsWidth, - .height = mode.dmPelsHeight, - .color_depth = mode.dmBitsPerPel > 0 ? mode.dmBitsPerPel : 32, - .x = mode.dmPosition.x, - .y = mode.dmPosition.y, - .refresh_rate_1k = 0, - .rotation = get_rotation(mode.dmDisplayOrientation), - .primary = primary, - }); - } - - std::stable_sort(result.begin(), result.end(), [](const auto &left, const auto &right) { - return left.primary && !right.primary; - }); - - if (result.size() > 2) { - result.resize(2); - } - - if (result.empty()) { - result.push_back({ - .display_id = 0, - .width = 1920, - .height = 1080, - .color_depth = 32, - .x = 0, - .y = 0, - .refresh_rate_1k = 0, - .rotation = NV_ROTATE_0, - .primary = true, - }); - } - - for (size_t index = 0; index < result.size(); index++) { - auto &display = result[index]; - display.primary = index == 0; - display.display_id = 0x80000000u | static_cast(index + 1); - const uint32_t refresh_hz = index == 0 ? main_refresh_hz : sub_refresh_hz; - display.refresh_rate_1k = refresh_hz * 1000; - } - - return result; -} - -// initializes NVAPI for the calling process. -// marks the synthetic provider initialized without contacting a driver. -static NvAPI_Status __cdecl NvAPI_Initialize_impl() { - log_misc("nvapi_impl", "NvAPI_Initialize"); - nvapi_initialized = true; - return NVAPI_OK; -} - -// initializes NVAPI with additional client flags. -// accepts the flags and marks the synthetic provider initialized. -static NvAPI_Status __cdecl NvAPI_InitializeEx_impl(NvU32 flags) { - log_misc("nvapi_impl", "NvAPI_InitializeEx(flags={:#x})", flags); - nvapi_initialized = true; - return NVAPI_OK; -} - -// releases NVAPI state held for the calling process. -// clears the synthetic initialization state while leaving the provider installed. -static NvAPI_Status __cdecl NvAPI_Unload_impl() { - log_misc("nvapi_impl", "NvAPI_Unload"); - nvapi_initialized = false; - return NVAPI_OK; -} - -// enumerates physical GPU handles managed by the NVIDIA driver. -// returns one stable synthetic GPU containing all exposed displays. -static NvAPI_Status __cdecl NvAPI_EnumPhysicalGPUs_impl( - NvPhysicalGpuHandle gpu_handles[NVAPI_MAX_PHYSICAL_GPUS], - NvU32 *gpu_count) { - - log_misc( - "nvapi_impl", - "NvAPI_EnumPhysicalGPUs(handles={}, count={})", - fmt::ptr(gpu_handles), - fmt::ptr(gpu_count)); - - if (!nvapi_initialized) { - return NVAPI_API_NOT_INITIALIZED; - } - if (gpu_handles == nullptr || gpu_count == nullptr) { - return NVAPI_INVALID_ARGUMENT; - } - - gpu_handles[0] = get_gpu_handle(); - *gpu_count = 1; - log_misc( - "nvapi_impl", - "NvAPI_EnumPhysicalGPUs - gpu={}, count={}", - fmt::ptr(gpu_handles[0]), - *gpu_count); - return NVAPI_OK; -} - -// returns connected display descriptors for a physical GPU. -// exposes the monitor snapshot as DP primary and HDMI secondary displays. -static NvAPI_Status __cdecl NvAPI_GPU_GetConnectedDisplayIds_impl( - NvPhysicalGpuHandle gpu_handle, - NV_GPU_DISPLAYIDS *display_ids, - NvU32 *display_id_count, - NvU32 flags) { - - const NvU32 input_count = display_id_count != nullptr ? *display_id_count : 0; - log_misc( - "nvapi_impl", - "NvAPI_GPU_GetConnectedDisplayIds(gpu={}, ids={}, count={}, flags={:#x})", - fmt::ptr(gpu_handle), - fmt::ptr(display_ids), - input_count, - flags); - - if (!nvapi_initialized) { - return NVAPI_API_NOT_INITIALIZED; - } - if (gpu_handle != get_gpu_handle()) { - return NVAPI_EXPECTED_PHYSICAL_GPU_HANDLE; - } - if (display_id_count == nullptr) { - return NVAPI_INVALID_ARGUMENT; - } - - const NvU32 required_count = static_cast(displays.size()); - if (display_ids == nullptr) { - *display_id_count = required_count; - log_misc( - "nvapi_impl", - "NvAPI_GPU_GetConnectedDisplayIds - required_count={}", - required_count); - return NVAPI_OK; - } - - const NvU32 capacity = *display_id_count; - *display_id_count = required_count; - if (capacity < required_count) { - return NVAPI_INSUFFICIENT_BUFFER; - } - - for (NvU32 index = 0; index < required_count; index++) { - const auto &source = displays[index]; - auto &destination = display_ids[index]; - destination = {}; - destination.version = NV_GPU_DISPLAYIDS_VER; - destination.connectorType = source.primary ? - NV_MONITOR_CONN_TYPE_DP : NV_MONITOR_CONN_TYPE_HDMI; - destination.displayId = source.display_id; - destination.isActive = 1; - destination.isOSVisible = 1; - destination.isConnected = 1; - destination.isPhysicallyConnected = 1; - } - - log_misc( - "nvapi_impl", - "NvAPI_GPU_GetConnectedDisplayIds - returned_count={}", - required_count); - return NVAPI_OK; -} - -// returns the NVAPI display ID associated with the Windows GDI primary. -// returns the first synthetic display, ordered from the live desktop origin. -static NvAPI_Status __cdecl NvAPI_DISP_GetGDIPrimaryDisplayId_impl(NvU32 *display_id) { - log_misc( - "nvapi_impl", - "NvAPI_DISP_GetGDIPrimaryDisplayId(display_id={})", - fmt::ptr(display_id)); - - if (!nvapi_initialized) { - return NVAPI_API_NOT_INITIALIZED; - } - if (display_id == nullptr || displays.empty()) { - return NVAPI_INVALID_ARGUMENT; - } - - *display_id = displays.front().display_id; - log_misc( - "nvapi_impl", - "NvAPI_DISP_GetGDIPrimaryDisplayId - display_id={:#x}", - *display_id); - return NVAPI_OK; -} - -static void fill_source_mode( - NV_DISPLAYCONFIG_SOURCE_MODE_INFO *destination, - const SyntheticDisplay &source) { - - if (destination == nullptr) { - return; - } - - *destination = {}; - destination->resolution.width = source.width; - destination->resolution.height = source.height; - destination->resolution.colorDepth = source.color_depth; - destination->colorFormat = NV_FORMAT_A8R8G8B8; - destination->position.x = source.x; - destination->position.y = source.y; - destination->spanningOrientation = NV_DISPLAYCONFIG_SPAN_NONE; - destination->bGDIPrimary = source.primary ? 1 : 0; -} - -static NvAPI_Status fill_target( - NV_DISPLAYCONFIG_PATH_TARGET_INFO *destination, - const SyntheticDisplay &source, - NvU32 target_id) { - - if (destination == nullptr) { - return NVAPI_OK; - } - - auto *details = destination->details; - destination->displayId = source.display_id; - destination->targetId = target_id; - - if (details == nullptr) { - return NVAPI_OK; - } - if (details->version != NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_VER) { - return NVAPI_INCOMPATIBLE_STRUCT_VERSION; - } - - *details = {}; - details->version = NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_VER; - details->rotation = source.rotation; - details->scaling = NV_SCALING_DEFAULT; - details->refreshRate1K = source.refresh_rate_1k; - details->timingOverride = NV_TIMING_OVERRIDE_CURRENT; - return NVAPI_OK; -} - -// retrieves the current global display topology through NVAPI's three-pass contract. -// fills caller-owned buffers from the synthetic monitor snapshot and configured rates. -static NvAPI_Status __cdecl NvAPI_DISP_GetDisplayConfig_impl( - NvU32 *path_info_count, - NV_DISPLAYCONFIG_PATH_INFO *path_info) { - - const NvU32 input_count = path_info_count != nullptr ? *path_info_count : 0; - log_misc( - "nvapi_impl", - "NvAPI_DISP_GetDisplayConfig(count={}, paths={})", - input_count, - fmt::ptr(path_info)); - - if (!nvapi_initialized) { - return NVAPI_API_NOT_INITIALIZED; - } - if (path_info_count == nullptr) { - return NVAPI_INVALID_ARGUMENT; - } - - const NvU32 required_count = static_cast(displays.size()); - if (path_info == nullptr) { - *path_info_count = required_count; - log_misc( - "nvapi_impl", - "NvAPI_DISP_GetDisplayConfig - required_count={}", - required_count); - return NVAPI_OK; - } - - const NvU32 capacity = *path_info_count; - *path_info_count = required_count; - if (capacity < required_count) { - return NVAPI_INSUFFICIENT_BUFFER; - } - - for (NvU32 index = 0; index < required_count; index++) { - auto &path = path_info[index]; - if (path.version != NV_DISPLAYCONFIG_PATH_INFO_VER2) { - return NVAPI_INCOMPATIBLE_STRUCT_VERSION; - } - if (path.targetInfo != nullptr && path.targetInfoCount < 1) { - return NVAPI_INSUFFICIENT_BUFFER; - } - - const auto &display = displays[index]; - path.sourceId = index; - path.targetInfoCount = 1; - path.IsNonNVIDIAAdapter = 0; - path.pOSAdapterID = nullptr; - fill_source_mode(path.sourceModeInfo, display); - - const NvAPI_Status status = fill_target(path.targetInfo, display, index); - if (status != NVAPI_OK) { - return status; - } - } - - log_misc( - "nvapi_impl", - "NvAPI_DISP_GetDisplayConfig - returned_count={}", - required_count); - return NVAPI_OK; -} - -// applies a supplied global display topology through the NVIDIA driver. -// accepts the cabinet topology without making any changes to Windows. -static NvAPI_Status __cdecl NvAPI_DISP_SetDisplayConfig_impl( - NvU32 path_info_count, - NV_DISPLAYCONFIG_PATH_INFO *path_info, - NvU32 flags) { - - log_misc( - "nvapi_impl", - "NvAPI_DISP_SetDisplayConfig(count={}, paths={}, flags={:#x})", - path_info_count, - fmt::ptr(path_info), - flags); - - if (!nvapi_initialized) { - return NVAPI_API_NOT_INITIALIZED; - } - - log_misc("nvapi_impl", "NvAPI_DISP_SetDisplayConfig - return synthetic success"); - return NVAPI_OK; -} - -template -static uintptr_t *query_result(T function) { - return reinterpret_cast(function); -} - -// resolves an NVAPI function ID to its implementation address. -// exposes only the synthetic entry points used by KFC and rejects all others. -static uintptr_t *__cdecl NvAPI_QueryInterface_impl(unsigned int function_id) { - uintptr_t *result = nullptr; - switch (function_id) { - case NVAPI_INITIALIZE_ID: - result = query_result(NvAPI_Initialize_impl); - break; - case NVAPI_INITIALIZE_EX_ID: - result = query_result(NvAPI_InitializeEx_impl); - break; - case NVAPI_UNLOAD_ID: - result = query_result(NvAPI_Unload_impl); - break; - case NVAPI_ENUM_PHYSICAL_GPUS_ID: - result = query_result(NvAPI_EnumPhysicalGPUs_impl); - break; - case NVAPI_GPU_GET_CONNECTED_DISPLAY_IDS_ID: - result = query_result(NvAPI_GPU_GetConnectedDisplayIds_impl); - break; - case NVAPI_DISP_GET_GDI_PRIMARY_DISPLAY_ID: - result = query_result(NvAPI_DISP_GetGDIPrimaryDisplayId_impl); - break; - case NVAPI_DISP_GET_DISPLAY_CONFIG_ID: - result = query_result(NvAPI_DISP_GetDisplayConfig_impl); - break; - case NVAPI_DISP_SET_DISPLAY_CONFIG_ID: - result = query_result(NvAPI_DISP_SetDisplayConfig_impl); - break; - default: - break; - } - - log_misc( - "nvapi_impl", - "NvAPI_QueryInterface(0x{:x}) - {}", - function_id, - result != nullptr ? "implemented" : "unsupported"); - return result; -} - -} - -bool initialize(HINSTANCE dll, uint32_t main_refresh_hz, uint32_t sub_refresh_hz) { - if (provider_initialized) { - return true; - } - if (dll == nullptr) { - log_warning("nvapi_impl", "invalid synthetic module handle"); - return false; - } - - displays = enumerate_displays(main_refresh_hz, sub_refresh_hz); - libraryhook_hook_library(NVAPI_DLL_NAME_A, dll); - libraryhook_hook_proc("nvapi_QueryInterface", NvAPI_QueryInterface_impl); - libraryhook_enable(); - - provider_initialized = true; - log_info( - "nvapi_impl", - "synthetic {} enabled with {} display(s), main={} Hz, sub={} Hz", - NVAPI_DLL_NAME_A, - displays.size(), - main_refresh_hz, - sub_refresh_hz); - return true; -} - -} - -#endif +#include "nvapi_impl.h" + +#ifdef SPICE64 + +#include +#include + +#include + +#include "external/nvapi/nvapi.h" +#include "hooks/libraryhook.h" +#include "util/logging.h" +#include "util/sysutils.h" + +namespace nvapi_impl { + +namespace { + +constexpr unsigned int NVAPI_INITIALIZE_ID = 0x0150E828; +constexpr unsigned int NVAPI_INITIALIZE_EX_ID = 0xAD298D3F; +constexpr unsigned int NVAPI_UNLOAD_ID = 0xD22BDD7E; +constexpr unsigned int NVAPI_ENUM_PHYSICAL_GPUS_ID = 0xE5AC921F; +constexpr unsigned int NVAPI_GPU_GET_CONNECTED_DISPLAY_IDS_ID = 0x0078DBA2; +constexpr unsigned int NVAPI_DISP_GET_GDI_PRIMARY_DISPLAY_ID = 0x1E9D8A31; +constexpr unsigned int NVAPI_DISP_GET_DISPLAY_CONFIG_ID = 0x11ABCCF8; +constexpr unsigned int NVAPI_DISP_SET_DISPLAY_CONFIG_ID = 0x5D8CF8DE; + +constexpr char NVAPI_DLL_NAME_A[] = "nvapi64.dll"; + +struct SyntheticDisplay { + NvU32 display_id; + NvU32 width; + NvU32 height; + NvU32 color_depth; + NvS32 x; + NvS32 y; + NvU32 refresh_rate_1k; + NV_ROTATE rotation; + bool primary; +}; + +static bool provider_initialized = false; +static bool nvapi_initialized = false; +static int gpu_handle_storage = 0; +// snapshot of the Win32 display state exposed through synthetic NVAPI +static std::vector displays; + +static NvPhysicalGpuHandle get_gpu_handle() { + return reinterpret_cast(&gpu_handle_storage); +} + +static NV_ROTATE get_rotation(DWORD orientation) { + switch (orientation) { + case DMDO_90: + return NV_ROTATE_90; + case DMDO_180: + return NV_ROTATE_180; + case DMDO_270: + return NV_ROTATE_270; + default: + return NV_ROTATE_0; + } +} + +static std::vector enumerate_displays( + uint32_t main_refresh_hz, + uint32_t sub_refresh_hz) { + + std::vector result; + + // reuse the active monitor list, then read live modes after -mainmonitor changes + for (const auto &monitor : sysutils::enumerate_monitors()) { + DEVMODEA mode {}; + mode.dmSize = sizeof(mode); + if (!EnumDisplaySettingsExA( + monitor.display_name.c_str(), + ENUM_CURRENT_SETTINGS, + &mode, + 0)) { + continue; + } + + const bool primary = mode.dmPosition.x == 0 && mode.dmPosition.y == 0; + result.push_back({ + .display_id = 0, + .width = mode.dmPelsWidth, + .height = mode.dmPelsHeight, + .color_depth = mode.dmBitsPerPel > 0 ? mode.dmBitsPerPel : 32, + .x = mode.dmPosition.x, + .y = mode.dmPosition.y, + .refresh_rate_1k = 0, + .rotation = get_rotation(mode.dmDisplayOrientation), + .primary = primary, + }); + } + + std::stable_sort(result.begin(), result.end(), [](const auto &left, const auto &right) { + return left.primary && !right.primary; + }); + + if (result.size() > 2) { + result.resize(2); + } + + if (result.empty()) { + result.push_back({ + .display_id = 0, + .width = 1920, + .height = 1080, + .color_depth = 32, + .x = 0, + .y = 0, + .refresh_rate_1k = 0, + .rotation = NV_ROTATE_0, + .primary = true, + }); + } + + for (size_t index = 0; index < result.size(); index++) { + auto &display = result[index]; + display.primary = index == 0; + display.display_id = 0x80000000u | static_cast(index + 1); + const uint32_t refresh_hz = index == 0 ? main_refresh_hz : sub_refresh_hz; + display.refresh_rate_1k = refresh_hz * 1000; + } + + return result; +} + +// initializes NVAPI for the calling process. +// marks the synthetic provider initialized without contacting a driver. +static NvAPI_Status __cdecl NvAPI_Initialize_impl() { + log_misc("nvapi_impl", "NvAPI_Initialize"); + nvapi_initialized = true; + return NVAPI_OK; +} + +// initializes NVAPI with additional client flags. +// accepts the flags and marks the synthetic provider initialized. +static NvAPI_Status __cdecl NvAPI_InitializeEx_impl(NvU32 flags) { + log_misc("nvapi_impl", "NvAPI_InitializeEx(flags={:#x})", flags); + nvapi_initialized = true; + return NVAPI_OK; +} + +// releases NVAPI state held for the calling process. +// clears the synthetic initialization state while leaving the provider installed. +static NvAPI_Status __cdecl NvAPI_Unload_impl() { + log_misc("nvapi_impl", "NvAPI_Unload"); + nvapi_initialized = false; + return NVAPI_OK; +} + +// enumerates physical GPU handles managed by the NVIDIA driver. +// returns one stable synthetic GPU containing all exposed displays. +static NvAPI_Status __cdecl NvAPI_EnumPhysicalGPUs_impl( + NvPhysicalGpuHandle gpu_handles[NVAPI_MAX_PHYSICAL_GPUS], + NvU32 *gpu_count) { + + log_misc( + "nvapi_impl", + "NvAPI_EnumPhysicalGPUs(handles={}, count={})", + fmt::ptr(gpu_handles), + fmt::ptr(gpu_count)); + + if (!nvapi_initialized) { + return NVAPI_API_NOT_INITIALIZED; + } + if (gpu_handles == nullptr || gpu_count == nullptr) { + return NVAPI_INVALID_ARGUMENT; + } + + gpu_handles[0] = get_gpu_handle(); + *gpu_count = 1; + log_misc( + "nvapi_impl", + "NvAPI_EnumPhysicalGPUs - gpu={}, count={}", + fmt::ptr(gpu_handles[0]), + *gpu_count); + return NVAPI_OK; +} + +// returns connected display descriptors for a physical GPU. +// exposes the monitor snapshot as DP primary and HDMI secondary displays. +static NvAPI_Status __cdecl NvAPI_GPU_GetConnectedDisplayIds_impl( + NvPhysicalGpuHandle gpu_handle, + NV_GPU_DISPLAYIDS *display_ids, + NvU32 *display_id_count, + NvU32 flags) { + + const NvU32 input_count = display_id_count != nullptr ? *display_id_count : 0; + log_misc( + "nvapi_impl", + "NvAPI_GPU_GetConnectedDisplayIds(gpu={}, ids={}, count={}, flags={:#x})", + fmt::ptr(gpu_handle), + fmt::ptr(display_ids), + input_count, + flags); + + if (!nvapi_initialized) { + return NVAPI_API_NOT_INITIALIZED; + } + if (gpu_handle != get_gpu_handle()) { + return NVAPI_EXPECTED_PHYSICAL_GPU_HANDLE; + } + if (display_id_count == nullptr) { + return NVAPI_INVALID_ARGUMENT; + } + + const NvU32 required_count = static_cast(displays.size()); + if (display_ids == nullptr) { + *display_id_count = required_count; + log_misc( + "nvapi_impl", + "NvAPI_GPU_GetConnectedDisplayIds - required_count={}", + required_count); + return NVAPI_OK; + } + + const NvU32 capacity = *display_id_count; + *display_id_count = required_count; + if (capacity < required_count) { + return NVAPI_INSUFFICIENT_BUFFER; + } + + for (NvU32 index = 0; index < required_count; index++) { + const auto &source = displays[index]; + auto &destination = display_ids[index]; + destination = {}; + destination.version = NV_GPU_DISPLAYIDS_VER; + destination.connectorType = source.primary ? + NV_MONITOR_CONN_TYPE_DP : NV_MONITOR_CONN_TYPE_HDMI; + destination.displayId = source.display_id; + destination.isActive = 1; + destination.isOSVisible = 1; + destination.isConnected = 1; + destination.isPhysicallyConnected = 1; + } + + log_misc( + "nvapi_impl", + "NvAPI_GPU_GetConnectedDisplayIds - returned_count={}", + required_count); + return NVAPI_OK; +} + +// returns the NVAPI display ID associated with the Windows GDI primary. +// returns the first synthetic display, ordered from the live desktop origin. +static NvAPI_Status __cdecl NvAPI_DISP_GetGDIPrimaryDisplayId_impl(NvU32 *display_id) { + log_misc( + "nvapi_impl", + "NvAPI_DISP_GetGDIPrimaryDisplayId(display_id={})", + fmt::ptr(display_id)); + + if (!nvapi_initialized) { + return NVAPI_API_NOT_INITIALIZED; + } + if (display_id == nullptr || displays.empty()) { + return NVAPI_INVALID_ARGUMENT; + } + + *display_id = displays.front().display_id; + log_misc( + "nvapi_impl", + "NvAPI_DISP_GetGDIPrimaryDisplayId - display_id={:#x}", + *display_id); + return NVAPI_OK; +} + +static void fill_source_mode( + NV_DISPLAYCONFIG_SOURCE_MODE_INFO *destination, + const SyntheticDisplay &source) { + + if (destination == nullptr) { + return; + } + + *destination = {}; + destination->resolution.width = source.width; + destination->resolution.height = source.height; + destination->resolution.colorDepth = source.color_depth; + destination->colorFormat = NV_FORMAT_A8R8G8B8; + destination->position.x = source.x; + destination->position.y = source.y; + destination->spanningOrientation = NV_DISPLAYCONFIG_SPAN_NONE; + destination->bGDIPrimary = source.primary ? 1 : 0; +} + +static NvAPI_Status fill_target( + NV_DISPLAYCONFIG_PATH_TARGET_INFO *destination, + const SyntheticDisplay &source, + NvU32 target_id) { + + if (destination == nullptr) { + return NVAPI_OK; + } + + auto *details = destination->details; + destination->displayId = source.display_id; + destination->targetId = target_id; + + if (details == nullptr) { + return NVAPI_OK; + } + if (details->version != NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_VER) { + return NVAPI_INCOMPATIBLE_STRUCT_VERSION; + } + + *details = {}; + details->version = NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_VER; + details->rotation = source.rotation; + details->scaling = NV_SCALING_DEFAULT; + details->refreshRate1K = source.refresh_rate_1k; + details->timingOverride = NV_TIMING_OVERRIDE_CURRENT; + return NVAPI_OK; +} + +// retrieves the current global display topology through NVAPI's three-pass contract. +// fills caller-owned buffers from the synthetic monitor snapshot and configured rates. +static NvAPI_Status __cdecl NvAPI_DISP_GetDisplayConfig_impl( + NvU32 *path_info_count, + NV_DISPLAYCONFIG_PATH_INFO *path_info) { + + const NvU32 input_count = path_info_count != nullptr ? *path_info_count : 0; + log_misc( + "nvapi_impl", + "NvAPI_DISP_GetDisplayConfig(count={}, paths={})", + input_count, + fmt::ptr(path_info)); + + if (!nvapi_initialized) { + return NVAPI_API_NOT_INITIALIZED; + } + if (path_info_count == nullptr) { + return NVAPI_INVALID_ARGUMENT; + } + + const NvU32 required_count = static_cast(displays.size()); + if (path_info == nullptr) { + *path_info_count = required_count; + log_misc( + "nvapi_impl", + "NvAPI_DISP_GetDisplayConfig - required_count={}", + required_count); + return NVAPI_OK; + } + + const NvU32 capacity = *path_info_count; + *path_info_count = required_count; + if (capacity < required_count) { + return NVAPI_INSUFFICIENT_BUFFER; + } + + for (NvU32 index = 0; index < required_count; index++) { + auto &path = path_info[index]; + if (path.version != NV_DISPLAYCONFIG_PATH_INFO_VER2) { + return NVAPI_INCOMPATIBLE_STRUCT_VERSION; + } + if (path.targetInfo != nullptr && path.targetInfoCount < 1) { + return NVAPI_INSUFFICIENT_BUFFER; + } + + const auto &display = displays[index]; + path.sourceId = index; + path.targetInfoCount = 1; + path.IsNonNVIDIAAdapter = 0; + path.pOSAdapterID = nullptr; + fill_source_mode(path.sourceModeInfo, display); + + const NvAPI_Status status = fill_target(path.targetInfo, display, index); + if (status != NVAPI_OK) { + return status; + } + } + + log_misc( + "nvapi_impl", + "NvAPI_DISP_GetDisplayConfig - returned_count={}", + required_count); + return NVAPI_OK; +} + +// applies a supplied global display topology through the NVIDIA driver. +// accepts the cabinet topology without making any changes to Windows. +static NvAPI_Status __cdecl NvAPI_DISP_SetDisplayConfig_impl( + NvU32 path_info_count, + NV_DISPLAYCONFIG_PATH_INFO *path_info, + NvU32 flags) { + + log_misc( + "nvapi_impl", + "NvAPI_DISP_SetDisplayConfig(count={}, paths={}, flags={:#x})", + path_info_count, + fmt::ptr(path_info), + flags); + + if (!nvapi_initialized) { + return NVAPI_API_NOT_INITIALIZED; + } + + log_misc("nvapi_impl", "NvAPI_DISP_SetDisplayConfig - return synthetic success"); + return NVAPI_OK; +} + +template +static uintptr_t *query_result(T function) { + return reinterpret_cast(function); +} + +// resolves an NVAPI function ID to its implementation address. +// exposes only the synthetic entry points used by KFC and rejects all others. +static uintptr_t *__cdecl NvAPI_QueryInterface_impl(unsigned int function_id) { + uintptr_t *result = nullptr; + switch (function_id) { + case NVAPI_INITIALIZE_ID: + result = query_result(NvAPI_Initialize_impl); + break; + case NVAPI_INITIALIZE_EX_ID: + result = query_result(NvAPI_InitializeEx_impl); + break; + case NVAPI_UNLOAD_ID: + result = query_result(NvAPI_Unload_impl); + break; + case NVAPI_ENUM_PHYSICAL_GPUS_ID: + result = query_result(NvAPI_EnumPhysicalGPUs_impl); + break; + case NVAPI_GPU_GET_CONNECTED_DISPLAY_IDS_ID: + result = query_result(NvAPI_GPU_GetConnectedDisplayIds_impl); + break; + case NVAPI_DISP_GET_GDI_PRIMARY_DISPLAY_ID: + result = query_result(NvAPI_DISP_GetGDIPrimaryDisplayId_impl); + break; + case NVAPI_DISP_GET_DISPLAY_CONFIG_ID: + result = query_result(NvAPI_DISP_GetDisplayConfig_impl); + break; + case NVAPI_DISP_SET_DISPLAY_CONFIG_ID: + result = query_result(NvAPI_DISP_SetDisplayConfig_impl); + break; + default: + break; + } + + log_misc( + "nvapi_impl", + "NvAPI_QueryInterface(0x{:x}) - {}", + function_id, + result != nullptr ? "implemented" : "unsupported"); + return result; +} + +} + +bool initialize(HINSTANCE dll, uint32_t main_refresh_hz, uint32_t sub_refresh_hz) { + if (provider_initialized) { + return true; + } + if (dll == nullptr) { + log_warning("nvapi_impl", "invalid synthetic module handle"); + return false; + } + + displays = enumerate_displays(main_refresh_hz, sub_refresh_hz); + libraryhook_hook_library(NVAPI_DLL_NAME_A, dll); + libraryhook_hook_proc("nvapi_QueryInterface", NvAPI_QueryInterface_impl); + libraryhook_enable(); + + provider_initialized = true; + log_info( + "nvapi_impl", + "synthetic {} enabled with {} display(s), main={} Hz, sub={} Hz", + NVAPI_DLL_NAME_A, + displays.size(), + main_refresh_hz, + sub_refresh_hz); + return true; +} + +} + +#endif diff --git a/src/spice2x/hooks/graphics/nvapi_impl.h b/src/spice2x/hooks/graphics/nvapi_impl.h index a43c9bc..d2c19bf 100644 --- a/src/spice2x/hooks/graphics/nvapi_impl.h +++ b/src/spice2x/hooks/graphics/nvapi_impl.h @@ -1,14 +1,14 @@ -#pragma once - -#ifdef SPICE64 - -#include -#include - -namespace nvapi_impl { - -bool initialize(HINSTANCE dll, uint32_t main_refresh_hz, uint32_t sub_refresh_hz); - -} - -#endif +#pragma once + +#ifdef SPICE64 + +#include +#include + +namespace nvapi_impl { + +bool initialize(HINSTANCE dll, uint32_t main_refresh_hz, uint32_t sub_refresh_hz); + +} + +#endif diff --git a/src/spice2x/misc/hotkeys.cpp b/src/spice2x/misc/hotkeys.cpp index 7a21852..1b53679 100644 --- a/src/spice2x/misc/hotkeys.cpp +++ b/src/spice2x/misc/hotkeys.cpp @@ -1,154 +1,154 @@ -#include "hotkeys.h" - -#include -#include -#include -#include -#include -#include - -#include - -#include "games/io.h" -#include "launcher/superexit.h" -#include "misc/eamuse.h" -#include "overlay/overlay.h" -#include "rawinput/rawinput.h" -#include "util/logging.h" - -namespace hotkeys { - - namespace { - - // 8 ms targets short screenshot pulses; sleep_for may use a coarser scheduler - // interval during early boot or when process timer adjustments are disabled - constexpr auto MIN_SAMPLE_INTERVAL = std::chrono::milliseconds(8); - - std::atomic_bool SCREENSHOT_PENDING {false}; - std::mutex INPUT_MUTEX; - bool INPUT_ENABLED = false; - bool RAW_INPUT_ENABLED = false; - std::jthread WORKER; - - bool read_button(std::vector