From 2d51d0b97d2813e2d798faf5dfccb4f912cc0cc5 Mon Sep 17 00:00:00 2001 From: Andrea Pappacoda Date: Tue, 20 Dec 2022 21:19:34 +0100 Subject: [PATCH] New upstream version 0-1276+ds --- .ci/scripts/clang/docker.sh | 2 + .ci/scripts/linux/docker.sh | 9 +- .ci/scripts/transifex/docker.sh | 12 - .ci/scripts/windows/docker.sh | 6 +- .ci/templates/build-msvc.yml | 2 +- .../ISSUE_TEMPLATE/blank_issue_template.yml | 10 + .../bug-report-feature-request.md | 39 - .github/ISSUE_TEMPLATE/bug_report.yml | 64 + .github/ISSUE_TEMPLATE/feature_request.yml | 28 + .github/workflows/ci.yml | 4 +- .reuse/dep5 | 8 +- CMakeLists.txt | 421 +- CMakeModules/CopyYuzuQt5Deps.cmake | 7 +- CMakeModules/GenerateSCMRev.cmake | 10 +- .../applet_pro_controller_dark_disabled.png | Bin 4477 -> 2712 bytes .../applet_pro_controller_disabled.png | Bin 4173 -> 2630 bytes ...pplet_pro_controller_midnight_disabled.png | Bin 4459 -> 2774 bytes dist/icons/overlay/button_A.png | Bin 3494 -> 1647 bytes dist/icons/overlay/button_B.png | Bin 3375 -> 1534 bytes dist/icons/overlay/button_X.png | Bin 3968 -> 1748 bytes dist/icons/overlay/button_Y.png | Bin 3337 -> 1504 bytes dist/icons/overlay/button_press_stick.png | Bin 5225 -> 2477 bytes dist/icons/overlay/controller_dual_joycon.png | Bin 7312 -> 3475 bytes .../overlay/controller_dual_joycon_dark.png | Bin 5889 -> 3107 bytes dist/icons/overlay/controller_handheld.png | Bin 4645 -> 2250 bytes .../overlay/controller_handheld_dark.png | Bin 3745 -> 2000 bytes dist/icons/overlay/controller_pro.png | Bin 9493 -> 4531 bytes dist/icons/overlay/controller_pro_dark.png | Bin 7488 -> 4531 bytes .../overlay/controller_single_joycon_left.png | Bin 7489 -> 3605 bytes .../controller_single_joycon_left_dark.png | Bin 6768 -> 3447 bytes .../controller_single_joycon_left_y_dark.png | Bin 2639 -> 1035 bytes .../controller_single_joycon_right.png | Bin 7497 -> 3603 bytes .../controller_single_joycon_right_dark.png | Bin 6729 -> 3406 bytes dist/icons/overlay/osk_button_backspace.png | Bin 2919 -> 1272 bytes .../overlay/osk_button_backspace_dark.png | Bin 2958 -> 1262 bytes dist/languages/.tx/config | 2 +- dist/languages/README.md | 4 +- dist/languages/ca.ts | 1852 +-- dist/languages/cs.ts | 1872 +-- dist/languages/da.ts | 2106 +-- dist/languages/de.ts | 2022 +-- dist/languages/el.ts | 2020 +-- dist/languages/es.ts | 1942 +-- dist/languages/fr.ts | 2128 +-- dist/languages/id.ts | 1883 +-- dist/languages/it.ts | 2149 +-- dist/languages/ja_JP.ts | 1894 +-- dist/languages/ko_KR.ts | 2126 +-- dist/languages/nb.ts | 1853 +-- dist/languages/nl.ts | 1945 +-- dist/languages/pl.ts | 1860 +-- dist/languages/pt_BR.ts | 1944 +-- dist/languages/pt_PT.ts | 1944 +-- dist/languages/ru_RU.ts | 2152 +-- dist/languages/sv.ts | 2167 +-- dist/languages/tr_TR.ts | 2201 +-- dist/languages/uk.ts | 7513 +++++++++++ dist/languages/vi.ts | 1909 +-- dist/languages/vi_VN.ts | 1907 +-- dist/languages/zh_CN.ts | 1858 +-- dist/languages/zh_TW.ts | 1924 +-- .../colorful/icons/48x48/bad_folder.png | Bin 15494 -> 528 bytes .../default/icons/256x256/plus_folder.png | Bin 3521 -> 1948 bytes dist/qt_themes/default/icons/256x256/yuzu.png | Bin 6751 -> 4425 bytes .../qdarkstyle/icons/256x256/plus_folder.png | Bin 3931 -> 1924 bytes externals/CMakeLists.txt | 71 +- externals/find-modules/FindDiscordRPC.cmake | 27 + externals/find-modules/FindFFmpeg.cmake | 8 + externals/find-modules/FindLibUSB.cmake | 44 - externals/find-modules/FindOpus.cmake | 20 +- externals/find-modules/Findenet.cmake | 16 + externals/find-modules/Findhttplib.cmake | 21 + externals/find-modules/Findinih.cmake | 16 + externals/find-modules/Findlibusb.cmake | 16 + externals/find-modules/Findlz4.cmake | 33 +- externals/find-modules/Findzstd.cmake | 33 +- externals/inih/CMakeLists.txt | 3 +- externals/libusb/CMakeLists.txt | 4 +- externals/microprofile/microprofileui.h | 4 - sirit/include/sirit/sirit.h | 13 +- sirit/src/instructions/flow.cpp | 13 +- sirit/src/instructions/group.cpp | 12 + src/CMakeLists.txt | 39 +- src/audio_core/CMakeLists.txt | 24 +- src/audio_core/audio_core.cpp | 2 +- src/audio_core/audio_event.cpp | 1 + src/audio_core/audio_manager.cpp | 17 +- src/audio_core/audio_manager.h | 23 +- src/audio_core/audio_render_manager.h | 2 + src/audio_core/common/feature_support.h | 1 + src/audio_core/device/audio_buffers.h | 8 +- src/audio_core/device/device_session.cpp | 6 + src/audio_core/device/device_session.h | 5 + src/audio_core/in/audio_in_system.cpp | 13 +- src/audio_core/in/audio_in_system.h | 2 +- src/audio_core/out/audio_out_system.cpp | 15 +- src/audio_core/out/audio_out_system.h | 4 +- src/audio_core/precompiled_headers.h | 6 + .../renderer/adsp/audio_renderer.cpp | 4 +- src/audio_core/renderer/adsp/audio_renderer.h | 2 +- .../renderer/behavior/info_updater.cpp | 2 +- .../renderer/command/command_buffer.cpp | 14 +- .../renderer/command/effect/biquad_filter.cpp | 2 +- .../renderer/command/effect/i3dl2_reverb.cpp | 1 + .../effect/multi_tap_biquad_filter.cpp | 2 +- .../renderer/command/effect/reverb.cpp | 1 + src/audio_core/renderer/mix/mix_context.cpp | 1 + .../renderer/performance/detail_aspect.h | 1 - .../renderer/performance/entry_aspect.h | 1 - .../performance/performance_manager.cpp | 1 + src/audio_core/renderer/system.cpp | 89 +- src/audio_core/renderer/system.h | 16 + src/audio_core/renderer/system_manager.cpp | 2 +- .../renderer/voice/voice_context.cpp | 5 +- src/audio_core/sink/cubeb_sink.cpp | 31 +- src/audio_core/sink/cubeb_sink.h | 7 + src/audio_core/sink/sdl2_sink.cpp | 14 +- src/audio_core/sink/sdl2_sink.h | 7 + src/audio_core/sink/sink_details.cpp | 66 +- src/audio_core/sink/sink_details.h | 2 +- src/audio_core/sink/sink_stream.cpp | 28 +- src/audio_core/sink/sink_stream.h | 5 +- src/common/CMakeLists.txt | 54 +- src/common/address_space.cpp | 10 + src/common/address_space.h | 150 + src/common/address_space.inc | 366 + src/common/algorithm.h | 8 + src/common/assert.h | 4 +- src/common/atomic_helpers.h | 1 + src/common/bit_field.h | 15 +- src/common/bounded_threadsafe_queue.h | 9 - src/common/common_funcs.h | 4 +- src/common/common_precompiled_headers.h | 14 + src/common/concepts.h | 24 +- src/common/fixed_point.h | 274 +- src/common/fs/file.cpp | 2 + src/common/fs/file.h | 12 +- src/common/fs/fs_util.cpp | 1 + src/common/fs/path_util.cpp | 1 + src/common/hash.h | 7 + src/common/host_memory.cpp | 6 + src/common/input.h | 76 +- src/common/logging/backend.cpp | 4 +- src/common/multi_level_page_table.cpp | 9 + src/common/multi_level_page_table.h | 78 + src/common/multi_level_page_table.inc | 84 + src/common/polyfill_ranges.h | 530 + src/common/polyfill_thread.h | 323 + src/common/precompiled_headers.h | 6 + src/common/settings.cpp | 4 + src/common/settings.h | 14 +- src/common/settings_input.h | 1 + src/common/string_util.cpp | 4 +- src/common/thread.h | 11 +- src/common/thread_worker.h | 5 +- src/common/threadsafe_queue.h | 4 +- src/common/x64/cpu_detect.cpp | 59 +- src/common/x64/cpu_detect.h | 4 + src/core/CMakeLists.txt | 72 +- src/core/arm/arm_interface.cpp | 16 +- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 28 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 25 +- src/core/arm/dynarmic/arm_dynarmic_cp15.cpp | 22 +- src/core/arm/exclusive_monitor.cpp | 4 +- src/core/core.cpp | 128 +- src/core/core.h | 29 +- src/core/core_timing.cpp | 40 +- src/core/core_timing.h | 14 +- src/core/cpu_manager.cpp | 19 +- src/core/cpu_manager.h | 5 +- src/core/crypto/key_manager.cpp | 8 +- src/core/debugger/debugger.cpp | 174 +- src/core/debugger/gdbstub.cpp | 151 + src/core/debugger/gdbstub.h | 1 + src/core/device_memory.h | 10 +- src/core/file_sys/card_image.cpp | 4 +- src/core/file_sys/content_archive.cpp | 1 + src/core/file_sys/control_metadata.cpp | 43 +- src/core/file_sys/control_metadata.h | 6 +- src/core/file_sys/program_metadata.cpp | 54 +- src/core/file_sys/program_metadata.h | 14 +- src/core/file_sys/savedata_factory.cpp | 58 +- src/core/file_sys/savedata_factory.h | 4 +- src/core/frontend/applets/cabinet.cpp | 20 + src/core/frontend/applets/cabinet.h | 37 + src/core/frontend/applets/controller.cpp | 2 +- src/core/frontend/applets/controller.h | 7 +- src/core/frontend/applets/error.cpp | 6 +- src/core/frontend/applets/error.h | 14 +- src/core/frontend/applets/mii_edit.cpp | 2 +- src/core/frontend/applets/mii_edit.h | 6 +- src/core/frontend/applets/profile_select.cpp | 3 +- src/core/frontend/applets/profile_select.h | 6 +- .../frontend/applets/software_keyboard.cpp | 5 +- src/core/frontend/applets/software_keyboard.h | 32 +- src/core/frontend/applets/web_browser.cpp | 11 +- src/core/frontend/applets/web_browser.h | 24 +- src/core/frontend/emu_window.h | 8 + src/core/frontend/framebuffer_layout.cpp | 2 + src/core/frontend/framebuffer_layout.h | 1 + src/core/hardware_interrupt_manager.cpp | 32 - src/core/hardware_interrupt_manager.h | 32 - src/core/hid/emulated_console.cpp | 104 +- src/core/hid/emulated_console.h | 15 +- src/core/hid/emulated_controller.cpp | 281 +- src/core/hid/emulated_controller.h | 51 +- src/core/hid/emulated_devices.cpp | 15 +- src/core/hid/emulated_devices.h | 1 + src/core/hid/input_converter.cpp | 25 +- src/core/hid/input_converter.h | 8 + src/core/hid/irs_types.h | 20 +- src/core/hle/ipc_helpers.h | 19 +- .../board/nintendo/nx/k_system_control.h | 4 + .../hle/kernel/global_scheduler_context.cpp | 22 + .../hle/kernel/global_scheduler_context.h | 8 + src/core/hle/kernel/hle_ipc.cpp | 135 +- src/core/hle/kernel/hle_ipc.h | 106 +- src/core/hle/kernel/init/init_slab_setup.cpp | 82 +- src/core/hle/kernel/init/init_slab_setup.h | 2 +- src/core/hle/kernel/k_class_token.cpp | 15 +- src/core/hle/kernel/k_class_token.h | 7 +- src/core/hle/kernel/k_client_port.cpp | 7 +- src/core/hle/kernel/k_client_port.h | 3 +- src/core/hle/kernel/k_client_session.cpp | 18 +- src/core/hle/kernel/k_client_session.h | 3 +- src/core/hle/kernel/k_code_memory.cpp | 2 +- src/core/hle/kernel/k_debug.h | 20 + src/core/hle/kernel/k_dynamic_page_manager.h | 169 + .../hle/kernel/k_dynamic_resource_manager.h | 61 + src/core/hle/kernel/k_dynamic_slab_heap.h | 122 + src/core/hle/kernel/k_event.cpp | 55 +- src/core/hle/kernel/k_event.h | 31 +- src/core/hle/kernel/k_event_info.h | 64 + src/core/hle/kernel/k_handle_table.cpp | 50 +- src/core/hle/kernel/k_handle_table.h | 92 +- src/core/hle/kernel/k_interrupt_manager.cpp | 29 +- src/core/hle/kernel/k_interrupt_manager.h | 4 +- src/core/hle/kernel/k_linked_list.h | 1 + src/core/hle/kernel/k_memory_block.h | 623 +- .../hle/kernel/k_memory_block_manager.cpp | 441 +- src/core/hle/kernel/k_memory_block_manager.h | 148 +- src/core/hle/kernel/k_memory_layout.cpp | 10 +- src/core/hle/kernel/k_memory_layout.h | 19 +- src/core/hle/kernel/k_memory_manager.cpp | 274 +- src/core/hle/kernel/k_memory_manager.h | 259 +- src/core/hle/kernel/k_memory_region_type.h | 123 +- src/core/hle/kernel/k_page_bitmap.h | 257 +- src/core/hle/kernel/k_page_buffer.cpp | 2 +- src/core/hle/kernel/k_page_buffer.h | 15 +- src/core/hle/kernel/k_page_group.h | 86 + src/core/hle/kernel/k_page_heap.cpp | 86 +- src/core/hle/kernel/k_page_heap.h | 39 +- src/core/hle/kernel/k_page_table.cpp | 2386 +++- src/core/hle/kernel/k_page_table.h | 434 +- src/core/hle/kernel/k_page_table_manager.h | 55 + src/core/hle/kernel/k_page_table_slab_heap.h | 93 + src/core/hle/kernel/k_port.cpp | 6 - src/core/hle/kernel/k_process.cpp | 138 +- src/core/hle/kernel/k_process.h | 86 +- src/core/hle/kernel/k_readable_event.cpp | 35 +- src/core/hle/kernel/k_readable_event.h | 17 +- src/core/hle/kernel/k_resource_limit.cpp | 11 +- src/core/hle/kernel/k_resource_limit.h | 9 +- src/core/hle/kernel/k_scheduler.cpp | 35 +- src/core/hle/kernel/k_scheduler_lock.h | 3 + src/core/hle/kernel/k_server_port.cpp | 6 - src/core/hle/kernel/k_server_port.h | 19 - src/core/hle/kernel/k_server_session.cpp | 432 +- src/core/hle/kernel/k_server_session.h | 89 +- src/core/hle/kernel/k_session.cpp | 7 +- src/core/hle/kernel/k_session.h | 3 +- src/core/hle/kernel/k_session_request.cpp | 61 + src/core/hle/kernel/k_session_request.h | 306 + src/core/hle/kernel/k_shared_memory.cpp | 8 +- src/core/hle/kernel/k_shared_memory.h | 6 +- src/core/hle/kernel/k_shared_memory_info.h | 3 +- src/core/hle/kernel/k_slab_heap.h | 35 +- src/core/hle/kernel/k_system_resource.cpp | 26 + src/core/hle/kernel/k_system_resource.h | 137 + src/core/hle/kernel/k_thread.cpp | 165 +- src/core/hle/kernel/k_thread.h | 18 +- src/core/hle/kernel/k_thread_local_page.h | 3 +- src/core/hle/kernel/k_transfer_memory.cpp | 2 +- src/core/hle/kernel/k_worker_task_manager.cpp | 2 +- src/core/hle/kernel/k_writable_event.cpp | 35 - src/core/hle/kernel/k_writable_event.h | 39 - src/core/hle/kernel/kernel.cpp | 336 +- src/core/hle/kernel/kernel.h | 58 +- src/core/hle/kernel/physical_core.cpp | 4 +- src/core/hle/kernel/physical_core.h | 2 +- src/core/hle/kernel/service_thread.cpp | 223 +- src/core/hle/kernel/service_thread.h | 6 +- src/core/hle/kernel/slab_helpers.h | 80 +- src/core/hle/kernel/svc.cpp | 473 +- src/core/hle/kernel/svc_common.h | 7 +- src/core/hle/kernel/svc_results.h | 1 + src/core/hle/kernel/svc_types.h | 521 +- src/core/hle/kernel/svc_wrap.h | 44 +- src/core/hle/result.h | 207 +- src/core/hle/service/acc/acc.cpp | 35 +- src/core/hle/service/acc/acc.h | 1 - src/core/hle/service/acc/acc_u0.cpp | 2 +- src/core/hle/service/acc/async_context.cpp | 2 +- src/core/hle/service/acc/profile_manager.cpp | 26 + src/core/hle/service/acc/profile_manager.h | 3 + src/core/hle/service/am/am.cpp | 29 +- src/core/hle/service/am/am.h | 1 + .../hle/service/am/applets/applet_cabinet.cpp | 177 + .../hle/service/am/applets/applet_cabinet.h | 104 + .../hle/service/am/applets/applet_error.cpp | 1 + .../am/applets/applet_general_backend.cpp | 2 + src/core/hle/service/am/applets/applets.cpp | 30 +- src/core/hle/service/am/applets/applets.h | 14 +- src/core/hle/service/audio/audctl.cpp | 16 + src/core/hle/service/audio/audin_u.cpp | 12 +- src/core/hle/service/audio/audout_u.cpp | 12 +- src/core/hle/service/audio/audren_u.cpp | 37 +- src/core/hle/service/audio/hwopus.cpp | 2 +- src/core/hle/service/bcat/backend/backend.cpp | 2 +- src/core/hle/service/bcat/bcat_module.cpp | 4 +- src/core/hle/service/es/es.cpp | 27 +- src/core/hle/service/filesystem/fsp_srv.cpp | 18 +- src/core/hle/service/filesystem/fsp_srv.h | 1 + src/core/hle/service/friend/friend.cpp | 13 +- src/core/hle/service/hid/controllers/npad.cpp | 40 +- .../hle/service/hid/controllers/palma.cpp | 229 + src/core/hle/service/hid/controllers/palma.h | 163 + src/core/hle/service/hid/errors.h | 2 + src/core/hle/service/hid/hid.cpp | 478 +- src/core/hle/service/hid/hid.h | 33 +- src/core/hle/service/hid/hidbus/ringcon.cpp | 8 +- src/core/hle/service/hid/irs.cpp | 3 +- .../service/hid/irsensor/pointing_processor.h | 4 +- src/core/hle/service/kernel_helpers.cpp | 7 +- src/core/hle/service/ldn/lan_discovery.cpp | 11 +- src/core/hle/service/ldn/ldn.cpp | 51 +- src/core/hle/service/ldr/ldr.cpp | 4 +- src/core/hle/service/mii/mii_manager.cpp | 161 +- src/core/hle/service/mii/mii_manager.h | 4 +- src/core/hle/service/nfc/mifare_user.cpp | 400 + src/core/hle/service/nfc/mifare_user.h | 52 + src/core/hle/service/nfc/nfc.cpp | 98 +- src/core/hle/service/nfc/nfc_device.cpp | 273 + src/core/hle/service/nfc/nfc_device.h | 77 + src/core/hle/service/nfc/nfc_result.h | 23 + src/core/hle/service/nfc/nfc_user.cpp | 365 + src/core/hle/service/nfc/nfc_user.h | 52 + src/core/hle/service/nfp/amiibo_crypto.cpp | 88 +- src/core/hle/service/nfp/amiibo_crypto.h | 13 +- src/core/hle/service/nfp/amiibo_types.h | 197 - src/core/hle/service/nfp/nfp.cpp | 1097 +- src/core/hle/service/nfp/nfp.h | 161 - src/core/hle/service/nfp/nfp_device.cpp | 719 + src/core/hle/service/nfp/nfp_device.h | 101 + src/core/hle/service/nfp/nfp_result.h | 24 + src/core/hle/service/nfp/nfp_types.h | 372 + src/core/hle/service/nfp/nfp_user.cpp | 662 +- src/core/hle/service/nfp/nfp_user.h | 55 +- src/core/hle/service/nifm/nifm.cpp | 41 +- src/core/hle/service/nim/nim.cpp | 4 +- .../service/ns/iplatform_service_manager.cpp | 11 +- src/core/hle/service/ns/ns.cpp | 30 +- src/core/hle/service/ns/ns.h | 3 + src/core/hle/service/nvdrv/core/container.cpp | 50 + src/core/hle/service/nvdrv/core/container.h | 52 + src/core/hle/service/nvdrv/core/nvmap.cpp | 273 + src/core/hle/service/nvdrv/core/nvmap.h | 176 + .../service/nvdrv/core/syncpoint_manager.cpp | 135 + .../service/nvdrv/core/syncpoint_manager.h | 134 + src/core/hle/service/nvdrv/devices/nvdevice.h | 8 + .../service/nvdrv/devices/nvdisp_disp0.cpp | 19 +- .../hle/service/nvdrv/devices/nvdisp_disp0.h | 15 +- .../service/nvdrv/devices/nvhost_as_gpu.cpp | 502 +- .../hle/service/nvdrv/devices/nvhost_as_gpu.h | 191 +- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 380 +- .../hle/service/nvdrv/devices/nvhost_ctrl.h | 114 +- .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 24 +- .../service/nvdrv/devices/nvhost_ctrl_gpu.h | 14 +- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 132 +- .../hle/service/nvdrv/devices/nvhost_gpu.h | 56 +- .../service/nvdrv/devices/nvhost_nvdec.cpp | 16 +- .../hle/service/nvdrv/devices/nvhost_nvdec.h | 6 +- .../nvdrv/devices/nvhost_nvdec_common.cpp | 81 +- .../nvdrv/devices/nvhost_nvdec_common.h | 23 +- .../hle/service/nvdrv/devices/nvhost_vic.cpp | 20 +- .../hle/service/nvdrv/devices/nvhost_vic.h | 6 +- src/core/hle/service/nvdrv/devices/nvmap.cpp | 233 +- src/core/hle/service/nvdrv/devices/nvmap.h | 56 +- src/core/hle/service/nvdrv/nvdata.h | 17 +- src/core/hle/service/nvdrv/nvdrv.cpp | 123 +- src/core/hle/service/nvdrv/nvdrv.h | 123 +- .../hle/service/nvdrv/nvdrv_interface.cpp | 31 +- src/core/hle/service/nvdrv/nvdrv_interface.h | 6 - .../hle/service/nvdrv/syncpoint_manager.cpp | 38 - .../hle/service/nvdrv/syncpoint_manager.h | 84 - .../nvflinger/buffer_item_consumer.cpp | 2 +- .../service/nvflinger/buffer_item_consumer.h | 2 +- .../nvflinger/buffer_queue_consumer.cpp | 11 +- .../service/nvflinger/buffer_queue_consumer.h | 8 +- .../service/nvflinger/buffer_queue_core.cpp | 6 +- .../hle/service/nvflinger/buffer_queue_core.h | 5 +- .../nvflinger/buffer_queue_producer.cpp | 40 +- .../service/nvflinger/buffer_queue_producer.h | 13 +- .../hle/service/nvflinger/consumer_base.cpp | 6 +- .../hle/service/nvflinger/consumer_base.h | 16 +- src/core/hle/service/nvflinger/nvflinger.cpp | 58 +- src/core/hle/service/nvflinger/nvflinger.h | 16 +- .../hle/service/nvflinger/producer_listener.h | 1 + src/core/hle/service/ptm/psm.cpp | 6 +- src/core/hle/service/ptm/ts.cpp | 15 +- src/core/hle/service/ptm/ts.h | 1 + src/core/hle/service/service.cpp | 26 +- src/core/hle/service/service.h | 6 + src/core/hle/service/set/set.cpp | 11 +- src/core/hle/service/set/set.h | 1 + src/core/hle/service/set/set_sys.cpp | 89 +- src/core/hle/service/set/set_sys.h | 3 + src/core/hle/service/sm/sm.cpp | 43 +- src/core/hle/service/sm/sm.h | 2 + src/core/hle/service/sm/sm_controller.cpp | 40 +- src/core/hle/service/sockets/bsd.cpp | 2 +- .../system_clock_context_update_callback.cpp | 10 +- .../system_clock_context_update_callback.h | 6 +- .../hle/service/time/time_zone_manager.cpp | 1 + .../hle/service/vi/display/vi_display.cpp | 34 +- src/core/hle/service/vi/display/vi_display.h | 27 +- src/core/hle/service/vi/vi.cpp | 49 +- src/core/hle/service/vi/vi_results.h | 15 + src/core/internal_network/network.cpp | 12 +- .../internal_network/network_interface.cpp | 3 +- src/core/internal_network/socket_proxy.cpp | 4 + src/core/internal_network/sockets.h | 11 + src/core/loader/loader.cpp | 4 + src/core/memory.cpp | 120 +- src/core/memory.h | 35 + src/core/memory/dmnt_cheat_vm.cpp | 2 +- src/core/precompiled_headers.h | 11 + src/core/reporter.cpp | 35 +- src/core/reporter.h | 16 +- src/core/telemetry_session.cpp | 2 + src/dedicated_room/CMakeLists.txt | 7 +- src/dedicated_room/precompiled_headers.h | 6 + src/input_common/CMakeLists.txt | 24 +- src/input_common/drivers/camera.cpp | 2 +- src/input_common/drivers/camera.h | 5 +- src/input_common/drivers/gc_adapter.cpp | 10 +- src/input_common/drivers/gc_adapter.h | 6 +- src/input_common/drivers/mouse.cpp | 3 +- src/input_common/drivers/mouse.h | 2 +- src/input_common/drivers/sdl_driver.cpp | 87 +- src/input_common/drivers/sdl_driver.h | 7 +- src/input_common/drivers/tas_input.cpp | 1 + src/input_common/drivers/virtual_amiibo.cpp | 128 + src/input_common/drivers/virtual_amiibo.h | 65 + src/input_common/drivers/virtual_gamepad.cpp | 78 + src/input_common/drivers/virtual_gamepad.h | 73 + .../helpers/stick_from_buttons.cpp | 25 +- .../helpers/touch_from_buttons.cpp | 22 +- src/input_common/input_engine.cpp | 37 + src/input_common/input_engine.h | 24 +- src/input_common/input_poller.cpp | 114 +- src/input_common/input_poller.h | 10 + src/input_common/main.cpp | 146 +- src/input_common/main.h | 17 + src/input_common/precompiled_headers.h | 6 + src/network/CMakeLists.txt | 7 +- src/network/network.cpp | 2 +- src/network/precompiled_headers.h | 6 + src/shader_recompiler/CMakeLists.txt | 22 +- .../backend/glasm/emit_glasm.cpp | 6 +- .../glasm/emit_glasm_bitwise_conversion.cpp | 4 + .../glasm/emit_glasm_context_get_set.cpp | 44 +- .../backend/glasm/emit_glasm_instructions.h | 3 + .../glasm/emit_glasm_not_implemented.cpp | 4 - .../backend/glasm/glasm_emit_context.cpp | 4 + .../backend/glsl/emit_glsl.cpp | 2 +- .../glsl/emit_glsl_bitwise_conversion.cpp | 4 + .../glsl/emit_glsl_context_get_set.cpp | 19 + .../backend/glsl/emit_glsl_instructions.h | 3 + .../glsl/emit_glsl_not_implemented.cpp | 4 - .../backend/glsl/glsl_emit_context.cpp | 3 + .../backend/spirv/emit_spirv.cpp | 13 +- .../backend/spirv/emit_spirv.h | 4 + .../spirv/emit_spirv_bitwise_conversion.cpp | 4 + .../spirv/emit_spirv_context_get_set.cpp | 28 +- .../backend/spirv/emit_spirv_control_flow.cpp | 2 +- .../backend/spirv/emit_spirv_instructions.h | 5 +- .../backend/spirv/emit_spirv_special.cpp | 5 +- .../backend/spirv/emit_spirv_warp.cpp | 35 +- .../backend/spirv/spirv_emit_context.cpp | 42 + .../backend/spirv/spirv_emit_context.h | 5 + src/shader_recompiler/environment.h | 4 + .../frontend/ir/ir_emitter.cpp | 17 + .../frontend/ir/ir_emitter.h | 4 + .../frontend/ir/microinstruction.cpp | 5 - src/shader_recompiler/frontend/ir/opcodes.h | 2 + src/shader_recompiler/frontend/ir/opcodes.inc | 3 + src/shader_recompiler/frontend/ir/patch.h | 4 +- src/shader_recompiler/frontend/ir/type.h | 31 +- src/shader_recompiler/frontend/ir/value.cpp | 3 + src/shader_recompiler/frontend/ir/value.h | 17 +- .../frontend/maxwell/control_flow.cpp | 1 + .../frontend/maxwell/decode.cpp | 1 + .../maxwell/structured_control_flow.cpp | 7 +- .../floating_point_conversion_integer.cpp | 7 +- .../translate/impl/move_special_register.cpp | 3 +- .../frontend/maxwell/translate_program.cpp | 132 +- .../frontend/maxwell/translate_program.h | 9 + src/shader_recompiler/host_translate_info.h | 2 + .../ir_opt/collect_shader_info_pass.cpp | 3 + .../ir_opt/dead_code_elimination_pass.cpp | 100 +- src/shader_recompiler/ir_opt/layer_pass.cpp | 68 + src/shader_recompiler/ir_opt/passes.h | 8 +- .../ir_opt/position_pass.cpp | 77 + src/shader_recompiler/ir_opt/texture_pass.cpp | 147 +- src/shader_recompiler/precompiled_headers.h | 7 + src/shader_recompiler/profile.h | 1 + src/shader_recompiler/runtime_info.h | 2 + src/shader_recompiler/shader_info.h | 22 + src/tests/CMakeLists.txt | 5 + src/tests/core/core_timing.cpp | 3 - src/tests/precompiled_headers.h | 6 + src/tests/video_core/buffer_base.cpp | 2 +- src/video_core/CMakeLists.txt | 109 +- src/video_core/buffer_cache/buffer_base.h | 2 +- src/video_core/buffer_cache/buffer_cache.h | 220 +- src/video_core/cdma_pusher.cpp | 29 +- src/video_core/cdma_pusher.h | 18 +- src/video_core/command_classes/host1x.cpp | 29 - src/video_core/control/channel_state.cpp | 40 + src/video_core/control/channel_state.h | 68 + .../control/channel_state_cache.cpp | 14 + src/video_core/control/channel_state_cache.h | 99 + .../control/channel_state_cache.inc | 86 + src/video_core/control/scheduler.cpp | 32 + src/video_core/control/scheduler.h | 37 + src/video_core/dirty_flags.cpp | 24 +- src/video_core/dma_pusher.cpp | 26 +- src/video_core/dma_pusher.h | 39 +- src/video_core/engines/draw_manager.cpp | 201 + src/video_core/engines/draw_manager.h | 69 + src/video_core/engines/engine_upload.cpp | 45 +- src/video_core/engines/engine_upload.h | 8 +- src/video_core/engines/fermi_2d.cpp | 26 +- src/video_core/engines/fermi_2d.h | 11 +- src/video_core/engines/kepler_compute.cpp | 13 +- src/video_core/engines/kepler_compute.h | 14 +- src/video_core/engines/kepler_memory.cpp | 13 +- src/video_core/engines/maxwell_3d.cpp | 535 +- src/video_core/engines/maxwell_3d.h | 4002 ++++-- src/video_core/engines/maxwell_dma.cpp | 311 +- src/video_core/engines/maxwell_dma.h | 11 +- src/video_core/engines/puller.cpp | 309 + src/video_core/engines/puller.h | 177 + src/video_core/engines/sw_blitter/blitter.cpp | 238 + src/video_core/engines/sw_blitter/blitter.h | 27 + .../engines/sw_blitter/converter.cpp | 1234 ++ src/video_core/engines/sw_blitter/converter.h | 36 + .../engines/sw_blitter/generate_converters.py | 136 + src/video_core/fence_manager.h | 104 +- src/video_core/gpu.cpp | 721 +- src/video_core/gpu.h | 145 +- src/video_core/gpu_thread.cpp | 26 +- src/video_core/gpu_thread.h | 15 +- .../codecs/codec.cpp | 44 +- .../codecs/codec.h | 21 +- .../codecs/h264.cpp | 17 +- .../{command_classes => host1x}/codecs/h264.h | 16 +- .../codecs/vp8.cpp | 12 +- .../{command_classes => host1x}/codecs/vp8.h | 15 +- .../codecs/vp9.cpp | 23 +- .../{command_classes => host1x}/codecs/vp9.h | 22 +- .../codecs/vp9_types.h | 1 - src/video_core/host1x/control.cpp | 33 + .../host1x.h => host1x/control.h} | 20 +- src/video_core/host1x/host1x.cpp | 17 + src/video_core/host1x/host1x.h | 57 + .../{command_classes => host1x}/nvdec.cpp | 11 +- .../{command_classes => host1x}/nvdec.h | 14 +- .../nvdec_common.h | 4 +- .../sync_manager.cpp | 13 +- .../sync_manager.h | 12 +- src/video_core/host1x/syncpoint_manager.cpp | 106 + src/video_core/host1x/syncpoint_manager.h | 96 + .../{command_classes => host1x}/vic.cpp | 36 +- .../{command_classes => host1x}/vic.h | 13 +- src/video_core/host_shaders/CMakeLists.txt | 7 + .../host_shaders/StringShaderHeader.cmake | 6 +- src/video_core/host_shaders/astc_decoder.comp | 2 +- src/video_core/host_shaders/opengl_smaa.glsl | 1339 ++ .../smaa_blending_weight_calculation.frag | 36 + .../smaa_blending_weight_calculation.vert | 43 + .../host_shaders/smaa_edge_detection.frag | 26 + .../host_shaders/smaa_edge_detection.vert | 40 + .../smaa_neighborhood_blending.frag | 31 + .../smaa_neighborhood_blending.vert | 41 + src/video_core/macro/macro.cpp | 4 + src/video_core/macro/macro_hle.cpp | 134 +- src/video_core/macro/macro_interpreter.cpp | 4 +- src/video_core/macro/macro_jit_x64.cpp | 65 +- src/video_core/memory_manager.cpp | 797 +- src/video_core/memory_manager.h | 185 +- src/video_core/precompiled_headers.h | 6 + src/video_core/pte_kind.h | 264 + src/video_core/query_cache.h | 22 +- src/video_core/rasterizer_interface.h | 26 +- src/video_core/renderer_base.cpp | 8 +- .../renderer_null/null_rasterizer.cpp | 90 + .../renderer_null/null_rasterizer.h | 78 + .../renderer_null/renderer_null.cpp | 24 + src/video_core/renderer_null/renderer_null.h | 36 + .../renderer_opengl/gl_buffer_cache.cpp | 17 +- .../renderer_opengl/gl_compute_pipeline.cpp | 20 +- .../renderer_opengl/gl_compute_pipeline.h | 16 +- src/video_core/renderer_opengl/gl_device.cpp | 11 +- src/video_core/renderer_opengl/gl_device.h | 8 +- .../renderer_opengl/gl_fence_manager.cpp | 13 +- .../renderer_opengl/gl_fence_manager.h | 6 +- .../renderer_opengl/gl_graphics_pipeline.cpp | 69 +- .../renderer_opengl/gl_graphics_pipeline.h | 20 +- .../renderer_opengl/gl_query_cache.cpp | 5 +- .../renderer_opengl/gl_query_cache.h | 3 +- .../renderer_opengl/gl_rasterizer.cpp | 359 +- .../renderer_opengl/gl_rasterizer.h | 26 +- .../renderer_opengl/gl_shader_cache.cpp | 208 +- .../renderer_opengl/gl_shader_cache.h | 11 +- .../renderer_opengl/gl_state_tracker.cpp | 70 +- .../renderer_opengl/gl_state_tracker.h | 83 +- .../renderer_opengl/gl_texture_cache.cpp | 2 + .../renderer_opengl/maxwell_to_gl.h | 292 +- .../renderer_opengl/renderer_opengl.cpp | 129 +- .../renderer_opengl/renderer_opengl.h | 15 +- .../renderer_vulkan/fixed_pipeline_state.cpp | 311 +- .../renderer_vulkan/fixed_pipeline_state.h | 16 +- .../renderer_vulkan/maxwell_to_vk.cpp | 350 +- .../renderer_vulkan/maxwell_to_vk.h | 2 +- .../renderer_vulkan/pipeline_helper.h | 24 +- .../renderer_vulkan/renderer_vulkan.cpp | 30 +- .../renderer_vulkan/vk_blit_screen.cpp | 43 +- .../renderer_vulkan/vk_blit_screen.h | 11 +- .../renderer_vulkan/vk_buffer_cache.cpp | 3 + .../renderer_vulkan/vk_compute_pass.cpp | 14 +- .../renderer_vulkan/vk_compute_pass.h | 4 +- .../renderer_vulkan/vk_compute_pipeline.cpp | 6 +- .../renderer_vulkan/vk_compute_pipeline.h | 2 +- .../renderer_vulkan/vk_descriptor_pool.cpp | 1 + .../renderer_vulkan/vk_fence_manager.cpp | 15 +- .../renderer_vulkan/vk_fence_manager.h | 6 +- src/video_core/renderer_vulkan/vk_fsr.cpp | 6 +- .../renderer_vulkan/vk_graphics_pipeline.cpp | 76 +- .../renderer_vulkan/vk_graphics_pipeline.h | 34 +- .../renderer_vulkan/vk_master_semaphore.cpp | 8 +- .../renderer_vulkan/vk_master_semaphore.h | 1 + .../renderer_vulkan/vk_pipeline_cache.cpp | 172 +- .../renderer_vulkan/vk_pipeline_cache.h | 6 +- .../renderer_vulkan/vk_query_cache.cpp | 16 +- .../renderer_vulkan/vk_query_cache.h | 5 +- .../renderer_vulkan/vk_rasterizer.cpp | 279 +- .../renderer_vulkan/vk_rasterizer.h | 34 +- .../renderer_vulkan/vk_render_pass_cache.h | 2 +- .../renderer_vulkan/vk_scheduler.cpp | 15 +- src/video_core/renderer_vulkan/vk_scheduler.h | 5 +- src/video_core/renderer_vulkan/vk_smaa.cpp | 761 ++ src/video_core/renderer_vulkan/vk_smaa.h | 86 + .../renderer_vulkan/vk_state_tracker.cpp | 68 +- .../renderer_vulkan/vk_state_tracker.h | 27 +- .../renderer_vulkan/vk_swapchain.cpp | 31 +- src/video_core/renderer_vulkan/vk_swapchain.h | 14 +- .../renderer_vulkan/vk_texture_cache.cpp | 48 +- .../renderer_vulkan/vk_texture_cache.h | 16 +- src/video_core/shader_cache.cpp | 37 +- src/video_core/shader_cache.h | 16 +- src/video_core/shader_environment.cpp | 124 +- src/video_core/shader_environment.h | 25 +- src/video_core/smaa_area_tex.h | 11223 ++++++++++++++++ src/video_core/smaa_search_tex.h | 88 + src/video_core/surface.cpp | 44 +- src/video_core/surface.h | 20 +- .../texture_cache/descriptor_table.h | 2 +- .../texture_cache/format_lookup_table.cpp | 8 +- src/video_core/texture_cache/formatter.cpp | 1 + src/video_core/texture_cache/formatter.h | 10 +- src/video_core/texture_cache/image_base.cpp | 13 +- src/video_core/texture_cache/image_base.h | 3 + src/video_core/texture_cache/image_info.cpp | 37 +- src/video_core/texture_cache/render_targets.h | 3 +- src/video_core/texture_cache/slot_vector.h | 1 + .../texture_cache/texture_cache.cpp | 15 + src/video_core/texture_cache/texture_cache.h | 258 +- .../texture_cache/texture_cache_base.h | 92 +- src/video_core/texture_cache/util.cpp | 4 +- src/video_core/textures/astc.cpp | 9 +- src/video_core/textures/decoders.cpp | 245 +- src/video_core/textures/decoders.h | 33 +- src/video_core/textures/texture.cpp | 3 +- src/video_core/transform_feedback.cpp | 119 +- src/video_core/transform_feedback.h | 3 +- src/video_core/video_core.cpp | 4 + .../vulkan_common/vulkan_debug_callback.cpp | 2 + .../vulkan_common/vulkan_device.cpp | 328 +- src/video_core/vulkan_common/vulkan_device.h | 34 +- .../vulkan_common/vulkan_instance.cpp | 28 +- .../vulkan_common/vulkan_memory_allocator.cpp | 1 + .../vulkan_common/vulkan_surface.cpp | 38 +- .../vulkan_common/vulkan_wrapper.cpp | 77 +- src/video_core/vulkan_common/vulkan_wrapper.h | 75 +- src/web_service/CMakeLists.txt | 7 +- src/web_service/precompiled_headers.h | 6 + src/web_service/web_backend.cpp | 3 +- src/yuzu/CMakeLists.txt | 51 +- src/yuzu/applets/qt_amiibo_settings.cpp | 264 + src/yuzu/applets/qt_amiibo_settings.h | 83 + src/yuzu/applets/qt_amiibo_settings.ui | 494 + src/yuzu/applets/qt_controller.cpp | 4 +- src/yuzu/applets/qt_controller.h | 4 +- src/yuzu/applets/qt_controller.ui | 2 +- src/yuzu/applets/qt_error.cpp | 6 +- src/yuzu/applets/qt_error.h | 8 +- src/yuzu/applets/qt_profile_select.cpp | 3 +- src/yuzu/applets/qt_profile_select.h | 4 +- src/yuzu/applets/qt_software_keyboard.cpp | 5 +- src/yuzu/applets/qt_software_keyboard.h | 16 +- src/yuzu/applets/qt_web_browser.cpp | 11 +- src/yuzu/applets/qt_web_browser.h | 13 +- src/yuzu/bootmanager.cpp | 419 +- src/yuzu/bootmanager.h | 10 +- src/yuzu/compatdb.cpp | 162 +- src/yuzu/compatdb.h | 11 + src/yuzu/compatdb.ui | 405 +- src/yuzu/configuration/config.cpp | 92 +- src/yuzu/configuration/config.h | 2 + src/yuzu/configuration/configure_audio.cpp | 4 +- src/yuzu/configuration/configure_camera.cpp | 7 + src/yuzu/configuration/configure_camera.h | 2 + .../configuration/configure_cpu_debug.cpp | 4 + src/yuzu/configuration/configure_cpu_debug.ui | 13 + src/yuzu/configuration/configure_debug.cpp | 2 + src/yuzu/configuration/configure_debug.ui | 10 + src/yuzu/configuration/configure_graphics.cpp | 54 +- src/yuzu/configuration/configure_graphics.h | 2 + src/yuzu/configuration/configure_graphics.ui | 167 + src/yuzu/configuration/configure_input.cpp | 7 +- .../configure_input_advanced.cpp | 4 + .../configure_input_per_game.cpp | 115 + .../configuration/configure_input_per_game.h | 45 + .../configuration/configure_input_per_game.ui | 333 + .../configuration/configure_input_player.cpp | 11 +- .../configuration/configure_input_player.h | 2 +- src/yuzu/configuration/configure_per_game.cpp | 5 +- src/yuzu/configuration/configure_per_game.h | 6 + .../configure_profile_manager.cpp | 67 +- .../configuration/configure_profile_manager.h | 27 +- .../configure_profile_manager.ui | 6 + src/yuzu/configuration/configure_system.cpp | 4 + src/yuzu/configuration/configure_system.ui | 14 + src/yuzu/configuration/configure_ui.cpp | 9 + src/yuzu/configuration/configure_ui.ui | 23 +- src/yuzu/configuration/input_profiles.cpp | 2 + src/yuzu/game_list.cpp | 18 + src/yuzu/game_list.h | 7 + src/yuzu/game_list_p.h | 12 +- src/yuzu/main.cpp | 516 +- src/yuzu/main.h | 34 +- src/yuzu/main.ui | 10 +- src/yuzu/multiplayer/chat_room.h | 1 + src/yuzu/multiplayer/direct_connect.cpp | 2 +- src/yuzu/multiplayer/state.cpp | 2 +- src/yuzu/multiplayer/validation.h | 16 +- src/yuzu/precompiled_headers.h | 6 + src/yuzu/startup_checks.cpp | 138 +- src/yuzu/startup_checks.h | 6 +- src/yuzu/uisettings.h | 8 + src/yuzu_cmd/CMakeLists.txt | 18 +- src/yuzu_cmd/config.cpp | 10 +- src/yuzu_cmd/default_ini.h | 33 +- src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | 4 +- src/yuzu_cmd/emu_window/emu_window_sdl2.h | 2 + .../emu_window/emu_window_sdl2_gl.cpp | 2 + .../emu_window/emu_window_sdl2_null.cpp | 51 + .../emu_window/emu_window_sdl2_null.h | 26 + .../emu_window/emu_window_sdl2_vk.cpp | 34 +- src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | 2 - src/yuzu_cmd/precompiled_headers.h | 6 + src/yuzu_cmd/yuzu.cpp | 9 +- 784 files changed, 82669 insertions(+), 32731 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/blank_issue_template.yml delete mode 100644 .github/ISSUE_TEMPLATE/bug-report-feature-request.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 dist/languages/uk.ts create mode 100644 externals/find-modules/FindDiscordRPC.cmake delete mode 100644 externals/find-modules/FindLibUSB.cmake create mode 100644 externals/find-modules/Findenet.cmake create mode 100644 externals/find-modules/Findhttplib.cmake create mode 100644 externals/find-modules/Findinih.cmake create mode 100644 externals/find-modules/Findlibusb.cmake create mode 100644 src/audio_core/precompiled_headers.h create mode 100644 src/common/address_space.cpp create mode 100644 src/common/address_space.h create mode 100644 src/common/address_space.inc create mode 100644 src/common/common_precompiled_headers.h create mode 100644 src/common/multi_level_page_table.cpp create mode 100644 src/common/multi_level_page_table.h create mode 100644 src/common/multi_level_page_table.inc create mode 100644 src/common/polyfill_ranges.h create mode 100644 src/common/polyfill_thread.h create mode 100644 src/common/precompiled_headers.h create mode 100644 src/core/frontend/applets/cabinet.cpp create mode 100644 src/core/frontend/applets/cabinet.h delete mode 100644 src/core/hardware_interrupt_manager.cpp delete mode 100644 src/core/hardware_interrupt_manager.h create mode 100644 src/core/hle/kernel/k_debug.h create mode 100644 src/core/hle/kernel/k_dynamic_page_manager.h create mode 100644 src/core/hle/kernel/k_dynamic_resource_manager.h create mode 100644 src/core/hle/kernel/k_dynamic_slab_heap.h create mode 100644 src/core/hle/kernel/k_event_info.h create mode 100644 src/core/hle/kernel/k_page_table_manager.h create mode 100644 src/core/hle/kernel/k_page_table_slab_heap.h create mode 100644 src/core/hle/kernel/k_session_request.cpp create mode 100644 src/core/hle/kernel/k_session_request.h create mode 100644 src/core/hle/kernel/k_system_resource.cpp create mode 100644 src/core/hle/kernel/k_system_resource.h delete mode 100644 src/core/hle/kernel/k_writable_event.cpp delete mode 100644 src/core/hle/kernel/k_writable_event.h create mode 100644 src/core/hle/service/am/applets/applet_cabinet.cpp create mode 100644 src/core/hle/service/am/applets/applet_cabinet.h create mode 100644 src/core/hle/service/hid/controllers/palma.cpp create mode 100644 src/core/hle/service/hid/controllers/palma.h create mode 100644 src/core/hle/service/nfc/mifare_user.cpp create mode 100644 src/core/hle/service/nfc/mifare_user.h create mode 100644 src/core/hle/service/nfc/nfc_device.cpp create mode 100644 src/core/hle/service/nfc/nfc_device.h create mode 100644 src/core/hle/service/nfc/nfc_result.h create mode 100644 src/core/hle/service/nfc/nfc_user.cpp create mode 100644 src/core/hle/service/nfc/nfc_user.h delete mode 100644 src/core/hle/service/nfp/amiibo_types.h create mode 100644 src/core/hle/service/nfp/nfp_device.cpp create mode 100644 src/core/hle/service/nfp/nfp_device.h create mode 100644 src/core/hle/service/nfp/nfp_result.h create mode 100644 src/core/hle/service/nfp/nfp_types.h create mode 100644 src/core/hle/service/nvdrv/core/container.cpp create mode 100644 src/core/hle/service/nvdrv/core/container.h create mode 100644 src/core/hle/service/nvdrv/core/nvmap.cpp create mode 100644 src/core/hle/service/nvdrv/core/nvmap.h create mode 100644 src/core/hle/service/nvdrv/core/syncpoint_manager.cpp create mode 100644 src/core/hle/service/nvdrv/core/syncpoint_manager.h delete mode 100644 src/core/hle/service/nvdrv/syncpoint_manager.cpp delete mode 100644 src/core/hle/service/nvdrv/syncpoint_manager.h create mode 100644 src/core/hle/service/vi/vi_results.h create mode 100644 src/core/precompiled_headers.h create mode 100644 src/dedicated_room/precompiled_headers.h create mode 100644 src/input_common/drivers/virtual_amiibo.cpp create mode 100644 src/input_common/drivers/virtual_amiibo.h create mode 100644 src/input_common/drivers/virtual_gamepad.cpp create mode 100644 src/input_common/drivers/virtual_gamepad.h create mode 100644 src/input_common/precompiled_headers.h create mode 100644 src/network/precompiled_headers.h create mode 100644 src/shader_recompiler/ir_opt/layer_pass.cpp create mode 100644 src/shader_recompiler/ir_opt/position_pass.cpp create mode 100644 src/shader_recompiler/precompiled_headers.h create mode 100644 src/tests/precompiled_headers.h delete mode 100644 src/video_core/command_classes/host1x.cpp create mode 100644 src/video_core/control/channel_state.cpp create mode 100644 src/video_core/control/channel_state.h create mode 100644 src/video_core/control/channel_state_cache.cpp create mode 100644 src/video_core/control/channel_state_cache.h create mode 100644 src/video_core/control/channel_state_cache.inc create mode 100644 src/video_core/control/scheduler.cpp create mode 100644 src/video_core/control/scheduler.h create mode 100644 src/video_core/engines/draw_manager.cpp create mode 100644 src/video_core/engines/draw_manager.h create mode 100644 src/video_core/engines/puller.cpp create mode 100644 src/video_core/engines/puller.h create mode 100644 src/video_core/engines/sw_blitter/blitter.cpp create mode 100644 src/video_core/engines/sw_blitter/blitter.h create mode 100644 src/video_core/engines/sw_blitter/converter.cpp create mode 100644 src/video_core/engines/sw_blitter/converter.h create mode 100644 src/video_core/engines/sw_blitter/generate_converters.py rename src/video_core/{command_classes => host1x}/codecs/codec.cpp (89%) rename src/video_core/{command_classes => host1x}/codecs/codec.h (74%) rename src/video_core/{command_classes => host1x}/codecs/h264.cpp (92%) rename src/video_core/{command_classes => host1x}/codecs/h264.h (95%) rename src/video_core/{command_classes => host1x}/codecs/vp8.cpp (80%) rename src/video_core/{command_classes => host1x}/codecs/vp8.h (88%) rename src/video_core/{command_classes => host1x}/codecs/vp9.cpp (98%) rename src/video_core/{command_classes => host1x}/codecs/vp9.h (91%) rename src/video_core/{command_classes => host1x}/codecs/vp9_types.h (99%) create mode 100644 src/video_core/host1x/control.cpp rename src/video_core/{command_classes/host1x.h => host1x/control.h} (62%) create mode 100644 src/video_core/host1x/host1x.cpp create mode 100644 src/video_core/host1x/host1x.h rename src/video_core/{command_classes => host1x}/nvdec.cpp (81%) rename src/video_core/{command_classes => host1x}/nvdec.h (79%) rename src/video_core/{command_classes => host1x}/nvdec_common.h (98%) rename src/video_core/{command_classes => host1x}/sync_manager.cpp (73%) rename src/video_core/{command_classes => host1x}/sync_manager.h (88%) create mode 100644 src/video_core/host1x/syncpoint_manager.cpp create mode 100644 src/video_core/host1x/syncpoint_manager.h rename src/video_core/{command_classes => host1x}/vic.cpp (87%) rename src/video_core/{command_classes => host1x}/vic.h (86%) create mode 100644 src/video_core/host_shaders/opengl_smaa.glsl create mode 100644 src/video_core/host_shaders/smaa_blending_weight_calculation.frag create mode 100644 src/video_core/host_shaders/smaa_blending_weight_calculation.vert create mode 100644 src/video_core/host_shaders/smaa_edge_detection.frag create mode 100644 src/video_core/host_shaders/smaa_edge_detection.vert create mode 100644 src/video_core/host_shaders/smaa_neighborhood_blending.frag create mode 100644 src/video_core/host_shaders/smaa_neighborhood_blending.vert create mode 100644 src/video_core/precompiled_headers.h create mode 100644 src/video_core/pte_kind.h create mode 100644 src/video_core/renderer_null/null_rasterizer.cpp create mode 100644 src/video_core/renderer_null/null_rasterizer.h create mode 100644 src/video_core/renderer_null/renderer_null.cpp create mode 100644 src/video_core/renderer_null/renderer_null.h create mode 100644 src/video_core/renderer_vulkan/vk_smaa.cpp create mode 100644 src/video_core/renderer_vulkan/vk_smaa.h create mode 100644 src/video_core/smaa_area_tex.h create mode 100644 src/video_core/smaa_search_tex.h create mode 100644 src/video_core/texture_cache/texture_cache.cpp create mode 100644 src/web_service/precompiled_headers.h create mode 100644 src/yuzu/applets/qt_amiibo_settings.cpp create mode 100644 src/yuzu/applets/qt_amiibo_settings.h create mode 100644 src/yuzu/applets/qt_amiibo_settings.ui create mode 100644 src/yuzu/configuration/configure_input_per_game.cpp create mode 100644 src/yuzu/configuration/configure_input_per_game.h create mode 100644 src/yuzu/configuration/configure_input_per_game.ui create mode 100644 src/yuzu/precompiled_headers.h create mode 100644 src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp create mode 100644 src/yuzu_cmd/emu_window/emu_window_sdl2_null.h create mode 100644 src/yuzu_cmd/precompiled_headers.h diff --git a/.ci/scripts/clang/docker.sh b/.ci/scripts/clang/docker.sh index 792ef4a..5176954 100755 --- a/.ci/scripts/clang/docker.sh +++ b/.ci/scripts/clang/docker.sh @@ -11,6 +11,7 @@ ccache -s mkdir build || true && cd build cmake .. \ -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \ -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ \ -DCMAKE_C_COMPILER=/usr/lib/ccache/clang \ -DCMAKE_INSTALL_PREFIX="/usr" \ @@ -19,6 +20,7 @@ cmake .. \ -DENABLE_QT_TRANSLATION=ON \ -DUSE_DISCORD_PRESENCE=ON \ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \ + -DYUZU_USE_BUNDLED_FFMPEG=ON \ -GNinja ninja diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh index e85dba0..c8bc56c 100755 --- a/.ci/scripts/linux/docker.sh +++ b/.ci/scripts/linux/docker.sh @@ -12,6 +12,7 @@ mkdir build || true && cd build cmake .. \ -DBoost_USE_STATIC_LIBS=ON \ -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \ -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \ -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \ -DCMAKE_INSTALL_PREFIX="/usr" \ @@ -33,16 +34,14 @@ DESTDIR="$PWD/AppDir" ninja install rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester # Download tools needed to build an AppImage -wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/linuxdeploy-x86_64.AppImage -wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/linuxdeploy-plugin-qt-x86_64.AppImage +wget -nc https://raw.githubusercontent.com/yuzu-emu/ext-linux-bin/main/gcc/deploy-linux.sh wget -nc https://raw.githubusercontent.com/yuzu-emu/AppImageKit-checkrt/old/AppRun.sh wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/exec-x86_64.so # Set executable bit chmod 755 \ + deploy-linux.sh \ AppRun.sh \ exec-x86_64.so \ - linuxdeploy-x86_64.AppImage \ - linuxdeploy-plugin-qt-x86_64.AppImage # Workaround for https://github.com/AppImage/AppImageKit/issues/828 export APPIMAGE_EXTRACT_AND_RUN=1 @@ -52,7 +51,7 @@ mkdir -p AppDir/usr/optional/libstdc++ mkdir -p AppDir/usr/optional/libgcc_s # Deploy yuzu's needed dependencies -./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt +DEPLOY_QT=1 ./deploy-linux.sh AppDir/usr/bin/yuzu AppDir # Workaround for libQt5MultimediaGstTools indirectly requiring libwayland-client and breaking Vulkan usage on end-user systems find AppDir -type f -regex '.*libwayland-client\.so.*' -delete -print diff --git a/.ci/scripts/transifex/docker.sh b/.ci/scripts/transifex/docker.sh index 6237b3f..6ddbfd0 100755 --- a/.ci/scripts/transifex/docker.sh +++ b/.ci/scripts/transifex/docker.sh @@ -3,15 +3,6 @@ # SPDX-FileCopyrightText: 2021 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -# Setup RC file for tx -cat << EOF > ~/.transifexrc -[https://www.transifex.com] -hostname = https://www.transifex.com -username = api -password = $TRANSIFEX_API_TOKEN -EOF - - set -x echo -e "\e[1m\e[33mBuild tools information:\e[0m" @@ -19,9 +10,6 @@ cmake --version gcc -v tx --version -# vcpkg needs these: curl zip unzip tar, have tar -apt-get install -y curl zip unzip - mkdir build && cd build cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF -DYUZU_TESTS=OFF -DYUZU_USE_BUNDLED_VCPKG=ON make translation diff --git a/.ci/scripts/windows/docker.sh b/.ci/scripts/windows/docker.sh index 6f522fe..0be3613 100755 --- a/.ci/scripts/windows/docker.sh +++ b/.ci/scripts/windows/docker.sh @@ -10,13 +10,9 @@ set -e ccache -sv mkdir -p build && cd build -export LDFLAGS="-fuse-ld=lld" -# -femulated-tls required due to an incompatibility between GCC and Clang -# TODO(lat9nq): If this is widespread, we probably need to add this to CMakeLists where appropriate -export CXXFLAGS="-femulated-tls" cmake .. \ -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWClangCross.cmake" \ + -DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWCross.cmake" \ -DDISPLAY_VERSION="$1" \ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ -DENABLE_QT_TRANSLATION=ON \ diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml index ea405e5..c379dd7 100644 --- a/.ci/templates/build-msvc.yml +++ b/.ci/templates/build-msvc.yml @@ -9,7 +9,7 @@ parameters: steps: - script: choco install vulkan-sdk displayName: 'Install vulkan-sdk' -- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 17 2022" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd .. +- script: refreshenv && mkdir build && cd build && cmake -E env CXXFLAGS="/Gw /GA /Gr /Ob2" cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POLICY_DEFAULT_CMP0069=NEW -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd .. displayName: 'Configure CMake' - task: MSBuild@1 displayName: 'Build' diff --git a/.github/ISSUE_TEMPLATE/blank_issue_template.yml b/.github/ISSUE_TEMPLATE/blank_issue_template.yml new file mode 100644 index 0000000..49b7f38 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/blank_issue_template.yml @@ -0,0 +1,10 @@ +name: New Issue (Developers Only) +description: A blank issue template for developers only. If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template. +body: + - type: markdown + attributes: + value: | + **If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.** + - type: textarea + attributes: + label: "Issue" diff --git a/.github/ISSUE_TEMPLATE/bug-report-feature-request.md b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md deleted file mode 100644 index 8086132..0000000 --- a/.github/ISSUE_TEMPLATE/bug-report-feature-request.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: Bug Report / Feature Request -about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better. -title: '' -labels: '' -assignees: '' - ---- - - - - - - - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..1405ccc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,64 @@ +name: Bug Report +description: File a bug report +body: + - type: markdown + attributes: + value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu. + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues + required: true + - type: input + attributes: + label: Affected Build(s) + description: List the affected build(s) that this issue applies to. + placeholder: Mainline 1234 / Early Access 1234 + validations: + required: true + - type: textarea + id: issue-desc + attributes: + label: Description of Issue + description: A brief description of the issue encountered along with any images and/or videos. + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: Expected Behavior + description: A brief description of how it is expected to work along with any images and/or videos. + validations: + required: true + - type: textarea + id: reproduction-steps + attributes: + label: Reproduction Steps + description: A brief explanation of how to reproduce this issue. If possible, provide a save file to aid in reproducing the issue. + validations: + required: true + - type: textarea + id: log + attributes: + label: Log File + description: A log file will help our developers to better diagnose and fix the issue. + validations: + required: true + - type: textarea + id: system-config + attributes: + label: System Configuration + placeholder: | + CPU: Intel i5-10400 / AMD Ryzen 5 3600 + GPU/Driver: NVIDIA GeForce GTX 1060 (Driver 512.95) + RAM: 16GB DDR4-3200 + OS: Windows 11 22H2 (Build 22621.819) + value: | + CPU: + GPU/Driver: + RAM: + OS: + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..766da2c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,28 @@ +name: Feature Request +description: File a feature request +labels: "request" +body: + - type: markdown + attributes: + value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make yuzu better. + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the feature you are requesting. + options: + - label: I have searched the existing issues + required: true + - type: textarea + id: what-feature + attributes: + label: What feature are you suggesting? + description: A brief description of the requested feature. + validations: + required: true + - type: textarea + id: why-feature + attributes: + label: Why would this feature be useful? + description: A brief description of why this feature would make yuzu better. + validations: + required: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa58248..25ef1f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,11 +19,11 @@ jobs: - uses: actions/checkout@v3 with: submodules: recursive - fetch-depth: 0 + fetch-depth: 0 - name: Update Translation run: ./.ci/scripts/transifex/docker.sh env: - TRANSIFEX_API_TOKEN: ${{ secrets.TRANSIFEX_API_TOKEN }} + TX_TOKEN: ${{ secrets.TRANSIFEX_API_TOKEN }} reuse: runs-on: ubuntu-latest diff --git a/.reuse/dep5 b/.reuse/dep5 index 9a90f9e..3810f2c 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -132,10 +132,6 @@ Files: vcpkg.json Copyright: 2022 yuzu Emulator Project License: GPL-3.0-or-later -Files: .github/ISSUE_TEMPLATE/config.yml -Copyright: 2020 tgsm -License: GPL-2.0-or-later - -Files: .github/ISSUE_TEMPLATE/bug-report-feature-request.md -Copyright: 2016 MerryMage +Files: .github/ISSUE_TEMPLATE/* +Copyright: 2022 yuzu Emulator Project License: GPL-2.0-or-later diff --git a/CMakeLists.txt b/CMakeLists.txt index 20dd138..47eddf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,11 @@ # SPDX-FileCopyrightText: 2018 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.22) + +# Dynarmic has cmake_minimum_required(3.12) and we may want to override +# some of its variables, which is only possible in 3.13+ +set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules") @@ -18,32 +22,39 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON # On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF) +option(ENABLE_OPENGL "Enable OpenGL" ON) +mark_as_advanced(FORCE ENABLE_OPENGL) option(ENABLE_QT "Enable the Qt frontend" ON) +option(ENABLE_QT6 "Allow usage of Qt6 to be attempted" OFF) +set(QT6_LOCATION "" CACHE PATH "Additional Location to search for Qt6 libraries like C:/Qt/6.3.1/msvc2019_64/") + option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF) CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF) option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) -option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF) - option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}") +option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF) + option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF) option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) -option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON) - option(YUZU_TESTS "Compile tests" ON) +option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON) + CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF) option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}") option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ON) +CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF) + if (YUZU_USE_BUNDLED_VCPKG) if (YUZU_TESTS) list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests") @@ -59,6 +70,21 @@ elseif(NOT "$ENV{VCPKG_TOOLCHAIN_FILE}" STREQUAL "") include("$ENV{VCPKG_TOOLCHAIN_FILE}") endif() +if (YUZU_USE_PRECOMPILED_HEADERS) + if (MSVC AND CCACHE) + # buildcache does not properly cache PCH files, leading to compilation errors. + # See https://github.com/mbitsnbites/buildcache/discussions/230 + message(WARNING "buildcache does not properly support Precompiled Headers. Disabling PCH") + set(DYNARMIC_USE_PRECOMPILED_HEADERS OFF CACHE BOOL "" FORCE) + set(YUZU_USE_PRECOMPILED_HEADERS OFF CACHE BOOL "" FORCE) + endif() +endif() +if (YUZU_USE_PRECOMPILED_HEADERS) + message(STATUS "Using Precompiled Headers.") + set(CMAKE_PCH_INSTANTIATE_TEMPLATES ON) +endif() + + # Default to a Release build get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) @@ -133,13 +159,13 @@ if (NOT ENABLE_GENERIC) if (MSVC) detect_architecture("_M_AMD64" x86_64) detect_architecture("_M_IX86" x86) - detect_architecture("_M_ARM" ARM) - detect_architecture("_M_ARM64" ARM64) + detect_architecture("_M_ARM" arm) + detect_architecture("_M_ARM64" arm64) else() detect_architecture("__x86_64__" x86_64) detect_architecture("__i386__" x86) - detect_architecture("__arm__" ARM) - detect_architecture("__aarch64__" ARM64) + detect_architecture("__arm__" arm) + detect_architecture("__aarch64__" arm64) endif() endif() @@ -175,24 +201,40 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) # System imported libraries # ======================================================================= -find_package(fmt 8.0.1 REQUIRED CONFIG) -find_package(nlohmann_json 3.8 REQUIRED CONFIG) +find_package(enet 1.3) +find_package(fmt 9 REQUIRED) +find_package(inih) +find_package(libusb 1.0.24) +find_package(lz4 REQUIRED) +find_package(nlohmann_json 3.8 REQUIRED) +find_package(Opus 1.3) +find_package(Vulkan 1.3.213) find_package(ZLIB 1.2 REQUIRED) +find_package(zstd 1.5 REQUIRED) -# Search for config-only package first (for vcpkg), then try non-config -find_package(zstd 1.5 CONFIG) -if (NOT zstd_FOUND) - find_package(zstd 1.5 REQUIRED) +if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) + find_package(xbyak 6) endif() -# lz4 1.8 is required, but vcpkg's lz4-config.cmake does not have version info -find_package(lz4 CONFIG) -if (NOT lz4_FOUND) - find_package(lz4 1.8 REQUIRED) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) + find_package(dynarmic 6.4.0) +endif() + +if (ENABLE_CUBEB) + find_package(cubeb) +endif() + +if (USE_DISCORD_PRESENCE) + find_package(DiscordRPC) +endif() + +if (ENABLE_WEB_SERVICE) + find_package(cpp-jwt 1.4) + find_package(httplib 0.11) endif() if (YUZU_TESTS) - find_package(Catch2 2.13.7 REQUIRED CONFIG) + find_package(Catch2 2.13.7 REQUIRED) endif() find_package(Boost 1.73.0 COMPONENTS context) @@ -213,128 +255,166 @@ if (MINGW) find_library(MSWSOCK_LIBRARY mswsock REQUIRED) endif() +# Please consider this as a stub +if(ENABLE_QT6 AND Qt6_LOCATION) + list(APPEND CMAKE_PREFIX_PATH "${Qt6_LOCATION}") +endif() + +function(set_yuzu_qt_components) + # Best practice is to ask for all components at once, so they are from the same version + set(YUZU_QT_COMPONENTS2 Core Widgets Concurrent) + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + list(APPEND YUZU_QT_COMPONENTS2 DBus) + endif() + if (YUZU_USE_QT_MULTIMEDIA) + list(APPEND YUZU_QT_COMPONENTS2 Multimedia) + endif() + if (YUZU_USE_QT_WEB_ENGINE) + list(APPEND YUZU_QT_COMPONENTS2 WebEngineCore WebEngineWidgets) + endif() + if (ENABLE_QT_TRANSLATION) + list(APPEND YUZU_QT_COMPONENTS2 LinguistTools) + endif() + set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE) +endfunction(set_yuzu_qt_components) + # Qt5 requires that we find components, so it doesn't fit our pretty little find package function if(ENABLE_QT) set(QT_VERSION 5.15) + # These are used to specify minimum versions + set(QT5_VERSION 5.15) + set(QT6_VERSION 6.3.1) - # Check for system Qt on Linux, fallback to bundled Qt - if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - if (NOT YUZU_USE_BUNDLED_QT) - find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia) - endif() - if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT) - # Check for dependencies, then enable bundled Qt download - - # Check that the system GLIBCXX version is compatible - find_program(OBJDUMP objdump) - if ("${OBJDUMP}" STREQUAL "OBJDUMP-NOTFOUND") - message(FATAL_ERROR "Required program `objdump` not found.") - endif() - find_library(LIBSTDCXX libstdc++.so.6) - execute_process( - COMMAND - ${OBJDUMP} -T ${LIBSTDCXX} - COMMAND - grep GLIBCXX_3.4.28 - COMMAND - sed "s/[0-9a-f]*.* //" - COMMAND - sed "s/ .*//" - COMMAND - sort -u - OUTPUT_VARIABLE - GLIBCXX_MET - ) - if (NOT GLIBCXX_MET) - message(FATAL_ERROR "Qt too old or not found, and bundled Qt package is not \ - compatible with this system. Either install Qt ${QT_VERSION}, or provide the path \ - to Qt by setting the variable Qt5_ROOT.") - endif() - - # Check for headers - Include(FindPkgConfig REQUIRED) - pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0) - if (NOT QT_DEP_GLU_FOUND) - message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \ - Perhaps `libglu1-mesa-dev` needs to be installed?") - endif() - pkg_check_modules(QT_DEP_MESA QUIET dri>=20.0.8) - if (NOT QT_DEP_MESA_FOUND) - message(FATAL_ERROR "Qt bundled pacakge dependency `dri` not found. \ - Perhaps `mesa-common-dev` needs to be installed?") - endif() - - # Check for X libraries - set(BUNDLED_QT_REQUIREMENTS - libxcb-icccm.so.4 - libxcb-image.so.0 - libxcb-keysyms.so.1 - libxcb-randr.so.0 - libxcb-render-util.so.0 - libxcb-render.so.0 - libxcb-shape.so.0 - libxcb-shm.so.0 - libxcb-sync.so.1 - libxcb-xfixes.so.0 - libxcb-xinerama.so.0 - libxcb-xkb.so.1 - libxcb.so.1 - libxkbcommon-x11.so.0 - libxkbcommon.so.0 - ) - set(UNRESOLVED_QT_DEPS "") - foreach (REQUIREMENT ${BUNDLED_QT_REQUIREMENTS}) - find_library(BUNDLED_QT_${REQUIREMENT} ${REQUIREMENT}) - if ("${BUNDLED_QT_${REQUIREMENT}}" STREQUAL "BUNDLED_QT_${REQUIREMENT}-NOTFOUND") - set(UNRESOLVED_QT_DEPS ${UNRESOLVED_QT_DEPS} ${REQUIREMENT}) - endif() - unset(BUNDLED_QT_${REQUIREMENT}) - endforeach() - unset(BUNDLED_QT_REQUIREMENTS) - - if (NOT "${UNRESOLVED_QT_DEPS}" STREQUAL "") - message(FATAL_ERROR "Bundled Qt package missing required dependencies: ${UNRESOLVED_QT_DEPS}") - endif() - - set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE) - endif() - if (YUZU_USE_BUNDLED_QT) - # Binary package currently does not support Qt webengine, so make sure it's disabled - set(YUZU_USE_QT_WEB_ENGINE OFF CACHE BOOL "Use Qt Webengine" FORCE) - endif() + set_yuzu_qt_components() + if (ENABLE_QT6) + find_package(Qt6 ${QT6_VERSION} COMPONENTS ${YUZU_QT_COMPONENTS}) endif() - - set(YUZU_QT_NO_CMAKE_SYSTEM_PATH) - - if(YUZU_USE_BUNDLED_QT) - if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64) - set(QT_BUILD qt-5.15.2-msvc2019_64) - elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND NOT MINGW AND ARCHITECTURE_x86_64) - set(QT_BUILD qt5_5_15_2) - else() - message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.") - endif() - - if (DEFINED QT_BUILD) - download_bundled_external("qt/" ${QT_BUILD} QT_PREFIX) - endif() - - set(QT_PREFIX_HINT HINTS "${QT_PREFIX}") - - set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH") - endif() - if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT) - find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) + if (Qt6_FOUND) + message(STATUS "yuzu/CMakeLists.txt: Qt6Widgets_VERSION ${Qt6Widgets_VERSION}, setting QT_VERSION") + set(QT_VERSION ${Qt6Widgets_VERSION}) + set(QT_MAJOR_VERSION 6) + # Qt6 sets cxx_std_17 and we need to undo that + set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "") else() - find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) - endif() - if (YUZU_USE_QT_WEB_ENGINE) - find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets) + message(STATUS "yuzu/CMakeLists.txt: Qt6 not found/not selected, trying for Qt5") + # When Qt6 partially found, need this set to use Qt5 when not specifying version + set(QT_DEFAULT_MAJOR_VERSION 5) + set(QT_MAJOR_VERSION 5) + + set(YUZU_USE_QT_MULTIMEDIA ON) + # Check for system Qt on Linux, fallback to bundled Qt + if (UNIX AND NOT APPLE) + if (NOT YUZU_USE_BUNDLED_QT) + find_package(Qt5 ${QT5_VERSION} COMPONENTS Widgets DBus Multimedia) + endif() + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)) + # Check for dependencies, then enable bundled Qt download + + # Check that the system GLIBCXX version is compatible + find_program(OBJDUMP objdump) + if (NOT OBJDUMP) + message(FATAL_ERROR "Required program `objdump` not found.") + endif() + find_library(LIBSTDCXX libstdc++.so.6) + execute_process( + COMMAND + ${OBJDUMP} -T ${LIBSTDCXX} + COMMAND + grep GLIBCXX_3.4.28 + COMMAND + sed "s/[0-9a-f]*.* //" + COMMAND + sed "s/ .*//" + COMMAND + sort -u + OUTPUT_VARIABLE + GLIBCXX_MET + ) + if (NOT GLIBCXX_MET) + message(FATAL_ERROR "Qt too old or not found, and bundled Qt package is not \ + compatible with this system. Either install Qt ${QT_VERSION}, or provide the path \ + to Qt by setting the variable Qt5_ROOT.") + endif() + + # Check for headers + find_package(PkgConfig REQUIRED) + pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0) + if (NOT QT_DEP_GLU_FOUND) + message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \ + Perhaps `libglu1-mesa-dev` needs to be installed?") + endif() + pkg_check_modules(QT_DEP_MESA QUIET dri>=20.0.8) + if (NOT QT_DEP_MESA_FOUND) + message(FATAL_ERROR "Qt bundled pacakge dependency `dri` not found. \ + Perhaps `mesa-common-dev` needs to be installed?") + endif() + + # Check for X libraries + set(BUNDLED_QT_REQUIREMENTS + libxcb-icccm.so.4 + libxcb-image.so.0 + libxcb-keysyms.so.1 + libxcb-randr.so.0 + libxcb-render-util.so.0 + libxcb-render.so.0 + libxcb-shape.so.0 + libxcb-shm.so.0 + libxcb-sync.so.1 + libxcb-xfixes.so.0 + libxcb-xinerama.so.0 + libxcb-xkb.so.1 + libxcb.so.1 + libxkbcommon-x11.so.0 + libxkbcommon.so.0 + ) + set(UNRESOLVED_QT_DEPS "") + foreach (REQUIREMENT ${BUNDLED_QT_REQUIREMENTS}) + find_library(BUNDLED_QT_${REQUIREMENT} ${REQUIREMENT}) + if (NOT BUNDLED_QT_${REQUIREMENT}) + set(UNRESOLVED_QT_DEPS ${UNRESOLVED_QT_DEPS} ${REQUIREMENT}) + endif() + unset(BUNDLED_QT_${REQUIREMENT}) + endforeach() + unset(BUNDLED_QT_REQUIREMENTS) + + if (NOT "${UNRESOLVED_QT_DEPS}" STREQUAL "") + message(FATAL_ERROR "Bundled Qt package missing required dependencies: ${UNRESOLVED_QT_DEPS}") + endif() + + set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE) + endif() + if (YUZU_USE_BUNDLED_QT) + # Binary package currently does not support Qt webengine, so make sure it's disabled + set(YUZU_USE_QT_WEB_ENGINE OFF CACHE BOOL "Use Qt Webengine" FORCE) + endif() + endif() + + set(YUZU_QT_NO_CMAKE_SYSTEM_PATH) + + if(YUZU_USE_BUNDLED_QT) + if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64) + set(QT_BUILD qt-5.15.2-msvc2019_64) + elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND NOT MINGW AND ARCHITECTURE_x86_64) + set(QT_BUILD qt5_5_15_2) + else() + message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.") + endif() + + if (DEFINED QT_BUILD) + download_bundled_external("qt/" ${QT_BUILD} QT_PREFIX) + endif() + + set(QT_PREFIX_HINT HINTS "${QT_PREFIX}") + + set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH") + # Binary package for Qt5 has Qt Multimedia + set(YUZU_USE_QT_MULTIMEDIA ON CACHE BOOL "Use Qt Multimedia" FORCE) + endif() + + set_yuzu_qt_components() + find_package(Qt5 ${QT5_VERSION} COMPONENTS ${YUZU_QT_COMPONENTS} ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) endif() - if (ENABLE_QT_TRANSLATION) - find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) - endif() endif() # find SDL2 exports a bunch of variables that are needed, so its easier to do this outside of the yuzu_find_package @@ -356,23 +436,13 @@ if (ENABLE_SDL2) set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library") set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll") - add_library(SDL2 INTERFACE) - target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARY}") - target_include_directories(SDL2 INTERFACE "${SDL2_INCLUDE_DIR}") + add_library(SDL2::SDL2 INTERFACE IMPORTED) + target_link_libraries(SDL2::SDL2 INTERFACE "${SDL2_LIBRARY}") + target_include_directories(SDL2::SDL2 INTERFACE "${SDL2_INCLUDE_DIR}") elseif (YUZU_USE_EXTERNAL_SDL2) message(STATUS "Using SDL2 from externals.") else() find_package(SDL2 2.0.18 REQUIRED) - - # Some installations don't set SDL2_LIBRARIES - if("${SDL2_LIBRARIES}" STREQUAL "") - message(WARNING "SDL2_LIBRARIES wasn't set, manually setting to SDL2::SDL2") - set(SDL2_LIBRARIES "SDL2::SDL2") - endif() - - include_directories(SYSTEM ${SDL2_INCLUDE_DIRS}) - add_library(SDL2 INTERFACE) - target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}") endif() endif() @@ -384,25 +454,6 @@ if (TARGET Boost::boost) add_library(boost ALIAS Boost::boost) endif() -# Ensure libusb is properly configured (based on dolphin libusb include) -if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB) - include(FindPkgConfig) - if (PKG_CONFIG_FOUND AND NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD") - pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24) - else() - find_package(LibUSB) - endif() - - if (LIBUSB_FOUND) - add_library(usb INTERFACE) - target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}") - target_link_libraries(usb INTERFACE "${LIBUSB_LIBRARIES}") - else() - message(WARNING "libusb not found, falling back to externals") - set(YUZU_USE_BUNDLED_LIBUSB ON) - endif() -endif() - # List of all FFmpeg components required set(FFmpeg_COMPONENTS avcodec @@ -410,27 +461,12 @@ set(FFmpeg_COMPONENTS swscale) if (UNIX AND NOT APPLE) - Include(FindPkgConfig REQUIRED) + find_package(PkgConfig REQUIRED) pkg_check_modules(LIBVA libva) endif() if (NOT YUZU_USE_BUNDLED_FFMPEG) # Use system installed FFmpeg - find_package(FFmpeg 4.3 QUIET COMPONENTS ${FFmpeg_COMPONENTS}) - - if (FFmpeg_FOUND) - # Overwrite aggregate defines from FFmpeg module to avoid over-linking libraries. - # Prevents shipping too many libraries with the AppImage. - set(FFmpeg_LIBRARIES "") - set(FFmpeg_INCLUDE_DIR "") - - foreach(COMPONENT ${FFmpeg_COMPONENTS}) - set(FFmpeg_LIBRARIES ${FFmpeg_LIBRARIES} ${FFmpeg_LIBRARY_${COMPONENT}} CACHE PATH "Paths to FFmpeg libraries" FORCE) - set(FFmpeg_INCLUDE_DIR ${FFmpeg_INCLUDE_DIR} ${FFmpeg_INCLUDE_${COMPONENT}} CACHE PATH "Path to FFmpeg headers" FORCE) - endforeach() - else() - message(WARNING "FFmpeg not found or too old, falling back to externals") - set(YUZU_USE_BUNDLED_FFMPEG ON) - endif() + find_package(FFmpeg 4.3 REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS}) endif() # Prefer the -pthread flag on Linux. @@ -541,12 +577,27 @@ add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY # Adjustments for MSVC + Ninja if (MSVC AND CMAKE_GENERATOR STREQUAL "Ninja") add_compile_options( - /wd4711 # function 'function' selected for automatic inline expansion /wd4464 # relative include path contains '..' - /wd4820 # 'identifier1': '4' bytes padding added after data member 'identifier2' + /wd4711 # function 'function' selected for automatic inline expansion + /wd4820 # 'bytes' bytes padding added after construct 'member_name' ) endif() +if (YUZU_USE_FASTER_LD AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # We will assume that if the compiler is GCC, it will attempt to use ld.bfd by default. + # Try to pick a faster linker. + find_program(LLD lld) + find_program(MOLD mold) + + if (MOLD AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1") + message(NOTICE "Selecting mold as linker") + add_link_options("-fuse-ld=mold") + elseif (LLD) + message(NOTICE "Selecting lld as linker") + add_link_options("-fuse-ld=lld") + endif() +endif() + enable_testing() add_subdirectory(externals) add_subdirectory(src) diff --git a/CMakeModules/CopyYuzuQt5Deps.cmake b/CMakeModules/CopyYuzuQt5Deps.cmake index a353ddb..ab56de4 100644 --- a/CMakeModules/CopyYuzuQt5Deps.cmake +++ b/CMakeModules/CopyYuzuQt5Deps.cmake @@ -27,10 +27,13 @@ function(copy_yuzu_Qt5_deps target_dir) Qt5Core$<$:d>.* Qt5Gui$<$:d>.* Qt5Widgets$<$:d>.* - Qt5Multimedia$<$:d>.* Qt5Network$<$:d>.* ) - + if (YUZU_USE_QT_MULTIMEDIA) + windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} + Qt5Multimedia$<$:d>.* + ) + endif() if (YUZU_USE_QT_WEB_ENGINE) windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} Qt5Network$<$:d>.* diff --git a/CMakeModules/GenerateSCMRev.cmake b/CMakeModules/GenerateSCMRev.cmake index 0e4bd12..2cdb918 100644 --- a/CMakeModules/GenerateSCMRev.cmake +++ b/CMakeModules/GenerateSCMRev.cmake @@ -7,11 +7,6 @@ function(get_timestamp _var) set(${_var} "${timestamp}" PARENT_SCOPE) endfunction() -list(APPEND CMAKE_MODULE_PATH "${SRC_DIR}/externals/cmake-modules") - -# Find the package here with the known path so that the GetGit commands can find it as well -find_package(Git QUIET PATHS "${GIT_EXECUTABLE}") - # generate git/build information include(GetGitRevisionDescription) if(NOT GIT_REF_SPEC) @@ -29,6 +24,7 @@ get_timestamp(BUILD_DATE) # Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well set(REPO_NAME "") set(BUILD_VERSION "0") +set(BUILD_ID ${DISPLAY_VERSION}) if (BUILD_REPOSITORY) # regex capture the string nightly or canary into CMAKE_MATCH_1 string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY}) @@ -57,6 +53,4 @@ if (BUILD_REPOSITORY) endif() endif() -# The variable SRC_DIR must be passed into the script -# (since it uses the current build directory for all values of CMAKE_*_DIR) -configure_file("${SRC_DIR}/src/common/scm_rev.cpp.in" "scm_rev.cpp" @ONLY) +configure_file(scm_rev.cpp.in scm_rev.cpp @ONLY) diff --git a/dist/icons/controller/applet_pro_controller_dark_disabled.png b/dist/icons/controller/applet_pro_controller_dark_disabled.png index 416e1e2fb0d8c62d9fc767df45b0aa899d6869c5..d45f91db55a020385d07450ec87856710206eeb2 100644 GIT binary patch delta 2705 zcmV;C3U2lNBA6ABBYz5CNklqp5bbB%wr!i&m^7w4vu~emTUhDYwr$(C zZQH7oN$&Q|COw(#&Yk;X&iC~qok^;ztE)~`_bzLofd=Y^|Ni@Lf{u=kS*=SAbar;m z+}74{duwa^E8X4QQ-qfrIO2#SCU0wNe~H?EZEbBQSX~XYw10H1K#KW;+P|%>orA2d z2HM-(4}#TD`{cOej+?2sr?I3aB&FOB<&eL**6M12BGUq-lpjLnG>zTcSW?a9CDrj$ zOH0ebT=_#hsYe%!#hF=+F=|xPOk5ewrN1v%dOLbA7ea5qdDC2MT!V8yqbL3Cxioi> zKwlQLUWB646@Q_>d3dX>wwjarz8A{>$5Z+qF7-+D`!VJ3{uyYVGM5VJUl}xEuBHC_ zNTK&f4cDT6+#7vPNOOu%<~aJ^Nx$DhpZnF5=12PehbR1B==u-z^LMyT{XcTy-zJ3F zqEsqPRlC_Km1gyXKAB$ni${&V|3~c~YOf!7;DNJ{Qhz=Sm(&45_#^51ZzSCBTSa%6pd6q|OviwU(mU_aabAR9P;lrn?mK9UP8qCHc3>!9VN-p$W z@M}VtQ*!zhzd%a)z*A-;Rwc+<4ji9ZG3TWXxiBAMjI0_E2bqmN_yy;BrGa9_T>2*! zgx-_o1WccN#@fS&ZON@6xu5>fCl8b`dta#U5 zcbzN`j{#`*70LB5&G4>C{Yt5M`jR|-ynovnZ#N~z8ZJ*ac+uaS3w@7!sriS}1OK#y zAfBUlOpa15GuW({)exRA`+;YLGQ9}@VN(y50Hh$9mbWMLQE2JrXZ#eZP9acl#u5-` z#brT@Ym7E#?uz7+nBagX@rGTvEM4J`UEWOW#c|CoC{9}UzQJJ4X|Ls0x(y{mc znzo!hWzLD+`#C9VqTVHH237Pk&@$GX)_MkqVtz?ijQ@5PRtzP67LYYWGq9Qn87C2Y+K0!ffSm zcILEl#L>8MKZn&EN$oFtjU$X^WyNGI_tl_Q48PQB$@H0&O^a42r|b!(U_2;-xk%KL zhy>IQr5pc5wa&p~7J(ClrUNt14utUeBUVfnv;`XgAi_npm#7J;FK|^NQQ$%ZMx>(q zA7e>?$x`RfQI(4e^AQLY%YO-&tR?7gb|e6x5W7~)`UxpZOD6<_8&j=?6u~JnM#~Lj zPg=GNl`S~dF{MoDFUL{0m7230fWx$fgx7bm;w!Jbaw2EZTl%NcbKa!(Dz$b@U|+vu z9*^ZpqtjS43RhCfrLEQS8s}!2M8_L9Jt6d2C}*OySOdjmV#TO6(|_kbPH^_GKsgEN z%wb3lQ^+P1IGvzqSCIPDm>anSDNd4UortVDflqu)A9x~TJ~ILi5G|VrB2SPch($_*UaaJ6Y>s!aOUSSy#WZwdO zMID|P)qH;r?2{#2G+!|XAcTwf0a&BaMj0$HHi+w^K5WNiWW`YS6={kIOg|$)R)ULDlh{)0|gH>~3KJ_T6bdgWM&YmGN@v0)L08Bbc zoz?|?t|$F%GQQ{(Xx~(*rAXpImSX<42%ZyNU0w5#QobDsR*c$=M+qiZ`s{2wBW7FV zRkjH|jxqqt2!9cfEd7aV^DRp0QNqYlI;;UJo-pJ;FITneiqydN{8>%2mqP2GxH8wX z8i5pZxtkTcL9*RU?PuC{nU-ZlSnd0!_ZM<*tV#%D*23BqN*~T@fS#V7=_6K*QU$95 zNDRxAzj<|5C14~-L4x9xWjLGP=C?N-1& z9O-$M**T*p*EEdd%GIIEh%X2orvf)zU}bHaRqLRl`w z6iXXoi@KZxVN)vya6m9%iZz0p6}t~5;KV~eDvvKmS@q00&gHF(#LU|ItT}8}i~|EM zzX#bMBdZe71#|+%{E4-h!%01e?OHaDfg>eZSbs5O)=BVNplFks<%7 z8!HZLz^_8qYLD&0N3q~kBiE}|OM-;(1qKG& zp?~%r4ffS6T7_kS_k^3`b;pS(o;X>=igEZl?Tjx1MHX6t2?u={Gd^gDE_{eGqI0J& zILV2Ecn;K&72{YOh$uLUt5^;l^r=Tfqc|b~=hN527uO3;3n(L5z2UAEf06dry-0gd zxB|CHDNwyDl-`Y{e0XFz88nZg{)xuJJ%3*7lvPgDpB3YH2#$@|#>(!GWZHd-m-1A34^tOGJiR>^4z$ zfgA=M1||zYMTZ&{F951u6^0|ynTuF`7m+A{6^)9@k@jRYif|YuOOc77mn%dCvQ;H& zM5788$Oeg`JRxX9cD7kxU0t0K=jwA|W|x$V_O!(A5|JCV-Ec7NN$flC+;hHk#u>x^ z0-UW%`S$HE?pD!y5J7EL*FpD$s%m$!h)Vj@G@g1)5UcI?JS2N(Dn}6U2>P?^EVHDq z1?p2_{QjcL71@Z$Akb_D71fxEcu-=9=Fj-t)Z-wJ0#R$trO!YAoF{7RKYgn2`Fy@? zfad1r6O$%QdOI8r+rGq&qqMa2j94r-96(h)t=4{0+hiz4#;Mq`Mj5Tb7*QsGi~({L z460E?Jb<8_06B`wfAGn;Zbe1ay`Z-M zFZ+Dn>8FzwJI>s3&^90AxdzoKA{YgO@Sah*JQAtdp3XjPVI0TF!jttn%1A|Hf-G|* zwRh8ApdJW^!-H~i20sH#0sToR6uQ0tLmNSa_CI`43dDUEf&uM5ci+yX>+*`+UBcz!VX4{$r1Ai8z7c zdnZnse8I~*cRZD;KY&@YW@S&CHsj*+&bz2-&z|R6lj+=0RP>3-c~e(Snlxp8-sGv? zT{~ZVA(`H;T`%sRGI{Fbiq=oPaB}raFTK=brAqoJ#Z-z!bd&4W{iQ#H2#3Sg7`aiE z_e7YzVnyX#5#4P>R_6zTSN6we?$F_nV9bAI=d?T(4u^Z>U0PZ?$P+VLL2p91O3(-L z14Y49{vwf@?IOCV&6>hw@}z}Pm1|VY!~GdR9E%n$+HVZE1Bl4|Qzl*Tdtj?k&h3{& zk?9@3F4(tkUv8rKNONo|=%>4xAUCA-sk%wv>SUrGg$V|O9|NWnYxzSupL2?e&UFfk zN}YnDlJ=#sC(6po)`2#H=4NGOuN^ab#8r8D=dVedY^pJ!Hv>;7e6O~)_Ps>8kG6yK zSQzv`I-e&_4BZOMxc1s_4o#HxC`_xBOHh5*b={O4#9vT+r-{jKRMw*MASSlg2^4)d znXa|=JHP?p(sAR?UXUrdi(`GHuECAeUba3`dv7v%Lqo#>Rmy=_$E_V+qs!xR$8oX} zZ60}K&2dDY%gSlFI8oN4FdDO$D5QMh`u)LTRb2}By4)TNa=+hS^tD9&^78T~(3=1i zVd?VaD`&JPT6pmd!Ql8*aiE=CI!-Vz@qRcQHvT|xrBe`m#|aee$qy7=2e7`ewi4?b zZ;hRL=&jd3S6^TMmg6|1oIvnVOxAHHQ2a`MLGhQm4_NB^o5x@bEX%O z&-=taoho7AQ>jIgQr6sjybRa@Ol)n9R*1EQA{PPu(!mYDB9?!?)7tt2qlfXqYXU_C?Rq9rl(WA$GL6xB`Eu5JuVc^x&BHf=#(v=m9 zaLbGt(}p)jYOhG8FNO+`W`QFb(_bXSc_!hy#VfI-oJHVI33vY5(9rPr_*HiHiP1pr zm@(tK6}wwu)aV5WG2@Z{OqDRAsdfewBb6!bXzvw+h5=FKm+5-NjrBG4*p?Az3>$K0 z4?bnv)_~pz90C4U)C3zE8V&%=DJ;Cu?=Sks_1Bl40id?^PLN*%2Z3$Y#IB>gZ_(}w z?`DOVwR(QHVRpU9F9`n3AO}_LU83^C#>U#D19bwyyMa4^`1CuW5K6UTbVWEEHiHKb z-m1z>RW>eOT$Y||WPp5s@d^wAlJ(`E;zlw5`M`EApOHrVm6~|(+PF7q|Q8BMCcD><- zU~s&MmUX!`Mv49~mA>ORBUDTUs>@a6F@?`{m3PjV`Sa)dnhqap8kA+us;;g+=Jlv3Cyqd%WU|HX=zdoo zaU(U?Ce!x^f=?=pbtAQ(bQ~uallA8CVMB(mS+k}My3ClSrlXU9*W#_esL0ibT$~>$ za{7~~iOL{O7Jks7Lx;LOwxLkyfjBER#>JxpE`lyUMCRT`?-!Bm%)w!g>*8eLiO-~RUMqGUR=OG-w2vtloReit|&msWK^qrew>8)$x@=sMI~44_8O5j`n8bM&}X`TpV)?K0W8p`rTe^78Vgii(PxKpyR2 z#aqkEfABwDeR^B%N+8*{Sl)>iy%SCn&W>-WrD>CttgVXvV?(6w-@4rveGZkW@$1eR zqaqhtK zr!JUNP;xWySsT^M6J=KAMTIHx>ywX?>Lj1oml6#kk;qVN2?Gq|K)*V(Qrj>~3sqxI3Brp_-aE zp#7Z&9fk44n2c(V$EYg?Wtqzn&hZC=KS`zIHq@;9N%TVB5KIaQXBz>2Mhg$T)d#;>Yv4X4XFj(mUcKG5-lvdzDw*VLW9me_?5pNf_# z2s_f5R99EOZPgY5H|GZe*Yzc#OwrtYyi$a1z{fov@2>zJs;&JyG0#*%8i03D-caF} z*w*Q;>mKgSXMP|!%Lw1KMoX)!tKUwiE3m_gOoonCNwAYGsap+kY-p_CkRJ&CT<|mo zgTYVN*4F+#m98-+YEOQW#72!8rKxn|2#3RV)v8rEJk}W0cm7Dy-d5Utiy=fwDtHz5pFFMv*+Rv7LPhwmwpOvmk$Mjq1~+dHF>mH7~Ta z_=fvDI-=!7_VBK(Hysu(TzCMqV+Az#EnmL;yuR2afx)yrE66KuL*2LfVqlHz1fA4w z+>Qwl-P6~IYFjS?KIRkzdl0Cuc=+LU|E#O4YudEwH%I#F8{({(NMBfSDmVqfO$cWk zeec~%`f4MwmVLkk0OL5$CwJIFSP}WTzaaQUZ6{-6>F2@f?$F(#Q0UebD=P03;nogTJpIWhpUlN%)g>s$1U-m| zqLdsM4^AI5XOSC9!-HRBl*hiF@+k~IhAN{rqkme)ABh0$+_$YE$_#W^{H zX981NTcZ_2h72h?e!R6JLEWQd2m&~<0|_=CwNsk*I<6z?-3YZCCdIo zZ@)ex-kBdrgu~(H<;z#iGbZ+w2)E?r<^8U#tSo8kb^iSMYnu)qduY^IcKu7&fX(abJwW;jn4njFY?id(LH`5DJC1fZhvujj<1e!{Nb+ zI^l5GD!N3)EKPPiEd`3L@Q|7Gne(;+Ien?>uHAcYN93^EQ1eKJ<2WVA$;s*L73bs( zNxD3*kJN1fj=c2pzT0~9=M)rQ3!1&Yq4p0Z91dF*ZdEb&r#r|K2$W1#D~pNAjda-* zKb^Skw%b}xo-5XyQ=BVS>(Np{?np=a3JMC&M)f{Xy#-JMu)d*otHNqeRJH{I!7nBn zIZokMEcQ_hx4CY0%A+2q0|~w2wN+JBqZ4&D)Ybh>l-rFpk0#?b`31$7#MJY+h^~zH zqq}XJ`u)Y%D|#=61EM?&pfCd%i;?fR^)iu?@W z_0I7LpGO&w$SrO|&8E0+kCWk!<9P69Umm P00000NkvXXu0mjfJ-OS{ diff --git a/dist/icons/controller/applet_pro_controller_disabled.png b/dist/icons/controller/applet_pro_controller_disabled.png index 72a549ea98d988c7e10d82bd85126f163b409ed1..8c6bcd3083e8bb67cf18262ab161b64df79ace4b 100644 GIT binary patch delta 2622 zcmV-E3c>ZwAjTAsBYz4FNkl_G4yd zW@ct)W~Rd#N9l8YwxhVor9V9H?UfXJcePs0u5=gF-(V1#fZu`a0#DCr>`vm^)W=|A35uJY+eZYZkq{X?wl7qCAg+&s|>=jJYjm zdyfW%zw7_I?SEdQJ_m98@GKh+=)(K&!q0!_Z23FB{)cgY;p=ZX$Nk0P?;ZDde7y(n z-K(>+b28B?6bkN4RE)|%DJn6_H{NzXUTm?&X2y`um2*z6&Z5LP_g^SDl4Ik<^8X$d z$-mVVtLP2K=l_TE|Ho%WM2(??;OxKX%%(dCMy`vQ`F{;B|F8NokID~IMi0YFcfzm@ z)b{;9WQCmFyNY4=ENnR5oR-xy52d4{Z)U?V#%(xC4LNn~8Ah=@ztt(%N91gScOQ<4 zDPW>5fbn-AI^JW{&`D<1F~b5A!^!+U=Tw#w%|PDqR>cGVtVIaIle6>+W~mHktr8mE zqoU^Rc7L%h-GAk|=y4S&^e@+`4^M7n-=Zp+Dcz3yGv0~G9FGNl{R-^<& zANg+eq{z+Zd*`woyr<~hiKLrIIB6SgMP;650k+*lNC=i(L@VkHlKpRho*Ck`*Uq)G>poW zV}DMjyHW&)xk)5|3>{-06guEb$psAKyh#(s73I80%6o%5?2$ylgRFS(z4xB@`s=SB zFO_2)Z)fQBBxu4B(aWfN4mvFqF|J4eZKSPQ8waKL+968(Q8yDqp(V$Ar}LbEahjK7Z#^K+10UL&*w z3$WMFS<4SGY7+kbW_HZr!vCgaE2{cW6@<<-U11Zc}qWH(z9ZO_bzze zKgb)uh4~coS?JyW(%6{Dli|r@iGR@ltdhGBZKmz<%GmCHs#^XaevG$AT#jK9Ya%OV zAvg)^H4y@r0^ZkCU25{vAcJLo;qqW4R3oBq@+#?bzk)oHbIOZ_Ci)`?i_CqK^2<3^ zi93!Qmbc}J@!8)s`LN&`=Qi2^D}JOEy<#}X3F!2VVRtWBrxrYhb|`!Ll7AfYJa+=K zB>nBUjBZ`*Lp!_$m;~wRKp5*{?f@&^OEdx8;TW}{D84MIElL}?19PW>Q=f@#F>nW9 zmrFV63k-fzqy$4Ypy)pl#BQ%(uUHHcY_UeeZb!PV*$or7DF<~7vCL-@VJ8-9x+lmOBnU}lJwqU-& z&+p*pCE^nAWuaNP0Ez2g6lhboz%g2^SXKI8MkM>U8Qo))Gmd*Ei^Djw={V<2WnmX% zP6_4Bt61VhI7U{Oj3PqrOYB6?X(d0$+lmR0f@fVFGQ_L>jRjuG zle6#?A?lYu2qKB`I)C2#$z$Z+wSzK7;6Nkft+l`Lv{087BkZq>bqqsa6C`q`og86w z40D>OfsD0YtD&>=oEc%%|I9_HlW%>T9A?GjCo#+E6UT9;5*mQcz0mzXR@!wO^9Z7T z{a`EeST=R)Gs|u^$VZq?d8^cp(PzcC2nANoJgQ|>L%y~g_kVmI73*yXJ)8JC7c0AH zIIuFH)rl8fj8NZSGbH=y{ZgG?3Qzn3jv3xS=Z-A;=Vq{Co_um^X04iQ6E2eIWy}#MWsVhr^0hwPnG>N{xzRBU;KbTNO>u_x{ zD^7-ggkrmYR(~pNoa=!(2%q_>{|6?w#`~*1tNClUB+nA9!PEg(d`g3LnrT8eUMxs+ z8o?5TiFY+Wd{Us$A@`+oET?jSXaNGOco#oS$~6MHRbZ;$ba z+OL6lsDG7mJ>F$G-ch3Uh-SsfmlEzAMCb8Mzp74i^)&CeYqBt7juEX!G%MaaDeqA? z>L?lneLTKK9gM#ncVC=7}`d>*$kI5yPeq&x|$dpSH1L)dPN; zx~91_MaR6HFO^qpm;^cAjzR)jb-c>R zBJT+tinkjR1z2%U+#O$T6gqe9IN_u({r1sA_{Ha}L+YILeaCC-A_dyWiq*9^_j}qP zh=2I2YM+nMkETa?MZ&L9LLf8dZ2|Rv2HJ~cR{R}g#)Lb3Uy|EI{Xy|Iggz@a9k~f@;jUVb gLfTipWXOf#lc7*Vsnm+>L4zVwX6$V&~ADo8{ ztt*Ege-7-#56;zx*p~x}8~_`0fR(_fz*ie0@#%o^3KP${H{SU10kEHyNMyyhgz>&5 zs*@GjspxjBeC(*VamkVefBZbQ2OSu){4k_t9*)5Bw`EvPGwBeoYQs^sIEAReL?1Xb&Rm<&R@vEr!cts??GwfP1p zufQ}!Tfd>o!wADb3K3D%dWbkEp2AhNRb|_N$AJWJR()hez@pnRc8}9z9DrcZ?A^2H zPanCiYxC(hiR-o&IZj{Af*~ zfX@)~&2VMOy3ZY$##q~#R`r*_QGgXtg!Lf5QfnUwhsuAGB7QS4A9x*@4eT9E01hNA zEiIEj?KD3U;ZjvjMbv;8lwDT!e7Cy%ce(U)2BxEy)(uM?4M zcYGM|B5*3u4wMdQR0kMUmB!f3ixpcB6apU=9c4}^DJkj7rqlnOOyQ$#d!wQpt=Ol7 zPS@CQXhCg`pa66N*8ttX4Zy`i7{>ueM6{-=q}g`u7z6U82*>RB%(mpz$<|O8jsLAc ziyHZd+KSROgY=~ehya^`S->ZQ)IX>+H7!5i8s}~c<+W914`!0d7#MfivhgNSupZ>% za8>C`L+IN8PXXrw4+Gz?i^T5`^tEuPJm;OtMtggEaMvC^y+AtGRa8{$&Lq>=xZ=wi zNYtpxL_}AGE6eW5X0JZlHXHO&$1Ev0rVeLPvGxCRu3^K;4dz(1$F_K16Ld2 z4MdhVMB6VMioqS9=`XD6ue)sUdDnIO`R%$Zhgp+Yqsn!lm!RBT7j6APHhccqWVI)H zWv2GbbBvMiis~an89)l}vVZcurO~Z;M1+b8la%}(jF1Lv8J;8)(qlffOYg{#Xu~Vv!93h zNZVpWBY*?4-m&`4z;*apaXqjG_-$jX?d*iedu~O^2if{+jJ2Jau(HDqmAsaE z-F00f!J?%Cb3i^3)kQU-ven^G=~8#;E%(sVGoGI9uZ3MV5s9=PlMwHxAQ#j};_rdI zHXK^;>wU&lp2g}}K%c<$n_~ny%V3_%<}6BOmKG6}8?&@M0j$7(!tS0hVZu=!`hc-s z)-<3#7H_huj~THqHb&bn@8fiE%ntlZsc>IoW6NX!ck#Rr-RiPeUDr(jBt&B% z7Xl8#iHLb%)v8ra%6Oj@%vtIE_X&*ax&e@{1t@qSo5T1un9QhVZ-d47wc;6Dw{DFZ z)hktHXAWDJ0HZ)gchn~qKX;&t2S1UN5U3j~?dcnTe*BLGoR2bzIj{bV8Sf9WIkm)F%4d|m;9ahOUd8!&+noX~l zmjM@l(!ZS?wc^^U@_R=Hy|EL<4LdqLpE_yEMm%_xgb@2D@4X^YQBzrZ^8kSX|7NTK&Z9Rg&O;|J>Z0+OKt=DbA6oU!bwdj`W|MD- z#>*9!qC^mm4p)`UOLa1p*`lmbL%kJgY-wp3o5^4}R8}jF%mkSWdY$pSlkyA-^v{Z~ z$x|;UZgcx_;?HAACK_?7v&lyyE5<1_D;fp+q{3gOw(py8IyyQEwr<-yGw5`$TeN7= zbM?{qk9K#N)xg=A4A(3!eG_;yTgUaWmXlGl9{;KAoPOf5Ba4fRz24^cfD-%!a0PyP z@K%m-4@^RfFzI>O{t{|GaY_6KDw}IVWntI7WuyuAoZ8XRQ80Jz+#W~?%$AS$oNSD| zpDg`3RxkC8yyQk&uW&=J0NslmHBS?X}m^lgYdB%M!)-0Xi4($Ur2rVq8KJ zjlcy!Lg5$MRZ#D`^FB?=%$z#u_20j}`2}F#Br0m}%49Fx*m@;!I>ws?;JWiZt&hex zZQcIabo?*p{#QvvW&{6~{Jpxm{1t2EPRGbkU3X<>p}QG-5(PD|KjG|ML#1csedyM`Yf$|-94epJw?$q? zIBlQ5`z{N!td&>O4cS8P8)BH<&=fDv)ggdUd$~Y`K!rm3a=s+lCeml#>XQ|dc7j5Y ztW`h*mki`nDh*Ao^8~&ko;g3gO+;SBFUclt?`t|afc|&7g1d0xM$oVtmd0XjCk)1s zL9j0?2Ab2_vX)@|*JUvy>^|>?_E&9nv7Vk_^Az6hRDD5Jvrc~D%93@qOUD#YI5udy z#-wDu1F;E6egRhiL#h$0s_FE4erw^an#!^t=A+{n*#;+Cs9Rp;=*=UHhuSOX&W~4beUmauRO^Z$g5WnkSMh7bA^b@>Ki^_4b*g5GXyEK{X#Q^o z)=1Hf#`8=iF>y@hTtlYY-^T7|d z?QNoO(P|LLTL%4(QFNk#EkiNU(A+vhkShZkxc(poB}pSB(vh}__@=Ztb*M;{|R(PuaAT4Bw)7! z6AUnV(x_1bt~vS{P-#S{&9onU5ZMHj_5OS_S+Zi+b-NL2J*8=|9rVUxv;lT**}Z%8 zKnkh!o^hEleaf+UtUnGo_W<_;Bk{B1jH$u&DaYO`FmABtaklK*H3ryjfK36=a~S0Ry-nI zz;#`#VrmVhKGX5EjEG#NRvt3CcgGr4hUXJiCsbg{oy zJdX_LibX^%b`ByTx2f%vY=#;emmjaf3bis{MEpWiZFT7yR9lQ^*ETk{|5dtDL$vig zWAt%_lA4+uavk+J6r^UwE6MQJy>3TUFB0WekPqqI*i-nL zz+_S8*M!PeCuRE`PF%HWmGj2ie>`82=>TeF!;Gnuo;ie{=6pF0zpyn1|1`y2nS@rY zTIIa{=9Y8B=*a+T<@Fg;C$0TIFuu>l)%c(A-3+l-{QsHz@PqT8WXX!Zs1yNzzztx7^zsL;7|HmAU;s2wKBcw2%1M|1#M>xU}hQ|K@ Xf-zkcF-jto00000NkvXXu0mjf@=_E? diff --git a/dist/icons/controller/applet_pro_controller_midnight_disabled.png b/dist/icons/controller/applet_pro_controller_midnight_disabled.png index 2907f3be42b56b014388582d0bffca97e7e3a4df..d27dbfc66ab2a17842567e5bfce928e18a8662c1 100644 GIT binary patch delta 2767 zcmV;=3NZESBGwg-*vNeG`xrX?8R-`(}1yQAQbMlniZcZA&yaH?ON4 zDx;;PW$nhs=4Trkn!f7SuipyJ;W93}?6Tz>8=Jm>v^6$14u4cml~G^cs=+WbA@dp< zS~AM1GMbv2E}+*y{<`LxYt|BF%nzw0VU$;C9ORpal~ZM4X4(Qqndlhj5K+bgA$3@L zNHtHcudm-)Pnu>tY4+A-c5P8)?2+!ZmYy^Yh$oG+@yuiNl)1N_Hj`R)-G<`-M(5hv zHRIAa7D_x1g?~oHDy(fVJKg8xn>VZL_S+CjPS8@;+<4M@3D*sZr_8J3(j1KA*HK`+ zyYc;gik7q<)TH?e?)7R;cr16`x%N6ohH6RU8eHFk<4-}R;Te-4GjVPzWFlldWSTB< zTv|`-(mZt6nwphFv9M>lZapYH#8x0>E77JUIe7D%^MB7je;pX*cPFIIgSYgIx1Vdv zE7JZxPZj4_UV&leKxV}u)WzqTNz|Ff>~I6cQ<)TuF#!*l+5SLd9%Qy=e>Z2xl12}B zO*N-)$gKMMmfd6Nx@v~iuBmK%*NW^Gl%1rp{??hya-u-&jF*zT2f}CDozq5jyu$3Q zrOcCK@P8m(8aLuuPr^IDa}1F)5_GhU-R5}IC>U@z(Gf@_GhK1>DfZ179`;)@K8Z=Q zzm~Q-3kAqCgPoB2y40+=bIRHXM!Fo~^s&yDn4+-^jKQ3!<7J6O!A^Vcy{w+J#=s42 z&(JF~m*en~d06r1iyt(1H1qF~`dA4B;)tUZnEDE@)gt zwy7vF)z1@9Kdqs%Vo^ju?{mnrAml5bGk>Vm!zXRL8YaByURO~34QCzbL($1~YJ~^f ztXKdfhr5AW*04;+0i4rFA2|S!Slu-v+0uS1IMv_H(*t)_=j^ zpQFL-9F#V@3k9s>G!VfsR)2|JBYXfwzVxeI;O@^0AHZ7%sjQe(08{3-zEdg`cIf?- zn9THa<(p$%&z|OIWSqOnb?rZU@4fd@|9O8VlQ+-M$~t0hmu}`7{)THWAbsS=FsztH zqQ~F`yd3bHPjj9KovwGi<=RP00uVp@(@0GIW>NqxuV-K#2q%m*L>OR{9 zDEw7`VP5DcR-8zi`{91mAq&+uddfWB_1qa8takxy3~;|Zbpl{fYNvrpvwKkHKaw>O z!s&;A#Cxz`j!B^E?MPP4nNipEWrPb11*gBmQ2SM7u*@%sq-D?(?4&FAUVqskeSc{v zZFWm$cXD~(Pfitng#YvwG;@eB=`s^52Jf{T&KXW@;lDSW`r9g`p}ZM$)Ja0=f%$BN-Whl&os zt}?SRz1GEiZxcRna?30oGk-8=Dn!XU!V7d+&H(IkIbrpO4nid&f-b8lI$fO!2pUA~ z6)$>WXzuKhkRKO5pds+`f6av($4(n~SkQWsOb&Q9EN=GJ>+W)s);Xfp}dnc-*p+Mw@;qjUjs`{$eNuMeSg2vD~w${6R+F# zbZOndjE_OBUIfF0V#P4Py7>D)J(B%bG>*HRftO#x;?R$5>Qz;%>F{7ZnHjcV-n8yw ziQ`4w26?pws1*L_JrM%z{W1Yk*xh&6E&rV}_6%UfZkNB^j~?(|5nkAb8ph<2xX+5C zUF%shK>4(X8*j^QJbyEIh;riAP~ClLo*Bf7T>}{taIY^4GK#cLw8yen-F6=RnHo<9 zjDw?nnl?Ol0apd|s_dr4FpQfOd$k*UggG&6*MPIu?1Z!~^$fA@wHvT;<#lj$p61n8 zOnxE&jM3ae89mUozVm4A9G7FlvSM6+pRZx)`XNCgm(Rm!7=Pw8kpmfPaI1kyu-CeA z$%QR*Q1s**3?^VVD<(hj38!C}NW+B?S+X@#%VgHc9jAFeRQ zlGz@`FpSEIA&&?Lteo-2@1a*i{)Sx7rQ%ThrZSVE&WTE6e~|~AfY|C(!{laN8b^hq z-AQ98trJTN;;&B7dNN|Ng7zSTS1_q5!a3rf!Ua zD60{462*NAM*@{K6PunwekuYKRy>=mIMl$>#@Mp#Tn-aL=A!_GQC-))Ej(@WC6yaBazx|0hMidSHte6`Hg7O}9 zqmH6L&=#}_4KvH#>kL_#y$(4@{LkQ)k}#~8x?yZ@hyp=d&?b>QhD{x=8HcEUO2&$* z8%vi$_v5hbz#PTG>ElGo+M`esggD+ugg_}-F?Wh1558AeBxE|V{VCXeFJKE84VmS- za(^NFTuQ}?sUvlz&LSk(g5lzu)1)##3OO~fV*Nzjc2Q#E49u}&%vvk@HR!2x*cZtN zV(;4?EMqZ@Rq0veJ=`}d76S|#G-$aTE9T~FzdgR3D8kSRXY1Kl^T$UEVZgt$jTpSr zXN}|*GZ6u$WW~EznOl;G29Z84Gt&nA?|&7cf)?c#2__$pmS!b)3&f?huP8Nwd&MLD z{=1iD57)@ZZM+&(2)Cd0q;ZNLx#?*agVKDP{ZC5!8n5}QvdbzWO3#Y9Jp^ObyjJtn{q741ZQ!hRTZhFG$eJ zsWLD#RpnSQa?c*3jD=V+z2+N*6_>%rIg;^L{D2OAP=|7=43a&5q>Ct{j511ve*yl$ VJWtHGUWWhx002ovPDHLkV1oMmUmXAd literal 4459 zcmV-x5tQzUP)xnK~#90?VEdal-0GsfBSrsgoKy8L9kY>Rt15XAQY)MGqKQQ z62z;x7JVqL_O`wCRZ1bmwlY30kbvI1q}ATmThw;lT2e)UB=QI|pj3GzQ7Emi7F<-+ zN=tc@%*@&Mk7RH%nMr11yn0>!)?$(G+xzUZ&tB&{-#+_01E2CKpV9+3-D#w%(x*?K zp&K`DJkiq*eO!=i8!TVGd~htLzgE?Aa&ui1zW@F2pZYKy`=~M5HaLF#_;JvYB3v4Y zM3#OSPJJB6dd2`OS#rPInAnRzj;aYPT=;{!9`@@aC+islP*E|jNrbxr5wVXfUc7iv z5Bv3z)2+Y&oIH7QDX;}N+Zfj`dd{Vf2i6ln_?Vy-#o~|$w1E=ZK9~6M^MQ%!X<& z1@c5Cs_?v^zm4QZez$wWhOBa~i~}>NXvVOJjs6(qW&}_Q6tRN7HvJ1@r=E4zfCk_k zRVu2VeeM+%eG;_mHB13PlkQ0-o(aSNtF28ONztFcF$G7Jhhw>sMVSUDqriCl#eOhP zq2vR(pbEqk96@3hy?*wWuQ~~`9*A0Nru=r(*0UTN%Z-S6n<$3?VkUN~IbRRIQCHWT zNxyN%6wMfBB3b~@E60^{lIC-BBhpu0j8xSTjxrRH;RqL^j8N!J>x(7Y>LUW21WqXO zrKZrjmp^b|Cise{#4zgxdLv>{RYCW}RKnuOs;2sNFT`~#E0?VRy%qRVPL8Yaqs)pY z`%6c*sD4XT%0%pCR>X*^LFA}ld<~n|Hg?v&)4+_LH7mE*p;HH~>Oc`O( z`W$y47!3Bx&o9^kTn74Jd3pIA-5*+yB5r?Rfxtajaxwb1qj~z7&Sq|>Ye3$i6Zctk zkP)_==<5t@*iyHMG(iCf27{-qb#4M$KyIt5sw(OJSb7v~+FVm9z0Cj>HlZ@K-)ZOm z&KjhhF>YVUb%NZl;@sG@dCk^ryr`_KtN>XI9E`=HWAD84&cA2V{=;U1ccxb*`nbi; zZ3@*snMx<+jG0t4W0+tb07)!y#74iO!kWfVO+)rW zfM76ajggyGIV!@nOP5yNXdGSvd7#i=Hmy50`<*uHRP1x7a$_$N40iC_>-F{$m3oD5 z00BWCEA*9oC7r#7&1)M~VJSGL>fD01uDRNz>7FE}xl%^7O2+XA7;(iE)xu**@cV=_! z)UxwD-jX?Pf63R~Uz%}tDxC!j7CZ{N3bapdZr;i%W6ldSO`Y)bPMu9V2EMDXO@;4m zud6%SVIZ-bgCxBe6!~jsHccP13AjAfrjHYnS6HTF-FE(wiS&>S zhC$eqYs})feDthYxyfxB)~-DX+H^WkCnw7~1V%-!P;^^52R+`>uZqgOKu*H#0#SX) z;|okr)~~3jI1KuYR@2N2cosV7xzYg}bQu(hh=P(76494{{>i9 z&6+jq%trvlZWHJ{FkY|MCFq1mRJNycSmC^MG7-`)tfx<(Tmb9=E@^3rR;rQNsy1zD zz$&E*eSrc;O#4JpNS^xnjpkWnB$T^$*T4( z$nP~cox{NXPGtMi2@V5aIHU`zA-EXP6Njf^Rq;mz>XyKX2<5F-WZBL zpLFNXJN@pr;upD&3nkVv37Yvl6?+i}f@SQ)X;ODeHn#X`Q75!@K;tQUl8Dl5-N=wFjXPyPn zYSG`Ig5Sp!oUYzoyZvN5?7__o3~oS5tL znFIw|v*K7Rb|x#XtgO5#lXhJ()>}MEl?4g6hPy03Os7BIJM%1yR02!HunEMIC_gh} z3JVK!wBPW<(VWPz-5WNXaC-v>jPPbdsOF2^ade!oY_x;j)7Ho73kDbTsjjY$#pSI5 zkw@dT;);rjH@eeLoC)5*7%S{)cT;t3W4LxkDt(Wy^aZdZ8bh_89qpafCr?hkBS+sE z(9qBjfrJ?o>pN^T@MgU9=W1ywQ<2H;qSEWT#K%gY~&XT`=i zkFHv^D(!dliAC2Aa{B@gdHjLbJpRBRJ-*VavD2pYPSiPXNWq_g_HR>>M^o8z`%7jk zI?-sf6ku<-`Z#ca-oq}A>zoOUAccbNieK;i{l=HX@=LM$Yp>UPR_9^p9*J4;U;eUx zekz@@)20oL+Lo6E{te|qluJP8yCOL|5`$A+T^-BmWv2-&Qjtf1FE@s2o=Ub?c=gr& zjmj?#_P=(8H~b-fUy(*NXZB+WkCHF&prZR4L$&{rXvpIW{8f>+nnLR)bv4lOzLIMk zk;!d6eMaHT&Q*~IjcDsOLv?jSsCGL*Wo6};5^Kd36+hV7zW>t4LBF1=KM^V$HrFid z%CDsH>{1xs6sj4Oc)!pe_<>qRHim2FLc)wubgW`_CmV8^T)#mJCuC4d5#(A`%2g>( z3`(m^Z;#7RIkRr1%8J`t)wESWRRUf4l!(V$dW*n#C(kMBC^fqgE<1CT;QHuXWB!n= zzN3EiYYKN+s4Bd=>@(Rok}1hqak9sqG|l)E^Q7Y9QHcJhsNB2j*)^|rP>8hb0mim7 zTzKX55dhok*Swp`Xk(~mG4PsNyQ#CzMKugmxBk;D?PTywT&Bi0CCZYs;-p&f#LCtOurjL3J5wR4*4>fY;2*T5bz zjA{ED1Up779oCm*nnG)z6ztCox@uB!@u<#p+rBBsi47StMAIs_5ex>c<2W}0hk=># ztoY^4>)vYITr;~dR5PS8RC9J?sODQQtY80+uIydzoi*G#X0@OzcWtTtO;`FVvPU!_ zFd&y&$L#H@AwWZ@_7+6{9BDS&>16S13axwDYR-U2gn{`j&VaR<}cWr7>J{ z+tQ^~4~lRbZ6_#xwR&S8ahd-~(vE>11W}MVs`iIX;o5E;we|P|zfzTrE)_0P;q~tN z-#oZrd9U|QU61H=lzV|URmo_f?F@Cy5opbd^YaV5z-28h(aQeLsRc*0cf0IZ1PA0? zfx8uL?9a*N-R)PD*AczQC2+pBW~JRC@9!t7RAgU2Juxv}neR!0!Qkm7OYXnPnAnRV z+*(pn^6Y{I3sRO|3kwTZ(tpHLN9nT|$ZB1BAALhqN)0gFvF7dWnD9&uT!PA1TqgHh zA4X8(w9kq)?YZKHhK5L9PUI%wrg6Tq(cS4wMc+|nxB(dwb7Ft%mJx^L2#d3Mqji_} zvtk($&x%vgn)unJ4mNo+nh`N|y(_{{0}M4WSJr`X`%9(@T--dU;Gym~kS)nsaaC2- zoOHU)gM0rJxWw%*nVM}sF6CW?VFu{Wv4dUiIg$}8_n|D>TV36fZQJfLZ{EBEs(cSX zRaT_SiuYDmx1cN%m4DB=lh#=DH^344p4~s2wuSz(X(9}}U`Roi`;VTc?euwiEqJ{AlH3pywTgVv&pMD?Cj$J0_F!q*ge%IvywVm(5B zCQQ)WN18%4YqK9p&y$~@f2LNPpWi#>>-nWn?HW~%dA9Dny(@ceUtk8Rc}<~p zn+?IBRh8RN`_r-B^u?zM-oO|z3!^dTW}wTW)Bgpx-+p`Z`{#=FmNe&z)p0Qg<<8EM zzRRaxbB>@t5yP#-e+1DKT33(A11>RPkGJ>>$&K8;va77H9_9AkTi2#N>hV#KR4ZP& zY}vA*$vQhX)%-<8?{Ki|Q{^_}{edg26Wt=PyrI4(1RYL0l+8r!3RMn>unRzu%Rxt= z`klsb?Xw@!_&yMcCn$iY%gYyBn@Vqjue8LX52L(wW_-#7L`NxdYg4FZOYU_u&7wQQrD=SB}ed!n=!p*HGMmys5dR<3N{#3=ZK30}L zeQroWC^_}`z`t<&II(nT)r}%N))OZvy8UC(k0Y68#hLJl3JkQ*iudo|U*45|IzCa4 zQk-SWmR)VFZlJZ~Ii_-}e1}9M<>V>vK~#Nm&J_GL@-JWhzsd%2e57a5f%R z<_Ux_ho@OnU58a*1gur}TmMPl5A0AdI@I8_G#~p0Yy~66v2Tl1GH*Xs z9(%zwuopGd{(li^CQj1y$_TN?O>Yrq(=zFdw;%?q#{j0xM5 z=NZyO65Bv9Myk0cG)rI|7&A6zm6nd6Bn7#ImG8^hGV9eQ?EEd-DKNt!Y00xEhwC@oM4S)9I1PxzEsooJLZ4Z~b;Jb&8 zmgUSPBv_YsL@d&_J)IQ1D3!id18OEpVYig#8QhRkbg0~HsYD=AGy$b}pH51)@hYht zXc$DZ5O&CkPNcIB>WK|U15B0d+H#sV(Ag&|qymYk;Zrl`ID9K7+C(E~CXn#0&--$C zUQRQQtA8|Vh60J|8GoXJyOmeyc;H&G-Had21SrQajh4YcqK|tu>I*r|%el>y=nCc$ zBB+l&i%L!tYH6i(sxJ{T%=M_5b&8y378h%jUeU(WFuDKr6i=e+emT)6H9~8co1)=d zClyHdcoS_VC)!7&(}LhFTG9H142?aBVuh6E1%KSE(Yc!-ILTxnE%o-m1*NE{k(p1M zAo!8dKvGA$imG$uW(u=4DpiS}IfolHzdKy2m!9b=iif2{@28{Tk1bg@gMp+Ta22&H zNQrjTXpAC$=TWU_144q&T}9DNM)OSVOyS)>|9Eksxj^dTE;>#|^aG8=MxIUliJEjY zynjrDXSBQMEORr3?!;fXN_!nVf(XwHchTh%nzz!?@Fej!#^?hnicTtX1ERTR`71R8 zlXp!8(k6G&9TGEzHJar&)%?k`xWP;yeIJSFYjiZ+L1^e^CXl|5RlbW#j5|Av$bSh0 zQX``S?6b8O=;;U~`)*I7Wljzz`Vk!iG$Q=6H*u2XtuQ}`=oHN|H}g84<%u)+oK|#| z|J(j^EU$&n{cGN?U1lG`GdWx?5=iPJ&!SnS{foBLF7kQ82aQAm$=6GJmmY>3N{zTu6^007<}OI zTJ7={Fd1OJzmblH*SvPnYL?$hp6Y!}R$sfgT0{Ukg$bnZ-EZn^3XQzBg=K=9tOvpb zQoW$B^y~uN(Kq@D0y`j|fwVL5ll68r(4GF;g?&T>q-xMWs$>PNGKXQc+kdhy(#{mN z@PeSC`hU?qkk*EJ;a%P7wybPk{v%iLmcI~gDtiep{|A@3d`H!-R=84w!2-YlD{P?< zHpgCbYj)9p$17Vm2x#ilH&#@%q)P2?kB_b9n7sWgxym^X^JA&GSp290yExXyPIGOt zn31Ci;`TDTu_FrX^Wnrs(|=LtTIMPVtd51|I+bnw2obd)2Jh;Zw9FSyNOQ9plXILE zO6_N9>F0xlm-stK7=^5YTI%TJnf8O~3v|I4Ove&z!HyrrndG0;Wz4zR6?mhQ&?maih$-x@REx`={0Myn7aS7zH{}oPl zuy(BwRRtL&#=_Q>6QmoQ0a&oj6>Wo#0RX;N{}q-J4L(V*Q#{tvJ=P^EC^o_GdLWRH zkN^*f42!wq7aa(Xx_-5C>zo8=LD3dr?wVLde-IWfJS#GCe04YoYT(;O-rF7iFebJ_3-#1A9^3KE$-;ZwMd-P{%7VI zD+<`&`puT%rl&v5+vgDexpRAKDQS-6XwiCgyDBCAIAy!4J7%jtJ@fx4 zsc@Q44C(Fd?RN3v+e<(asmcyv$MS(i&SXEz>Sr48FV~94&*MsfTGm~H^$60&sfWnfTkcxM$DN4gnYP-9;=ZjU4`|RmYfEWIqX3h>Qi6+9_{qt1n z{v#2-yT<>S!C}`qfEQ5T-07oNwYc&GAuQm4n*31le~kIXaFrzK+*y>$EF)@@&NSYUHdd z0|{vQ9VKcR6OE+&I^nCR-uAGTytOL=(4PEy%~{685@-dI*T$|GoA-N1iRcdG9!VbM z*sH=!V4RR6B}z*K#H%YD9SW+BSJ;Pzui}VW>B~9{etjg*s8;YDO}Lrau>%UVoRzwz zip;_~=)|D3#KIk^rM$QA$tU->W{)bKGg$Rk5}co!nu>*sE0%I^>?ZdEe7X!Njl(t= zy@!A^FfgdLok~}rR3`2u_tVO{-@{@!&*s)k?J;kJ3F`aW@_@cJ1yq3m59ec}T07_a zyEg}pWBXJq;4LGc(U4lc!*L~Pe@Y#GV`o( zoTZ}{U7{!PyFZRV<5fwGWTFQKZi-1^gugTs^78Ue95JH$^3DeXY5 z3$wep5%qvz9&n=tS`3=i3``S&kv5L^U9*3>JV$P@{yr=jCANvW9L~j_88wnj(K0`& z>3uHZ$Lc6DUXn{)O>L0i)r%SP8MrB3X&7atru4-R%M0(lOz0~x-EWC$G=$QwgA4X< zt1JVY&}i|$nIw{Zt5@%gT$JFa{OaoJn&D)MfH{M~zyZGru&!cx6BB9L>_`pd`${Ii z#0v700NI5Md+w=)a6+MK*lH#GjKvKis1V$k$X=4ArDd66R5go?vOPDZ%kg1Dhw5=rFD@nM@yW?2k+Sz3b)q{O}mSjNV4pG$~2&<{^@jy7fGDeAh%wYofjlE>A)N*Q^8Q?~PWSjd5~!S9|A;Mx!mB zn_3CxAY{Z7&mbq(13VX;(P*yvr`ATs#@14{y==zp)`*`wgJGv`i3$sovY@&!SCnN) z$Rnru2gV8Yy}h0-tOJ6`D^9mR1 z>lIGB%cXqFUY&^R7b~3oLcLyEwtoIbM8p}3IsrbJb86uW*+g>k`FphpSB0T``}X$s zHviY$b?wvB(?EJ@sc4n4s)0~lN5@no(V0umeB#5058;5%MXL1ZeH>0vIdGF}8K>!E zB`5IvPZZfLbZ8|wV}V{>-ckGL(IdMNcpw~sX>Zr5Yvd0khTZ_wOBU><~FDzKV*9Qq_%e5ejyyjkWcP z7c$NRIQU}E1}TG;qj5Hn9dW;}mQ?jy_Y~`!H*W@e;%Jz+e2@!6XX7@6375MS0zPMO z$h5gcgHmntjf&(}jFTFs!MY&-cQt`#bmMoMQ!1s|@#GOp`T2Zab6usm!tC`!ztmKd zo*sWq@!0i=I(gz`@SK`ah5Chw)fzin1x-yyBMrY+(Zp|)%6bL{-1A_}wAn@LJ%xxs zoqBqDqBo`F2c$gXJVD(as?WF^G<5~Pz& zSy|b}(NVTn&fog^(6_BEtJKtfCv-2wS_jfEPqdoE^1T{~iGX~f#YYH8#f=HLZpznZ zkSY!N4>eZrBIDvZqknP(s*csKuGZw7(T&Pn3HImoaCVlzUouE~xkv|!Y#^PTo!6ND z@}RYjF%(Lq(jY=mePLoEy{oJ1Us~dJ{~l(n6o>N^2i3bfxhH1vDY|!tmN>bc08XRZ zAt}E$&A|})I+C1{avRZyz}&rCmvM0al*=H=qW`>=6?^WTJD<0>l*rW`wWJRUKg{zK zs^M@j>UmDW^(HEB;_>)a!H%=v;D0*{yUbjY&Dq=xZ@t_-iQl;LiNQS0m0#9vIZP29 zgFIX6iYCIpEB)INLPecA6w#F}_EVJ!*AX*SS5Xa+Wm*05r9yqZBZSfxgnViDwj#Tn z2!lzpt_Hrp!~f7mj@&8OI@sv;_G9#KF}Z6G6beQtyo2{KPZxU=u8`|tCt1WTjHHIU zA{KVOHUIckLWz?e!)cEEqAz5<1yhm+JpS!^T)spmu~xFMcX!GMIHyWp!hT5ITeR>$ zamY$LshQuS=WQ`HJ^gWim-dDvm14g2$fdcyu@PZpeF|2Vv^*FVVt5UYf1xrq6Z}eu zS6ttNGCP}KXSy9Mwn)e0_ity+PD1kF4-fg#zpNC_oPkOjV3D!CNeq>ZjSXH#;PKJn z%Rne!{*h_o*9-GQr&E*{BwuDjB{9zg^JUE<*W+j}vv`6dMP*&I+e-MD?;nD>S=JI{ z$E@6dL&z~9Y*jxjJ+VnvLeG;v06L){lix-}uLt>kJCG{nd-R9-63s=Y(}RP9wO;1Q zztVkrN>PzjS^4@^FG2FyyLay{OrB~i2-nDMXHEb40$x3Xc5z|#nhorY<}r-?Fqk8x ziW#$$Y1>8#e|i0p;~~$+-oY`{x4^yapHvUC*-+eVW~rvnV=KdCenOa7G^ARN0dBbEBaa64d(T__97dlwYxfs^8;xE zJNlj$(Ni-Nicri4@-&F{Kh_R4&>0KbMuCAU(jd(N4 z%dhQjpgx`wRjfMJ2NRpu$;)?BJwtQel+wiVE`;79uC$QrK60H!bU@SHH@b&{FWcGL zGT*yr@t)KZ!DiB9k=1_iTE0cF(Zv{=62;Z?QqVuhtwq`PR1> zu|Dr@iy#7Pbn|>%fi6|p60JWIi5Yqta4C0|wyVSN`52CuxdZo_y(1|pIsPi8UYWsp zBIA5I77t+0D5j>6M@e6Duw3qap9$k%8S6ba#nMwMhd#<+&9?NlB-~ z;5Z;w6Y47~Hs}!Kowi0Q=M!g-cplcjKqvGef6=!B)<*P^2hZ#qx)we^YiMZD*U+Fv zSUR5r?$($^LG^));HXoV-$LgE5#dRL16|ZSeP`^tSr5UiIizB$>M?PSkj2*o&WxjW zg!}_OE>;MiEF^)+HNmkmOZwB+6q5ZO-WJCrZSUvz@lUkTAH8%{mPRzaKu#WrvQ{Xf zBauk|gWF1@|H5K{f|24OlA{pW1!!`oJMt!%Cq2jG7aBsIG-+=^q~$aAfB{0RI8EKgai8#p5f+NJ%{eP38K_Keq#l z@{hhu>wbTY|6mAQwm#-=Fa$bSgZ82um3-8*K*zd*A*_(ZJ( P|5Sjjr2~Rs;d|?U@cf2? diff --git a/dist/icons/overlay/button_B.png b/dist/icons/overlay/button_B.png index e8927addc4dd0ba42485d890925e4d0e157c437e..4a19d8176c74bf98c1be41a923e1fa765a7f53cd 100644 GIT binary patch delta 1527 zcmV;`)nI)=K7yJ9oM!k zoNZfLsn)h_+qP}#;k^7WwZ^@Z^g{?@$v4Dx#idC48VX`4!M6R6oiL{D**s3Z!VjCEu`+qRVdexUfq#fXs4X_E+ zQ*OjBR!-qxi9QA&^_f8BRe0fJQ6{dO}45EH%YibHpFSr zfTz(?-YDF`_kRc$gXXjr;30Q@nVUevZu4amk;SsCy`Tp?@I+iBGxH+_^oS|`ydpBd zk0cQEP-=W6GP7Vm=rIq)WEalje-N8M54u%8ESw|jO+j`jMt3$2?pM(Y^scg*Q<-IO z8uYNItqRVZP#*<7ZlXVv2$O3x&QMG^S%imG8%!fs7k~O>y;JRguPmQW)tan)iDg;r zocZ{aGokwv?sDzlwl9;lJi_&y%;v1kctTs*T}UZgo2%I#_lIHv}%VI+TBd($2MaR5IS}dp?`p#W?M|6Qb~QDQ1=%R41endBcW#;WIKK$p{%SYf`MG-Li6yD&G@6t zODOupGa?xBUUQ+~aN8}st#>IU_@2~U=&!b0_*H)|YW{Mky`R`_p(&weXEPVD75v6_ z3(W~7ty&@!ay)7-w5{zH8WAd<_B|p(0~<_*E_UMH_gWDu_|;748%(yfI-#KaL4Slw zOm-8Y6}cysXOH~4H-dwMMec^i^9LSq<}%qlp#t7vbg)yvrJ zb*J>4Fc~_C32K?gwV$lFT9^tw%zsAeHzk?~U2@!kJ?@7latMnNs_qMG!n>^DX6P19 z;MXibXeg_Q@Q_oW@VG^<%k0Xwm)VjHF8DLQ4ER3Z;p==pU5IZACf(jnJn{&$l(X}cp`JZjE(pbB66b#=IUn^kp~YM-r3BJdqB8Df$Q-- zPMKTg$L&S9jvDv_sw~{cmjRPq&76pICKp%RFslJtO{YIZ)dgg|64 zosvYQBwYc)8$^i4Zf)~H;?dtIrgLxICf@gos!2*<@AiJk4{v7OW=7!GB|zl=*-k5o zPgl**(6CM;y;MLK3KdrA;$b1NPm&I4v4@aQKcI9aivgGlTCm~Xvgq2W!Ai?ZN_I^T zC-g73o0_N6)G_2dUo_f9wJYSv$*iiU1Ax02t|6y?wy^c?a&rbluJfJ1cZT0_!Tw$` z96#&Jw}nev!j18W>dvLjp}Dz+E0&nPu&^+Mo0}Uy8y0dmu79QD2j}FN&&9nE0|SGn zP?o%mY^Br%k*;B1pc$glJxk=s4v!gkAW zCydE|jk0=gmPZ>5@XoIXysp45c5b2FR5deC9b2N)U~2K=9AF&5X2k>Zwx3{CX`jU+b&(Bdbg&6B^&=rHKr53oX4Z?~w-v2Q z7sWl>-9KnvQ*(#u(Ym`A-L*u7vQRClUOS|PqEfo1+Zash&?cVZS0R%gcWs&8%SHz*v zWTOm9HArXT2~uHRsc4ju$(1egdP(LSM}bYs%v?183A}M0xhyPzIxcRFE{$9V@{(~# zLSfDsm-m=HhLon;H<@S(61|4IWG`mGx z`1ZSgjF|;&VU-`Z3pkSYzb|b*R>SH|-SOFr{Hq5H#crtgxFyRJ_t4iQn=z;QZz$nN z=+izB2nNyu-Lsj{rb6pP)_USv9&w5@n3JBPt!K2qj z6tSUo59o2{C!$@|;{N<`&d_|w6ViNQcXP=|MO0$>nZZe%!JEDlz+ORfl73m=uV24D zS1NL{e1b2s=^7eFmW6E?-M%ee?LCn&KkshaNVG5;vH%R9=uWyW3FWf`r*deW7%Ua#`E@>*!NUC+f>`H0s^#(;+ zFf-Sd{oJ{8)XbQa<@c}L6>Vz;HYXl8+cTw<<>lvBih9v~#xj)xx*TTd#h)sjXexeh zmApAs@7dV*?E_1){!o{|v$5?{ElFlN#ZQ${dqF>mI7hpAn<{6A{hf7JH)sP_d<=6rfEsD%;Zn;D zyOZ^qCUJ8-aEY4dMbwf~w5u2D?CebIzD|yKrRtJXU$5yl5e|LY8NxAnIuw^Ae#i5& z=gRIralW{4x%2oP*D@;%Mph-KM5Rk=ey7KXxmCnr6#K~NXvG7%=?$l~-ORr?L1P zdx6kMihgbVhDx=w0t;d%xczk8D}Ymvmxreds69RBW64!GaEW*d_?RFmD1qW%wNn~; zh~jmx8RdP1Ou8l{;=Y^AFR3YWk(~Ld()RiL?3Er{>8PF_lO8a)K3`zg>gx z^eCq~-{VTj)^c@oYn>k~5rowqhaA_tG4VStFF&7MSXh{gBm8PKEAT361}myW5>w^y zZd~Ee@Ei_x8z>Ny*4q2iB%Wzjem!NMcL>jynh)hE z6bh;S);T@HNrGmD`=GoulG-x;=F{IB_)U}}_~aX#Eu~-iG39ge9AkdZr*MGVCM7W` zDZH_Pw1sYgF)_jffviH|rIQ7XLeCeuPI-FZyGA26PO(}_=?i$D(gNkyWfyJ*1zHe%3 z;%TcNhUJKTI(M!MEr0ec~L+)QMQXL^?8MDUqW@-;N+RP3uZ%nmE9}ewi7p)YbXH8f%;l;(pt=-8AhO+lb z9k<(bxS(nRToO(sv3l7Su6oRCGzPQrne^q=P0kS$UF_8bFktc+K8tfw)uPMZHbZc12q;F?uhZoczHKdA7!)+B1IT1AtjhyOgkq;j}v{|)0 z)I(ku7Z>m4C)hs=zU_zTs-+|9>6w9Lbz2(8Vo!p*5OrYjzrN*P4^H^#SfTJy*{(hU ztN33#2IP~pw6vYl5;k>^N7A-|2wqsPPq2XQuiF=~gu+4Rs{Q4@HwJQLXyYx%eBF~i z$66c-_>*{qS`1?2X=?|BaP99Qqh>MX@^qozNOHdF5%u{au>5F=$J6uIz1cYv|-(DAIvGx6 za*v^G^ct)zEiJPt^Kf{8Kxp;;$?f|QG1bMCoih6`^B+V-!u&oitdHv={fjA@=?D$+ zy#}2^mhb1g{Gk0`w}iw8R2CtOcn}oRlo?jIJeb{c1Sa(BYsHLNlk&{?5}P_@R#yX; zsQ#zGSJooAoB*4Yh1jC;rA-gBU`L1lYlxohE4f59jGfN;Xz+6bpbSlsW%_r+{s(Rt BTUP)8 diff --git a/dist/icons/overlay/button_X.png b/dist/icons/overlay/button_X.png index fe70fb6852e8bda84daace73b0b85fffb1281368..f50a539748f6b7f4ec7c2915025ba6a969f82115 100644 GIT binary patch delta 1743 zcmV;=1~B=6AJh$y8Gi%-0091z-mCxs2AfGlK~#9!?VJOYmE83D@BS7$RlTxT>ei_`l^m=2D6O>8N-M3j(#qNc zC*n@k5?wJCGf;#i*8Z6!iZBCX(M7eyoj5UTk7jF7?Q377K7U6ss7W>cuu@W@M8ud92ij~R4VR#>_!4O!7 z9}=1IFu8+jiS=NJtjGID?wslYdkHGQ2&h)KTAQVAzzzYULk&tv@UbsL5{#H7_BD~3 zEZj=9#3nEWY=6T0YU_w>X74A*$|$hMVV`JznG3)aQ=ENN$Tn@;t^#8~ZBBleHc!J+ zFeWTboDz~vB(N9+V`Q};44X%w28n+JO^ppcgJ;z2nW+^Fo)NH*&Es8VU&y*kAau zc@Wlu-hXH>JeQ%g36We*a_p7ljh>UEKC(*oN1#{oIaRRw1R?US(B~V%JMVk6nZFlS zf!?{6NynQm<;g;iGg;%-AW;6 z3lY`n7k95MuTP(j^jc{15Lbw_$QaJ(XrKV+C&iMXxhv@01V3g!m}F9#6MS3qvYj9KynYz z8@RWE!RahmeS`QbFA3M%5rMI9(lh!k7@U)ZWqK{oA$Te)9Iwt~ArYLfw2Yz*G{Mut zGk@zxu%i%yZX--ktoOnNP0>%V`n~Y?p9n*!-Q5T7NuVjtVzppNTqYVKalETh+zFcF zH4jFgB#fbMb2VzW0?m=&2u+(qXcOjm*VQPxf+o3;bs9G7c^+Ys&hAFXf@W!>VYC%t zmQn6TXM$$Q%_SbF<$t+^ zA=Is|MsXr&h8vj_5+-v6s{@T~)pCw=AG*7PCMe)E!OpilpH;%O7otn0n2My*>UJTI zBlwJPwTfq$kD`Xso$~RaBlo)`&iWF;`7)vewtOJm@tR`@(&W)@Nnb9-+Tpeztg|)RvH8p?|o?=|p(M z?qGRU;y|xyvi`#DPIrC3^KLE?c*tW)EcXD*yYMBb&$D8B`Ub+4KZM>zJdx1nQ@@|| ztOxZtjTOSJeyN!dEfMVJBjMPFZzcjQZtv-ViW|1y}*6RxPMtzc4vz-B;yE2wY~ z8o`*8%sce|NA-#F3QG9|UOJr0a1qYJmP%qf2_%mZV{BIBhB?88wF#*FI3 zVWE^Jh}*~Pp_M4GPsI{2CM;1WTj41IRDv;}M&*TGLPX7v!MFNHT7TgS`=mJ7jLzT7 zibkhziPy0)u4R7A7vXA3!31=b6}_?#!N6#DC^#sU#nsBe@Ug4ZtyXGvdqgt9TX_+`MrnPj{Lxy7PSoNUV-HNwzGwio8$Nw>| zdLx3tYO>VRUkeErc}OcHEC-O-OYNRG-o9P6Kxd4`G!&r;YyV6WiZBhM&>8Ql+y2~Q lH!H2Q(n>3>w9<-={si0vexpcjv5002ovPDHLkV1h$KXdwUq literal 3968 zcmYjUcQD*vwEvRDDr>D?B5IHyQKI*@n-Ed68@(=x=q-pIJz5Y!^pYs6cd@LEn*0#a zB?ub?5j9xd+uxh_$2&9Up1F6<%)R&2&v|KNa1TPmNdo`?L{|rHLXv*}F_@CHB6C=U zNdn}nrHcfUCnPuf#^>6rTh0Da$oO!h{Uo{MzI;fFBud+7PtFTmc%2?z)X5P$07 z>WjAbauWCSd6K)W!b#G;(1mLv1M{}>!@ZEG%)xGA*SYg>PP7aIgAVmT>br2egcf%D zjL&Y>W7QSaNLT>>mw3HN8Yy;duoAXq^#KPJc}^P4tBhSI)!0a6GM%i_dq$ymIAg$% zsP_pftjC}gHY_M&{@Q@3d`6Yb#jC!(8DJYA+-b=2=IhS9ui@$3&QUON!B^bQ;r|n( zl3DvT$((}&KUXAjk{sB8XAH1g0(L?X?fCA>7lpuAXp#JG6ORTJOjddzhb(XZBt*a5 zApg{I{fZ_`D8EQ9*S$1D@q;Z;6b`vDbBNule1Pr_zqX9F$j!>u3CXm0rj zQ^IN#d8QOVD35f}?HH*?Pfyn+{Shgl zX!+(g#DVSxNea(5fJ>s{<^D-9fkY&&^YT+b=<2#DQ@OXzk|N>_Dli=b@sVZx3M=P#jBxnWrDVarcGuDx`&9{}E?GX#zQBA$hRot>R68PqkS zc&2cj9=P{%z07L>rs;>l1bJ#}Yg^aT3#N@Ze zsC+_E8#QH-&8E?L{w6;2p9G(RHqB2s?S~H^qEV<&p0aFU}=|c^8B3b7rSDtjf8IQI_swJI@)7)qV^dH-K4$!f!Y9h~qo!O|TZutk&aPa4 zuhnqqrc3&kwJO)v)`}m8s2TIEJXa}@zo+b+T~t|Fx%aT_!Bb{MCYj(Pw?7M5wuXiV zt0Q=HPmk8*%uFH;8F4XOUCwP5@4n;n!N|-Ej6xw|ywv36bP9Teo_BXgapU2Wyz72Bb-TzC<@i z#e#Fv(!50@%9D0Om+)lm3mzV485ztf=exaHr{ZE_a0mV?H>YRMT6Xz(0Cw~yxwOZO zU3^iam?wyri(rXDA(2S@=4$wj^YzkK{5NkROuG%rnTYeg(6-RvVCAE&x$>$}sd$H^ zH_3h)$GIMDb8F5;am^mGYdNocM%=g)4B6YAj+VCy^d! zy>ZaqRUe9}EXoom^RQpP*-q zd$zVb0X))=hw-@3Gf5|!#lp#niKz4Q5Ub9>G%S=mWclyOFZ$7Vrl44Q2t+lB;UzTy zfj|_zBErY492`o@s*r$)xcDztCg2}YQSF;Tz=Jd=MbBf}lDPSMpfj#4Ypg%v&$p)e zqoFK`xQUIGM(ajL>g|qAagRlw&;yv-nYzzDlg@Iq2WpX2&elF^BSr4@>)3G1T|rRfv=E`_|Un zKSGJt|Le$=wY67I|IVUc5z2Cp*AT6qizx*K^^OcZJH)Hq{hb~VL~t%Pd26n%6pPKB z3p@Nh)qXggR@gF{D-W%DRP*x)WouivaqTAO!0f+6-Lx=2LQhN+?AIIc#l=NMb@fVX zmJ`RQ+Swmj(@*Jw#nshx#>OEpJ#6aOB)Se5iK(6p9AuC_@bAATm%7nBU6a$(Kl@%X z%^%6-LZGUCtN6x{6Q9{Fe-ECisVQeq&$U*08WgIVDxS+nKuFJRuGKwfTbh=JM$&a! z4A3w!VJryzNxj|rF=ufBi2NUn>TL) z5zjWK2?Jc7D=zoovyD!)lvGqv2@?Pg3eNV_*VD@t8rmN{7!*jEo_^%`__4#6>o^sH zqVF%fOAEied=~Zj{jsMG^?FG@kne7FvtST6M?K3~< zf{ILy?tNJq<;X}*dV6hgO%3FXxWmSrW0;>O2K7Q)=*xE|G7DdOw{Vf@Z;=m!J> zk*yZm!C*^4K{0RW)f3I2`9ah#ItP)+`ZP#T?gptElIp-iL1+tlH*0X9 zXj6q1mzGA&{cva~YHmgzf2(6Nj*Jcc`-a z5nYU?;hZfyHsudZ=^K74ZER#zR5?=V9$-v*T-DU3U>B&01$yW=tFm>2oTHSin^38yL`M-nL}*E4)s3KfQUC z)PQJz`+UHGc!h{|ARk}fhQK`sk2AXj)s()#HYI_*Vd+s`H8mU4Zgf2shr7(_q$|DA zWx6yj*RR!JQInJ9fV()F%lWqUgT<_tWBO#pl zr$FrOl)a#?D|!tx%p-sQ)?YjCYAoAaa-7!)>tm(IE3XY@T5jFCMW|iCYYyV^c-m5K z8Mm1eQdHHs%gr?0fA9tJQ&H#T&%9!9%_Tci#M>ERUx_gdE7wq`O0a+Q2S3fE$ zD!e^$5VpzXy*#pA0juvC^MVTTQb)_9>R0FC1JW|p;^$r+9UVEZU%zJJJH`-|91;hY z379gL#+yqROs3SpB{Swy2o@fnoE(}mqwtPa@0#p}IweEHdoTVZ2|(;HP$j<;qJ=Y= zC3p=BmskKzS_?!_sdoCi_sd&%17+>aPq(}juEP%&CFM~($#qE7g$)V3=1<4k>z4Xp z7d!@LuK{=GQ@51X$YB@0k`CndKfGr){Jx`5sx)@~3d`}lUzopD}J#%tgZ1>*H<8Ue=vNSLudsl8uivZ5KxsZ#(qxw-vK$Hi@0c=KK3j5dT4_aH6@;O#9lIAD!9TsNkEDM>LK#mn7&R?8lf+2D$rK#&;e@@f!gNGP zWyb0$oA(h+o*%-`!%^Fbz^PYd&pHCg2Xf*obTe0l_+3aIJ=a^)fAIfFNb!p5WKdMv U3YsB9nw9}wgaN!(%P#VN0Ffwzb^rhX diff --git a/dist/icons/overlay/button_Y.png b/dist/icons/overlay/button_Y.png index ca0de569df161c6492d7e42ed06f291b13a75322..435ec30d5970517add8906b4cb0f0fbc31bef447 100644 GIT binary patch delta 1497 zcmV;~1t$858sH0%8Gi%-0091z-mCxs1*b_wK~#9!?VMBmgs%$_=xzt*;G z+qTWIZ5uahdu-c8Pi8$lgWIkAQb{V^mD!xK>9>L2;zKUUa}bP=AwU7$_geRY{Q|a^geG z0;3^j;(b}ya>d$*;t7lf4KNaq*FMxrNl_Us@CRs+zeNXBvP#meh8Fk>On{B(>DIAB zavxD2o4^FwgcddTFS4e%9E-sMuuR3FMp6`?kJ1~i-#P6BMS4)oCOw{dAV8%Yf34Xf)=o%URjcwXJS2Q3G3a{ z6Vil>bs%VwYIzcxHLx7Cj8!GIO_!jnwZkO z3|m2q*($Fx^MiUVXgPVeT3pZn;2SUnd>PYI-U_CK8-L?e+x>MWd?)h5?KEVYe{dQR zid)!5$x}oy%PS5@yxIEoL^xW6)GRv;e}ds?N>52AA^?}aC~vtSblt|E zA&<-92{0^W9Pd+-t_ry23-pvcLxe^=8cH1^ z7>!=^lsrxN(oT7cmAsIMM2KTG;=`z-CvzS>1%GpRf^QL9t-QqtL`1|p+M;n*6P~80 zq%EQ36XmtXvXqF*G)>KtW576k>@$(OLUG*(eahF3L<~xf(GnGR#1lP_%jqfjo>Fsm z+kuFMyj4%M8yJuG=_z?qYj!)6#}o1BtR;$taYaiwN~0v5<@3%zK*VL9-XAU)pX-

TrA~TgaO=>0!49;8PN((_b*9wIb3<^%Y+fc zoq?jV0ceH?=qY)g;H#Q-IWivvieeaOilIuKrM!X*6z8wvWrQh)1d9$gCwivO5{tUp zHX+RMQ?Te{&>XMO(_|9MO^Qwo7F}RcRDZd|q9D&(OlsgWa^q*+wFtu0zoJI$h@Htk_lYDIr>!UT_}R`lVpdwc&(rRX1_x5cAn8bxI@Cv4nf zu_1M$f0rENgbzBUPPARrEj^o3C4agx@;~ckvs8&Ta-ycrepr$k(PgnVyo;OCA}X$P z;zfJ~n!x3?3QjjA`neM~sDw43<>X7N{yVm8U1>m5QGIQrp<^mycR9jZ&7UiFbIhge zFwC%0bB^&=1+qGpS*N+ot(Hh@0^2sTC)T1so&nDqO;4QWBo`N0Vuj{%QGc3v3lTBX zg1!1Ro#cZfiagl-Ty~gKn4I;0yoD`kZ}X$ygllaP3edGq;ghR^FAJ*qIVu&ix!NKy zdE`=Yhg00t++Q?JxLw{xG#2|hHl(y_Vhs>Cpl7a@V^eU1sBgZdrF*QUy>!K+hQ`?8 z;t_GMV|Pkw%Xcv?`e9u0wo(-zR{d5_I~xhl4cAB*m7KIj9PFMX?-Czk2!6%{%*9%4 z*v?wa#RU9>A^1Stx&0akIa#t~$&w{Y7HhH%`)G@)JHB+>00000NkvXXu0mjf5uC+4 literal 3337 zcmV+k4fgVhP)k@0^qU?z?;6edpc_*x1f?4-{8zI7xJU>= zT`pH15k0{en+;%`k+Gv^@H>pLa-YvvV`S`}7_@@s{pYbIz9m zSPUS=?0f|20PvQgD1Y#Jy+I=aT3%3($CDO`L|%-Zl}TpjGeE)swzRdiy?FBE$t!vW zvb3O1r*jr#Yzu&Io1Kd!>WJtCP1DK&kRAanDQL8L-3DNi*}3Qe5gp*1Kc&YdkL3h) zyWNiv(QD|u;7?>OGsYJAe7@Z>omxiF2@@tb+S=MyF~)uk3+1IV1Yjc({YulckPL^G zc;oW&^6pSobq}m_J!vS6u_HwEh~Mw8kK31wgyJFkefc2_9^tVdrz4Tbv_K$mA|5BE z3hH*d-2gryqHmd*hlQx;oXsA<}H(E|+o4$aQaPUf8d6}FhMkfkWfdrqfwnCJn937V3U zvKB-1Ix`-K=so~{6ty>`$#uD0GXd-|DqaFYjIp2ie7^VkGt~VJj^2UO&yM}po0Gl?lKmuqfc2D&x5+1c46 zQ&Up|GDd>Z($etgqmKdrR8>VwY3%F#*M?2DN}@x88Ko6ii?Y7gue-g!-176 zS7Pkgu|oEdNCXQPE|eAi_O`aR-vL9e%76fG?+!uB&~&cr?U z+#@rEZ1zl=x1k6Vs+m6Sm*CZy!GU>@z8`d%&Tp>P_FHQk%tsf`Z#3 zk;oeW95VfO=gO5UNJ>gVK|z6#eQIhdl9Q8hB5Bz*SemNdWLp%bo>-Rs!X3fQC?n-)2I6!Fu9J|vu7haJ3FqJ z)2C05sgqS&TB_@1Qv5j`Qxmnd7dY(aB#v#|X$&pdRX0*REYL`IiA>?3+)V6hZUz^Y4U7l?W9T75L_tSlkh!oos4 z@W2B?ww!Ym7Z)QO4h!cZk-Vq|as&;B!#|SYXP~TGw+`2?UF+WvorC);R;=i^gXC~H z@a(hC3g@tU_ikvKCTuSo&iTWDye8MwTV&_YpU11OzA9{=pP!G(lPC9S6U%sQeSJM% zdF2)1_i6@_YQrOo^FTEt(Lg>h3 zK!Tt-IXRgC?ut8|IEh3eC@wAr=Um9XuMX0ZB}Q z1$8(a#@U#4TuaWiHE5R#t|( zx;i1-8#_pfqG0LLrNSdhn>KC2g$t&vUehCyNMwA%3EJM?j&ULP zvt}VLFHh*W>gwv4MyiZ}F&4j|M3f^V8JSd8R^sE2KNhxMwrm-)va+yf(IO%HP$(4B zSh9h{FDNWJ>1e}-4Z)s(dSdvNAL@H0YF(bfgGs`6joE*49>(mX-?J2Lb`S`|i6sW+WXcf(ozDVFh9-SE;C| z5WXE}5$a`(trnxLwxZkOfDCN@-6{d3o2X@W+V(*CzBe>TD#?!NP( z?23hQ$^LBm=eqY?s-U4zXeR*Er(q2tu7<!yrL6*Rg{vAGi^hY*ahvYMKjrtZ&4 zFA`G}r5J!^3s)0}c12N2ZhTU@pkA-{9Dp}u`?a}=*Q%?lZ(43euQJXx=-j5*Mjtw& zE#Xb*BBe|}cR`vFxZh{7b!4@L= zrC$DrDnzvOiI4n#f1eBX z>b8kM7mTq50FDC4&^wk0oO1+&gYw4E#iXR9U-W0B`(!1*-+vasd~|LgZZj~5F}C2? zv14cYGnBfbb#F8@G}Mk6GiDTk`;CZ~fRy3DI5!P3P7RX@2?jhtaVI8Fh-H6!y0p z;fsJ(IA`yu%bep(*PDB&q5fd<3 zPyq0Hy{8#tKOEwhG?fmA<9anTVd!H0>`kofK%{f_9Oco13Ak>SKzc%p;=v%+4pu zr$kf^;H~J|6NWQr1?^6Letr(;d>#?a1|YVrgFKEC(L0>;H;rmm`ywF(?T*Le8BIhF zan7eO#->32rB5AzLyWQgM6@qjVq+N+PS6{1I-TjNs^$<;F6VqaV=M=qJ6WWmb9wbq z`2VWhUjw*=&J{+#22c+mNJK$ZRf9wn^!a?2Dg(5!v9YnSv9YnSv9YnSF_ZrRVS7ty TfH!AO00000NkvXXu0mjfpua&z diff --git a/dist/icons/overlay/button_press_stick.png b/dist/icons/overlay/button_press_stick.png index 6d0254d50ee535c54ae1f5576d1fbb53d3f4b488..13bbff9efba849435b0a1adf67a6fc8b771d83ca 100644 GIT binary patch literal 2477 zcmV;e2~zfnP){o^_Mx<~>RWEl3sb%I zlsf|kj9(Y<$s0@U|OcAT&z>!H15y1**jh&d5jOne_=Y0 z`6{h;yh%7>WJmp78uXV=x5o>j!tLe0j2m5_9G`eqe4f*szxN`RHdcKbYAE=M*P{6t z*%9Yu9DB~|LV2PtL!Qr+ZIeO~=Y`N!fAPYGHdcKb+HrAvQb1w{%(SP;JhfPco}HAM zg=6?StV_pRDyfZC-{#{=`j!_?iAnpKOY@j86F6$QI+?$T=Q&X%x zr0hlE01s1*%l^TF`XVRf@Al+PNr6oYaJQbUA24cBnT5qECv;G?4EcYFX$LvGu=NJ^ zF3JIZNsDdqLYQP&$~PevVYtDPAu6YFNim^;VnPyfxvn4Au|CC}tuL9(o9wT9k42(- zO-|6aILO?~8&Gz5U92(bF13wqk*IFt9hJ&$rkKppz2qEoQsdrg`ctY+1k+mvoQ6L5 z(b#)YHX5Tb!!Xii)%3qY!h!a6i%P>v=9t(to`Qf|jFYa@eU~EPNV_!PEpI13xXBn5 z{=onh)NyBs#9J0vk->&ojY}-&u&d65dc1>#L+$;RIR!_Uu2!uOn+Lw$hlLm6E{qhM_N}b4tR|Hbz%A={+|Rky=A~>GGpWv_l=G zp%zSa_#y3)RCfq3X`Fbz{zk96Nv3MvzhGKP2e{d;62a`2$w5~0>0`s|I?L;# zUXp~nO;*&8omL_*wOGj1v>#CA?abiQol&%L9xz->IGki7 z>%=ulfoGf~@;E7EoeY1Mh`EC=AcdO_I*k+if`QyfV>?({_X|opiN14=$hT>)B7bAb z$ob+5FIwCdCWUP4pc7lcOIYvxWKgK{DkH_|EvIO_OZDc^YqZ0B?9_>^z)CKSSG*i4 zqCGLi?nP=1D&9{yrlsw>PE0+f>~;wx$%u_q7cH-&(oBI5{vFtiXv? zC2}koz*WHs9*PO$GO~B#Sjoz#yP}Hm^*Ty{S4G?w_1-8lp?Mx=`!U+nq$1~3X{@TV zs=B@pT=tr1WM4+r_5Sq03b&KV0^gJwD+x^E@K;O8{$xQmuV8}BG;E()i0(V*1_}Z< zVs<8DUNz&KXoaHh-}6Fb0c-kt%uH#ZVBmb;4gcY$%<4nFtn3*ow{vf@;4R=!mzL8g z8zS8T0t$co6IXbZGD`e!u~I<7&!7I92kx4?UOf?pO02USf`3X*;(tCh(#=mJr~rr8 zqTIlp0T1{iv%XvtDbB#8Qj*}_f%Wr$2Aq8xwFc=Gm@_v6+XpAt%Fgm((Imc*L!?)h zRO%T)=eJtbtA;f#(8G6jmKu&aULKd==YlBwjEi|Wf4QD6FU||RW-2#PIAE(Rk9|!V za+JC*!N>EH_&;1n!V`QP7FWR+JQ=(Ze64Ip;em^h$nrJz%ldFlnc^mFDM!n-{5(Fz zclaB#=C;4_9X{pfx?J&Oep1Vp^2cJGa91lv z`?Rc6a6OtiKCOpmTY1u*T47Zn>Ov&=|BdT;EpAVFLWhPWah^I8a<%hHQ0$7mLxp|* zlsmAG#5QUZ@;6uz``}Le6(&QAcc;?8TS0ybFE#KYU&BZ8X0jMJMn_7oSi4v|=@lKx zjk4I>y+_Hlcrg!Y^CK@$r9-r*F1sq-?r!Kqr3QAxV)@Dfx3)XNrluWTgVsmPyKn2HDwl$^wiyq1!vEF^|=@k>c4{3z$ZqiSNK zHnJkF#vcV!_zf4!GE`5lKt{{XxE?=*Ch!X$kV7P<6x@S1l$Vp8aR%Qjui^uIjlVH- rZu=Ww%ZGRkcgbnI6P9fnLM6r=WtFyhltC3`00000NkvXXu0mjf+;P@y literal 5225 zcmbW5_di?z7sqeR*t?A_C}I<}T53c?T57aZ?G+8RcWc&)U9DZCXremoT_Z(}3K~(` zYSAI6y^G?T&%f}!kH@+1yMB0|_c`y^d7hhMZiZxI7GMSdfX&1hVM!g`sdgXCKs}r2 z?)Oj!x*!9Sn_%h@33kU)@0t9KZG!-Sz5hQ0<;t-OQXldM8{H1J@!oHy3{oMZZALr|Xvm0Dy;Lg3!MiUc6Qk5h?UOcQB^*YEgDZBD6(~uEBBeZ)(Sb zd+$RzE~T$z+W-|tv_guWWDHaCup3Eq2Uc!GcX(y{Bl(F@gJhegzs zH10WSDxX7Tg6&1Ni7E8f)&Iwar)gOG+HwkO6_H_(|oVBv34l>ueQD%-=hI4>FKV z>|`MP4V<#l*OjQi;W4Mx_Ix=fPq>*AyD_#?zW}Mcw>7Gb@r7B|s5eTU7x)ebTAP}P zJO=;x^@1+^k)u@^X~;uVlBP!-ER2%6NOyNbp#4x__6;KH0>y!PnQP8q{=?!C9%F@F z;s+XKZ8wycoGE^CIN5!}mrMQTu)P}2pPb?^hiZK^IY}!9SVP%rR%bf&pKN1E%E2)Q zz4_EwfTr>eOLWiKM`KCuSHa&UT(vRY-ZNZax^wWLFG*Q_^VLa7Nf&&4d@fzB*yeiL z6!rHN+^%`uP-Z{fv z;wtg0AbYY^W7suVh&nuf(%6QT82Kh=Tj=u#WmdQGY_DtYuh)J-kv__hRggbK_JE?w<)Db`RL$jNPGonZ*c3f|n#Ifz@4#|SehGWEl5c0I z_OtZ0>Agz8o=L1YuNd%unG+Zy`%XE%u^>Y0!`VJwzC~{0Ya1(}3;2f#Tq8!}EWIwq zHpt;unMrDEfOrQ3p0BBpn!=mldO0En#`}ZwY@@TN;Z?OxCBf_{LTgx^DT8P z@aiC$N(0L1)9D;zNnbjD6ll*~VLyjtS?p1-fVRje6x}(DTr?8T?#XX+06y&6epFj) zLWpzG`^z$09Vw!x&|}3~TDi_e6e|Pl1puWb`uQC(jhw6EH`^(No&rT$8-ozRuRUy0 z1S8oag53lWM90uhz! zh|R>vmzG{D$}{T5!@RDLfk?C?y)#OLBpI7rcz6{z`O2532tNv{vxR+P1j^0t=6TqT z^P0Du-(ZGz3#TBoSGP|9iY(4Cex$SS)Jc)SUue6B-Y$`|3L&1onDn`Y6;;9cz&%)Q zJFGX$E-xfPP2kp;V7d8{9@-IYI~9h#D+@oXE-!b%{2Ap>!fE!oQo}!Zu)BL1fk1MS z7>WNNTyIoOca+A`J8F`Fe>4P(h(C3Jy%esqd#J@j#Xb@2FkMUQA0X0hN{lQGYWA6W zZJQAi5>oZ0are=+?!7p@`K25O7%%fh_)b}2gd;RVqGCGbuE_^u2z#>h{{Fr$Xx{n! zRw&xh!pSLSdLTpaib;9SVe?;WQGUl+ep%F&Z*qc;$8AfYnD z*Mfu_v~dJBYCfmj{JFbK4inYz{ia2XWQtW_Nf?} zQ_O%c)tyhvQHGjAH5+SdU33IKKp^mU%**=vfaPmSU6UnMA~s3SIqmp4K$=p?pRYpu zQ>r&YWkq9IQkIZFs8R)Ld^|JxpH-aRvyg(awONO<@7JaPJwm5 z;8agRLBTBHRLfLrEFKdL?H53wRtdQtP8EIC8SAbU70YsS=v!@3MoXJ(Z_s(25 zXzPg3R#{GPao&B2YszBbX@yERQ97ll0__m5z62(o8hU^I%*Yrn10nttL@1w?Z9Oy8 zVLB#jHTjXaKEksI9}y-C6=Ki&&oKeQq2Y`1;ZU@$xKA*piP!V?y=$xX^-0(MAgmpH*bnOvlqj4 zHb3fRQSDjj5D~9P{&DIRP2*f|`KPv;u1`6dc^=JiXlc$k|CcJ~3R~g!+Fv_Z3GqWZ z+ycJe2h}w+7^zht2;j^bY*oK_QO6J=4L}wjFK$vOltt>B2Zzk#-f4xpQAg{5$o7jD zY1nfnFsH^^6LOQ2>JH!G2L;d`qRDc4?_K66svZHa5r@&>W&4_yBcHmma!W4>xY9eq z6468$e#LyK*Jf73eWUB3n|2+)BN=NATR3~p9qiDPfgKySK{G*tYY*Q!-%y5JE-3Uf z@UYQ8;9!Vox}j4+Ceb*5WytP(*ZM_}*txV2H4Tk0TFq}pEx77xlYw7bqmGFY zr?f45`b7y+*6C~WK_}+zzyxku`+NHG` z@H5u+58+vbvS*Ya#q#UOCMq~lfQBzjj zv9!G0&PND6-;OK7d)zLE^G?jo#pv1}v7@`YD=RBmfOCrWw~>e|Fr!yUEt{5>mLuR< zW8;q1gNZ-SGqGdWZa{7tdwaji!kUE*Pbwrp)MTka*?878HADK)H2PG7cj7z|;W@wQghLBEjNS33@#M!N+7jimc{j_BM27)BAU9 zA*1Q_Y-4vFK;!(RTkj8l9cMX7kt|X00bech5oK$XcxFE_l?h7VTpmvcwP|^8%#z7F zAp~nIr)xbjBA(@Vzhfx+g4bd}T;W04MQ>}jKSkKnC8_+0xH`BnMVzdZ5UvtJg^=zz z(s^S%_uqffXVX^azkmNOIRCTz196T9a5)gQeR5e`mF#dkfs-)@*X{3E!{rfTt#*xPUMJ3tPCW3aqU)!)y6^G+T4SPzZOsV{y!F!K z{g5ZGvPIFMx|bFk=>h6;p3recoNUhp+Hb&D^D-<74&^eR_p}c)LD68~Y(9Woh%sRL zsxAes7})hjhn{}6TDGE#jR}7HWqP`GH2bF2&__(L{af2;kaH?f*E&-Y@M}P;wIJhe zLcEeD|F{{Lv;b|*iXBv%Tins=YWZ&6<;8n-VBpqBgT2nrl)D-rjBixAy30pNdSdf8jq^rO z8NFf^nY%00tZ#lmRaF{oizpd7#ncJ>*h`9(ohRAySPUS{!O-u&y?!Sb z!b?l2OCn(hIF$NN^XQ-T~`BkS)i(`4-LShhWl1G5;-ldO5r9$0j1pMDQ;F}Q!2ytO!e2no z90+~t900MndCGBy#Zr?BLWSrJ!{Efi8KbbU13?HlV$s^!nnD$E$;x4enTT)We1=#H>IhjmN?980^Ij>KXZE})W=dukuc-FzC3fn= zKIL!Q+ppY`7uUTX1Pj}t0?U2qtbsCMyU7tO|AWRxzOPNi0^8rtbZPk=qLjmt^c1_PCkiO? zrSXr+L`id#l)m}O;MPH#I}eL=wc9_K@&oe+$F)Y8{z-^n(Fq2C{{bsCpbkT84uSYC zeq9#WLJUn?-`cFq;{Gm~Z-S)e!xS4cn%!E4UB7-~$Q#go(r>D`ih7n)X zM$@it`{ywSHVe;@_P}YSrKJm->scT%Si;aBnuh%c{lB)tgMxxs0?Sw~N<&yf-@B?z z)5{(E=P3Z0dL9o`Zao$OJWXJJo7~E*zbt|C3ouU{rpUG|y!N)V4RkzE0q*1(BjKQ8 z8gv0JO2;wyQ%Pn+We2*vb*^NoBU%mqmOXmHm@|7tZ0HsgRV@=MPp+pGE$NISujYHGmj%|Pixcx$JprfZ08celO)m2o0oyIPyvLepC4DBd5{E85F;eC|>gw8MWIc{;5e13W%T9D|B) zU)KecXxnb14qY9GWvP*jPvq2i&IrgnVtk%cRgM*PBdPGL#orVG`3KiWfG%fWmx`GW z7I|e&Ar`{PnIav$M!It1N0oCMPDRm^R{DpjTxKdEe0Hm#HJ zG_PLW=${6Fvd7zcYVl^vRRp-uvo!OjfFHGLw8(oSqoPhc zhus5{=Qie;fT*Y_o=DxfeuTJ+BUpvh@VeRnyA14mP?iD8vt4}ALE)hqZZamnqVDlk zeCegIb&->klb*x_m~UmqmED7bG8d$f0+R<59~Hf|fEva}3-%hQ&rJXI{4u#5^9>2B zY42TY*V-3L!;T}n(X7Q-tvjy$@Yv+X!@mPachyE5ns{?g^f5)D0M~ccc|zdyEwp3f zjep&L`$9kc2R3 zoV1(DTyNA(cj(jopS5t=V0kXhpNXS&A+&IeCoue@fsf#gXc8gZp`sd+&uHY8cA_Rz zqJfi%)Dw6DC%hnn0Y1)Yu=<|(*2THBkx{oSYJ+Z*i7RS=_!jtAg4I?S`6F4Opj+lQ jf;WBoEwka3z^Q0m^IzrolG7^cZ$ZGs$P7_q;Qa7^A(6KO diff --git a/dist/icons/overlay/controller_dual_joycon.png b/dist/icons/overlay/controller_dual_joycon.png index 8e8b5ad417f9498bf2d3a158fb960671bdaec021..286b8d8aabb45985d8952422b62a977e27b8eb2a 100644 GIT binary patch literal 3475 zcmZ{nc{G%5AIDK>24ReSD-4p%EiFPR*@m$zWYA6ay{tX9m|LO92=mB3gC^NZQARQ} zlumaApS1sY$HQK~oW;OLn=#-?UCjTP*zUuM^qEYx~R> zwa_EuWX)PjyPxD^8^MT^y(UJLJ@b~7!o~fE(jtx2yL@wL*#sD*IB~dFxHynRwmqEs z-p6RkLPG&pCp4v;_-XIQ59AtqDYiVi>!*ew_=_W(9qi$hu%9K=(ACMm4=WwG+Hulc z&XL7a*x3o~$6|(7`Vz0gFe^{5$~?ylSrZ9H>=7-@K9*Pxt&?BpW|yNvm)+9+i$*hD z4#W<+Qiz+E<&jO4!GtEZknsd`gjFU+9>8HVZ+58|+FR{^#;24@0wkTfm#Yq5Vx=HF zG_iqu^K>E(Kiic}>%LB6j8PsfaQA+Q8WP3dm=%vesyi4o>gDjnmo?=x11^}X3oy6zW82IpA z7Fu~@F}zmL*hu19MIV;oO@~i2Du9!(51y&>S-c*BtoCmzQB(L1Ky#5gdG<0?fdYX; zjKB2XsL%4Q%a3HBB1+xF7^MwKqB^di)VRFk*kgWIjt1@LVUOxw$FBT962|3BMd8)jKiSYak@POah_E4uMBEuHX>D?5= zpU`mz(;w*Mvd($-jjHmAnTNxiiQXfl+O`mz2=U6iZ%QnJe=M%%5_ zoY>m77QtU^p#(c4GnUn(M2~HH5=9F2UA%sG;O|18HGg0z65fwPvwGJE4^{2p3V^%i=t0i)8) zuNRb5vq7qkt`b&Q9-|;r0Ufigb30&6e;LYV;df0-eV|L{&+D|PEXCq~ELCOlGI@XiMszY20oAV+C z8#)kuFHGL-;vm%PM|i^Hck1SWe42BHqRJ>sDIUgbEj3LQs{K_bfKh9`#?@`a!kbT^ zLAih8HwfrEG7)#~27S>`!{{<5%yGxe{_SiZ^&_e)^>-t7_{9I}9QL z8Qx=3Xn-nUTkG|r!eA)oUHA}+vSn08TaFH243Sv1N`P5_2xG=V?4d%o2dCvYHKj}U zO-xU0WOOv@G)OS-Sgz>1CxhG8Z1 zadk8=>;ld0s0Y*i{UOy%<m%Q|aQivaKlv|c-RrwJ+lup|eazeB{2Q**R>0R~i z-#~p+$(?OO(!Gf9sL%vLsObCb`(EKkp_Lo#p$EC5B(Ie0)8XOeKvTSU&yn!$ z4cPq(W%y?MsREM%>R+`kd6uhIa3>F$v1T8{(3IIL3z;1`WvT_}jfbsfx79i-_}g>O z><;6hb?r7IuW)M8j zO;Z@E0Fdc4Zh^Dl|5=FsSNGyt^3)Vt#fC-X_dUcU%lm!>4?r2|G;B~yEaFZzhrvVGNrp(c_VV!P|IDg)Op zGZp$FIR@V%{LyP_zjnVXx0gf|LjDnMkKWi`rDLbH-ZeLsW_kZPq`YeG#lkPAiJal{ z1+EYJM|-g{aFTJp<#)E6v>)ZSGh*PF*zrH*JNpr^dP*Q@K=M zlD7<691p=)0B<}Wmi&&)aViPR`5LtK-C@VVh($K8CQGE#qUbo8BO=Eap0*zNVf^?; z3^Le+nEml6ivN^*F48ZfG;1RHX-SKcJL?st=!5jc5%%RWPnE4zt&p@wm81DBCBJj< z@!eaoVPqf!KbSG3!X8NP9(G$s%2mnpkEh|FpLLt!$sz}#4D>}V+mi?vWFH*zJyMrN z`DueOFrj96F`S63Sf%LQxW}`5b?p#ekI-0tlQwH;6r59J>McrpIRnesjh;p^8hIyg zZKu$j#p%C2R65^o$2zUuD8|o)EU1n;pHwCRo9R38QHz|8t(kSV_KG;44oWo~`ix%s zti5wEFFMN8i4b;RbaoCo6TXQ($Tbg#ZpM4k#@i-uopiF1gkNbeP3;@^zEWbEI{X7+ z9xiUz7_;_|A>8S`6vcAt>N!J2X^TYK6Jh$hk&l4a6A>hObqjN8;uJ$-$mKPq#XZkx zy~VB}v+$wu9p)))Ldiv==s4ZKb&RZ^HHBUb@S3AEc<>k207PbMC7He# zZ!M>E>bbAaj_&#Q!?tm!Y?cX2>T5f3aaYjP(w+$)Om^-aVR`!`K67wV&u*Dk)vBnc zYFFj&EslH1pewc_W4(27ftSVoVCmLcD(24cseCBr^2AP`*MO9QvRD7zy8sFvoTetw z;VT{&L!&Yew@z;ijf~!l2NkOS#;7ccMfv@S_1vKaYuU5sdj$K==>4wQOn0sb^~H%4 z>LBS;AN+@py%kqCD}545x@)~CNX+-G)rR8Ft8|in!txGpd={1elvn>=`blRp==iaJ z6FCFyHqm@e`Nm- zb}#G5Idn`aoaP_{mnh?3-&fBR1eEt?)24;WI{t?cWs~Mxv4*a8Gzm>dqP`}BZ>wK@ zyY0C~N}nbciJaLsA>Fyw;G+v2xK3l_%p)6N$ZBD@kAxbcJ&QK2dDG_8;K%cFY{})X zEzDYpjX6{<3xMZk0V^%9XOD9v1Zpf5qK}o(rZpI`Qws?kB4KeDk;S$FHL;lONQKr^ zF5i$Y`4gUSCO2zE-3~dj$m)Ygzt=JCC<3kH`@sy_o$jQ4x#LOP-mEy7N9rBSVlFyr zB>Kt1Tl)(fi8!c9*~$#i*(e%)yVzUrqB0~l(#xD5fjSdKwzb-KZUzTc?n$T^_0^aP zC=NyY*1@5beg&$1f$XAOIiNjA{ueM4P@Udfm>QUf`JIAZX$x^@1b5CmD>ft98baFw z7jKE-pJu3H&o{O05;NbqOS>d#c!CEE;p4iD;iri+=j*|TnYRc$_bd!8h}ee#EFKtd zTR!a);W|oq^;0q7@GD(}fQ>vUPZj%?mj4MFIm0%@6=OnAAGe0*>6?XURHS)DSXmWMK{v(| zvVn{r_tqs z-@*>^m>A*A2ZsJgT9p?FX2!XS2Fz-x8N$<|!cR&wpL;vK!{Pjm11`7}NM4#%XF2Vp z^sVYrQ*#DNpLNU$h2F-#n4ts{3HO%489)8!f^_muU+C literal 7312 zcmcJUXH*ki)V3$|DnyVfqCpU(B_JpW2pW`L6MC;oq((qGh(aiWfK&mc2^p$j=p{;3 zBs`BarAv!+l!q?xj^D5M$M^T0wPt0_tXXr;*=Nt**L^3>$lxA3ivSA%0PNaYXcGW{ z@%=rS7@#|`Ny~}Q4;}utHiij0LYZ6$&^5DfD z3Uu^y1p)&DWgd9p@Xn4tt}@!-i>)!@e2@47ePB;3_-@lC_cDrC5Tz<`U z_a~y$;XlyLAZq#licLJC__Wh#GzNik^U*$A`uEg@not>g38s|#YLJO%A zHgYL!%``-3n4E;_QMq4v%;>WJ4K|XT6V6mYa)(*4&h9_{S|Iih!)#jn=FHU8RM_Lu zPe%2i403dwgSL?#;N8D}9Sn9NVuHK&ju$Pdx4#n`$&d5L94n6%BW0r+@*Zs3m4kcx z?Gaw1aYl+)uOjoQ16(OBKyIdy+HmMn*~-r7c(Q9AQ%Ut2O<$v~N zk|iq?@rY7z(?EpZgSikix5k;fixODTKlx^vVomCYSW z)8?h7B#+^7JL{|IUcXeTMW9iY&a z)lqvz+ME4#jDyPHO)c!LEUet{*OPY*4GmQ})u!tKSCKtg9FDa@2;=c-q@0{zz^JxS ztDOW=RaNx}T#2pKi?NzMr5$t1`7BBA_VD4_G4M?^zjk>2=p_E{ds|H$Fgu2|dQX`E zOnRkyj+P)GW%d5PT4aN3E~_im9Pr{zaimU`p_YuWwo#uv!zN5Ai;tk#7K#h#D#?hx zVF$)1Cd@A_db$v09@1}(Ir4?`wrt)C(+mvUD~yYaE7!a{(4S#Wdjn3kmFOvc$_H?! z$k!P>HQ9TQTFOGQRrj*Jn&4n(_q?$`8*K>N%DcwJ1)tXUIP2z7C$+)yj}-rpSHJKlDE=21;VA5Quy~uRfuk_f* zXDbM}PTQ#TdeE9Wc?I}SG{-T)hjJ`&RSMa@xmV_qv+DnFY@CiQQi-|O2-_yb)*0H= z?P~t)_9eE?EmbgBCejl{9rHsk9OqM|i4FwgkwMXnfARq9co=2O~R|e&A7s! zX~6r@Ple*m+Sp{;EzCNR^gfp`IXW`pe_x5w8Yg@2uqC*bd<_=8FshgfwqK7gw4Teo z7GGFkLVQFM={}d+K;L#LvtRY)Ak{r|@c#1G62_zU2$9I-w^?InCS^*QFYi7nBG?GT z{K(V(Vk^ZHkv>Qr^rsxtxo%YJhdgS;Z%VM{DOi&ma4H zloLY5nV!6=IkNuOJS2ZF*!fF37<~7O?{(V!ek7Z@^WF|gQgzT;9AQuIWAeq978JtwX((UF!`J?;P;PNL)n(!BN6rcY)BCXUsii zy79}jW6gJxy%`l>SbN%DBrd>}aw>So#zWngH%oYTZ!gm+A8!Wc^i6@mX8Qflh#!qA zM5|D5=NdIQ|1d`O;^K6lON`QpAUb_1<{Fdt>Ikn&e*OBz2%fiE<#$u?Jx7+sCF-(^ zZ^&nz3U7zH`cgbUBfK(nO1@@z^u+aPu{$>KvOJqHSE|ow9Dd^@jifZ`vFOQUhFnT$ zwDj=yzEzgv3tIK-KE;Ow2k%z;N!i;y)bO=OtaDLA&)sigY)(Xt)W5Vb)17?x?j1M> zdKgOk5>ee&4j58JCUtHpSGNvsV#}GbhgF99pceoQ16ujo%DTex84NM2svVrW)YN=iyOW~UO>Rl>j>7^M{8@p|VDDm&@j=mn?BHs)U&xc~00udj1h;bu@nu!GR>sLi8tm<_kHjR(@VV=e2I zygOwXIFVioa(AgzWvHC$*m1zNOIB{X_fa z9{LUV?U#Et1FV7pDTSE8?jZH-4BA-hc;gm)6wO!Hz14)tB;Ua{ zNhp@)*bQf!wo>fQNAdWLZ=HMX*@INx8`P?elHEYs@6xukakS~-d6>54fS{IlCXpl0v_EjQeHcC!zv zc>xqv%n#GjmUp885K6v|>znCGw=M_kHOwUycMHdhNsIS*BacF7PEY2#6WVkbtZ>N? zSkb0UttJ~@ZW*0>aIVPd_L;=OpWk&D!Ie<8`P=DQB(mPN7~n0}6Z%z&5x0&9 z?0}lnVe>P9@HZWb91w_A)7DeYPxj1&V(HjN6BSfwL~+gb8tosC=tTEjYycPFm1b(**B&m|lY`XIcPj>PQNixn=o>?rOy zQ<~&sV`GK$PGqlpj=vWow*ebFVW&FSHX*XGYw9KVh*W}n7L=m1$8Hh9i2O$e=$DD_S-*@!DP88G&aHWiVN3n?!9T*S zbhBf?D9vM*G@J`sLEE4GF51jo-T-T!p!MB$izp=m9@4^_Owk+6PiC8zXVxG!;`y!S z$8#<&uG09vWgv<6=0%fdU#GwO+A7Q^qE^56pcDt=``oDmul5!daHFj%LmGdR7Z7~g z-u?~hvBjt*6Ziq^@Yp;%ruVB_G=HUAE=bu=P(OFjldFVRQ@(LyKeU84p?l65kog^U zx(c*cg6Gyz29**ch6w89ldG{8;5bC3`)%wk?wB9+BixzGPcHJU{rqXc@CNL+^^hh) zq^d;xTp;PJUrc(5*{KkZZ$CTsG&(w3*5BWMhq&_GcR+hUVjZ_^3iUUOPm!T&9RQi1 zd2xtE2BHH>H8j=N{qjmPQV+M=sAR#znhqeJF%TNub{+F~PJ?YuFLP9Zw? zjIU+%FH-2YCMy_CbwAH}kv}`Vdz?oN5(>FlR$5v*Wp$(g)ykLVb>F=b4=lcF#Kgqp zbEfpCCEa;0Q9kjU%I|z(E9~?#P}{%PwcXl~oRl=pI4djHS1D1p(!s;>tape&@|>uT zpxq1-AgGaLfW*2Z=&D z5Nq3ONq&2)|EfMDKk(e3%YAdw$?c@_l`+`)n-M2}l8|g1ES)v9WNY+-E>NF|>iq$j z#q~V{DCnSWS8j4T*j!OwUM|Ac8Qt96telAIhlQRaKNdZOgUzd!n4+V>d%|Il@HsGi z)8yVz;=ShuEAo8UDLMx%x8|Le(EewR`^d;x)xo29)Y5L2@MkOU#31tmBMT0|m>c$Z zv8sm_xX?%4Hor;rV%+NCn^#Qzwka_1)=0c!PsKZ$Af^3}=i`O=euF~s z@t?UjFOYk#&abVYfGca_GV&7KERIBs@)>nY94F!{X7f^+hnQ?^Jd?NOkO3%-rqM9w1LO1MnBC#E6|HEb=DO2 zb`^;?8=}L_G%J=j5(>)-m7ZMOzZCgFU|umdXnV=k#DPq8yw7`%mk&<#KzW15uF{}@ z@SVxKMb6I7+5gF#VRv5?03FU&f8LWs@?a8~vU}>q)kW~l)tR??r07@XQ1m%y}eu|_} zD4jsAEwr5J#iMY|kjwsofwDAPlTIC3h1v92r3Ltq_iMeVUEthZRRjI;Hcytm1Yn6CzF>K+XRHBM|^yojwnLYFM9~B=V)%+C--7y%fAUgps#f zO>1;d!otFoaO)4Ymj*(#urq@A$-8);kdUK$sb^~cifGJEZSlO3^I4qd`Be^U%#H^9 zJKO9vKP^?#&mo*NYsxaL|6bL#KuY07pK>i4t6YT)FTU9NJ~Hd)OAj6dZct@OHsgUsA+! zkX-|zm4j^WxNDYOO@DKGUH@j}NIq3aP}6vAlNs_oQZc>-OD{&i(?c7XF(JASGkUTz z0H@}^Mtm{Cw6h6XFO9T~Wj}`l?ar09^G7Gl>1TFTsyp|NS?}GuH%7akwm($l_Hx&4 zi6h2wKTU^$PGbv?TW$@QL6V3*078igj;K&xTD$_;H&2dED=VKXTMbet`1) zhd+l0>H%*SXr)T%+ry^Qi`hwH{av9=GOr|Q0;^d6I+U9YcF%Z3MDy}y?#&ykXrgb4 zl>>Ugd?XQ1Io7${RG!6S5&c~dR>Mbjf=8_^*lVUn%nU1kwTmW}O`KlCl03xC6DmG% zmdK1i3ZuH(Ef*Ctc7kRXUzg7c#K>!5%LVSCzw5M+b88{%Cw^Be$K&4@_aoAH?4vK~ z=#A5hxhFQgoiyWUYisia57~jm`j|47F$Y3wN_RPpH&@E$;`V|0`udy2{>)8z$c&)1 zYFwl-F6v*gEtb#2N9R-XriO-w)Q$!JzMy@xdK_|SIswWj@mq*^jd6}~{V@qd1Eh+* zphgbIP8EWT&l3}+x?TDi5gtAe7YNx^f>&t1t4?-n+7-IgXv_a^j3CA;@agm@0)YvA z$VlEid@P|)4(R{D!K1^>s<-|9D&qCu;YMC5dX9VAZ~P@3s3w;J!;@1<_Z6j(1Fl7= zrT4cBYQo>r#{ccCl)BLqI`Z|DEz_^Vc*CA|isA^G<_P1%lZ$ zFxX-;2tINo`W4SC%<<-z{W;E*0)ZIG$lEqeociXQ^f8}Jv5r$3*oLljrhgwvA@$X< z_NMdPlQ~b%exMmT-;CqK0bR}B3jdyg*6WD%_V&iI?m~!c0mSy?a=-zeEH~dSsOFqh z8!{ywP>VTAc|68q@*Df?{kQxk+!)C0s(LLW!yj4V#Id+#^_uu1VT7@k{c zCKI$YS6R|8blfSr^{FMAaJ~fGJ8D1u^|x~EK1$p?x@V2^#*W~Os2o-%^X03;NUUne zkB`r0#vzCH=fmFJot?$_ZbaZEV2QZm3W=o5;QYX3lv^9j9GGE_JD9$$B9ZbJN~EN}7?75}5{AC6pUBLit1QKE+zEs8p>s$;5G zg4tsT0)qb^{Nw|iA*1L$Bi8?C(V-L23R#2bkQ(V#y0QRBO%g-vf-i-4D)^!F5 z+`w-c6L~m1d`kgFJKIqU-3d)?kU5t5`}AFG|2Xptao5#agGdkeLh#pR*SO61R8a)&&h+(8Cx0k3xz$ zi(Y}zV+Qhb{-sp@st?1A*fvQf8nq7-=m3u!u8B=F(yiZgvyVbYnT=W@-w`sExKo7P z)Ah_A$6Sb}e7Sb1~be&8ZAO3|@emjJ#A@&w5U-f$46w8;bt*xT_|C0DV ze}U*a&?S$q+dEmm1NsU@0XrQfUJrc(v|0}2$tJ4uWEB(?Tmk;zKR3C=;E>YG~@;=31$N{ zWv0+G20u1; zy<;P*w=TOxXKKYUKSy~lX6vqkAQ>Px`>)oQt$YFxEBUqy#4aHM2eRd4%&6f*Z&k6*xL&m)p9KkqpXMW?h~ITLmWpB& zioxEej{8A1jhs$bk>mxTw-=cVOh#B?D(A*pAIu`W2l8pR{yo^Hb|}RcX3VVSo?NV_ zk=KL(F@Tb*#HzV)78vYRi9j^=S(8wJJx|JYs6jr9ii(P>;3_;c84~!S$H2&FPvdj& zwM4i2RaRD`1y&ZQxfo~RJIk-voo_r6(ZA?teh#AjI)j)Ma|FFx>|d~!kL(W)2{{dN zaani-@WL!o9@*$T?Of1}w-*)?YV(}$c`n@)%Yzk5xBri5N*w{rs8EAS*D2wG<%%In zX>O=IKffE3sUXCm`X;t2mI$NkR{w09X!Gga7P{Gopj0c}kKbk%K#C;tNxZPB~{ diff --git a/dist/icons/overlay/controller_dual_joycon_dark.png b/dist/icons/overlay/controller_dual_joycon_dark.png index 63e03eb4e0f1c692864c6af98aa0d349c83dc3a5..3fba546186e15b63cae4386fc53c09658db953e2 100644 GIT binary patch literal 3107 zcmb7_c~nyS7RPbG0ZCF(F*VZ?HNlRQdEPr@ zN$xy`5+}t>1SceNhPn?0e-xdb__PP}5fK%JZ|NAH5a~T5bx{TBt=kK=3_7fTdQZ$P zZ@Ra+>g#-CzVR04%&&yJ5ez>}5N$<1Em+#6_ogRX-{8uW6ghCK!dhnfQ#cwuYBo?4 zKG6H-JuB)u&WSbF7^S#71s!7T9--fQz;mtV>R3k;C8gWIdTW z1i;!l?~#oX2S~7onRINcS;Bah0!{@ky~pn?Iji&ALjg@OloDRlVVVs{J_wZ$vJSFO z%cv6-@S2rXbeVqvjVzgJmzi>t<}^Jt#lf#^*(=+}(vORFdN`Or;=vt!Bp7}$ODfl; zBubw?NauIsl;K5v*MckVolrqjh|6(zXL>g@fNMlA(+8ae!GJ&ZlI`-Rg=)~~mmqy0 zyM+pj-K}{b@7L`K0zN58)m=lXLFZXlI|*t`6)q%Ol(}QtK<7j4GRB4Y&(6$EJ=t{m zId`eig`QAimyDj$_Z%$!WZyZ#*o(=$CKRAt-W~#^q9@MPxt=|@Ceu9%bSqVYrZCow zgRcg86sf|`%m%JYq^XnU=I1|F3!c7GNhv)WxzR=i5`Vl>;S`>Ybh5FLnlde1S7CSX zi8Ong?4{KL!=4c-7L{UunWi*wqKt2YMz$o?U~==+-IVQ(H;V}sqb-DYsA`(8S{-}cHd5O zv-N4UdJk=DSu3yquAu1?IAz$~4r}G@y7_e@8=S zSzlu9f;mH$(}M_?Opa_Cq1Azm%A12dO=b*PamI|rm+48DZLY)VeLLk$H+g|YJc+w{ zcjYhEw`Vv%0@GZc)zSrqRhqTJk?W%H??&U`I+LqY`e&O|IA>nADA~R6HnM;E%6Z+; zI=Ifm07;hLaDyI&F21gIgi===quFiNC^-ZVdS-S+a&9tELt>@D<<6fvN+5Gv#_?m} z<)MHQ4qesryXig<#4SEjND${gW;}gFq`8lu46$6h6JR49V&@7&ftRc*^EQs!3pO}0 zRdAx)AJ4?Uut2(Qv5!DgGE(@OO>p55ak1uS9=xm>+$QTHh^rqn(nQ4ewx3(q8z~{` zi!?ZR1MC^*o5hGUlw*r_4I`~ML!6r4o398>7>WWc1i~P zgH(Wa+P{P_b+$Ou>TkRa+r1&has4)n8gORIq_!B-_zskeHw@HDl@FsSeMXMmq<8!+ zy2b)zH%Ie+@)mzI<*&Tp5XBb?x<(YYXr-+MR!#wcC+bocvqyQOGqXmIF^~)uac#}z z5>mm(n*K}zkGM$CLR0LjlC!)bQ?W#zDu+J8d4k}N2F=BLb*%b6KKJ(hDg^e~^Kf3x zmxHqNaVEd{b;=!ehRDRY8jQC(g1;9pY&@oB{x|5)p{9E}z{S(*!2?Av#;5=S`!K@P z5VJ1;K-4os=2mAYWikSQ_KOM=D$f^jmKeCstZVDkDb}#eg<=dzu?N?ZX$S50=0fT! zh|Y($|8F7K2(8b=N&Z!EHOJs3A^TKZATW&`2!j0HO`?v1BPJN?(DBXj*#7#|%ibbj7K znB~&(^PpaescVa6xA^d95)~kS?V_?C!5OzMf5mC5e%4w^-MtPfYwtAIVbL&;Ug^>b zDke*_&PGOisR}6~4;h4lhx+muZ zqc@FZfWF*Nn(n166ksJ;_jHOF@ox?nR6oeasnXUT8{GX6Z$uf(H_D-@UO=ZlS`Gqq z1X{h?d5p3LcPBLO$j+T`i`t!#bLYp#C*beMu^%i;1U6D>AUmBLFBAzBMT{~l8?sNi z{XBaET=+V7#&_Em)1{KZCJNJd)IdLw!I1S4ZA;^VaOfnq9tD-cPb;nle~%Lvw|q|u zx`ktdh?WCmP8Hf{!NV5|J}adCdfXb(Z5t~ugM26)dZ@>wj8gTuv@Ubh;HEa2OWI`Z zo|2JjcyBwN$r&|h3t%wPhKaV!I8>NrB?Pa%*US;F)PC-*!Jxf> z-E&eBxPvB5EA@04BA)q@SyQ+kr!{Zt!R=`lduz5Y^Y4a_9IR!VAO#IKdHnrqRG`%@ zNXQ`tpzX$GOS(G|+quljd3G$)Fh3hvMq z{4h|Cgx1NisT)=ZmDt}M2*q>QCMGNFj+X+ZIR*ofFet{CCBy$>v~D{W_ty4zp~fw` z)f zOFG$bi91?rqatF zt@7rwlDx*4x8$cFr>Uv*n zLFF1M@T|7!AEQaOtJafS><8VaxLv>j&R`B$Z^i5^o8Jm71lTy6NY6Zd^G zWoGOI4y0T~ago8$n~vhnwIwh+3H%(e^YOv{VlP)$fvO7|d=PnCMt6@VZ_K+UKqdNk z>eM4O9ru6AWxT)9CNPovbB{iIB0)K__e}y<0>RBvbyQsc(FanA{ z2&ewcmu3GNPUv^~5akwmjxoOZwtBnnH8dqMP19jNt*JFayLU+LrQ4R!zrn8L*l6q= z{~%l^tWqpUmG9dgm^fL^q_trwIGrX(+pFbdM15V7Nc20W9WK1F!7H?Xeqx)bw5c4Z zDLrzo%i>E+DMNjDmRhv#_;doEtf1krm)M~t{v+ac<`W)qa);bw5@KUf;iYIHNF{PsZ33SA%u{R6I8103{Rb6P` z7~MP9aia4(Hw0CeHr;+0ot?1AegAH94P`pX#%zY(z}Fy zR73@Y*MOlzq>7k-`t^=?%@!KKv>cnXsJ!OWqsiYOFJ?aEIGA%-I2fxFZz}*VsnuN9AtTafMjWE< zm?NOi4~^WbPU<3(r}ow-1@ix0Y;O#9Zu?2!kLz1}kx*H>@T_S+;*00%ZYvh||3bEh zn}i@~_0TnaMhe&knuAxtTJVQRLI>}#KB}O^z-~;@7Say6#$ogFXsTUnT-~mTM{!GXqrA~oAah077VZCtYi;P1a@++ z*PL?2lhW(uLyuOQR!8O6yw}o0Rrcj~YGRCFu5*1a<{TI(hA|@m@vL{pO}1pO@(eAF zvPiCE$Fa%2XiQ8>35sN#qZsA7lTWD!7WNvtKY3XW*7NiffP9RzSQQiPdB1;xxNah{ z)fS^MWwUl4Vmy`L<}?}5hT%v9EA)D_!E{hoG43@%_mx4Rs5mA=DWBVm?8H0t11$27@DEKf>O#SC61<2^3ABYyF$g=0Kt?Yd$thFCH^Ks|7QsMON; z;rECzLm%@b^I`ZXX$z=4#ZE4QbwFQfC?oO2`=A#1h)nOzPo8t8f|e(tRLC0SNeWJx z@qkV_BXu4r#%|4hvMHayrok z#2H0orV)3Ug%LsX31qwMBtlA@El(xTV+CCv<0qQ!GDfr?%1Go-;|50xlm~7mscDkw z9Py_kkzcM=SMYfKD>+qnPm(Dxj|aOWm3(1=JZm4vF>aCOEWT~n_De%CDVsmX0%us5 zo{tO5KHj{C@+>TaIN%YYT5-v|<<3qB_WH#A;nQO>(IG2}V~;!Ho?E=&5}|uoF*s4rdVM!DmBV6uu4qA>_(W{k6sH3puK9ZdTE$tqGX#0h+Tbv z7ZI0fn2PQE)A)LwZw*;}3Ui`J{R^IZ#9*9zCjz7tMU@2R;N0 z@L`X~EIQl3U@)fvH{Xa!(Ha~*-qQl*6J^`A=rox@iX(1u3c%wXZn$WSRCKwyJd?-j z82|MD*Gn96qCPehZ%pa4i^%ZY4RU`X1%1WZ>X(&dJ9_|N5;?87{H3^4Z%632}cz;}LFS={JS<=tl>12@jxzv zXK!wr*P%bHmqX7{GXHVj;a#kp%LdLjiX{Qzq-9G#eV1`|MgrL-WaHP&%UiDYV%Z$x z&zoyfcEA>e>D$^Gk_a6Vl4$7fLx5Cpj!%y6UcEqeNska%tNG13e`9mHryuc3r#=9B zN~xjfJv$l+v=0ow6(sc~7j!1ab9iy}LJ#=L%{&;rl_3vLnv&99vZ$T~Ekdp6T_J^E(R;*}c=cAd zOOp1fz$AmR`8`wjvwiy>*m+AyLRT3jEy9Z1wD^*sl}BR=RY)=4LD%w_sZCE=ed@UN z)#YU6KJ9e9sAmXQmL5RbKhv)><_0D??^ zW^B@<1YL3$cMA_sx7*Wj$j*E&>5FH*nCJY(vY#{En;V>psD|*+cFNYM@j((eczD{5 zU7f7EBAemjU<4m-zDuPibgOHQsVs+kKc*){|2*lpou)_hRB8(k>Eerz@L!rtY2k+~19$m*e`#{d#`9 zh!nGedk;u^_qGD1#6w-=T^PQyS*3%qe=?{q#(+O99GyJ{0p= z;a?=`&Z%GJD6t$jQ#p)JlPR9WbfFN4;LXW}(r=t~@}XR76+D$~Y4kDfRX?wBKkyPF zZ1L#SZWL2%;WDjK0Ea9ql^PEyl}Sq9v|^|_cU_@!k}AZUydVIOKwJb%8vOlJ9Gyw- zNQ0KG#99?)HqQ&u`$rxNE=eKk-WydnJou&nraTJpPjT`$9rGrI1Zw5eo8=d;Fz)zY zBoOn)F7^CV>tQlXENQ;9y-VCsRP*k7_b~TJoWw6Li6JR$Kvc|G^!Qmx@C4oyiQM_A z5S03PyL{lY<)}E7<5#LiT`sR({wKMm$tZ_oH^eB&<%Z^mTU**&b!qzqstn2lA6 zEw8no_)1NkTNg#>KP{EMi~QsR`jA)TBA>NLQ^PN(rqjosrC9nI#6m;fJq~>o$mNEJ zE(M!{o3O}|7LaeOb}N#PNGZmccxU3{nx0<3t1=Ry3nD>Zu&`3sC^N70`b$HJRO;YY zhMoPDlxJBFL%ACLH@P%{;^2IR zMxEmh;$yGM4Ac+zD2$|4*G&bp-Tl3=z2(~Knm88;F>#%;WZvW)=Pr2H|76>AB;`Cd zV$)G|?SpQh){s~WYzg3&TQB%SfD#md9~m3%EzQ8|ZC%4Y0e;PWxFwF*AW9LX6!ZDK z_*##gi^sX#mL_PXvW;nYc!`Q2I@n&pWGaCbHm%6Jfb; zPX(pEJbM@T#T{yGwli!!5AEQLhZ|c-D(En+Cj2zTBEx*fyw@t!>>;M8AR-|MZEW)6 zG5H9D32Vvl2nbFvlJiLTegO8WL{FL&;4v7`(SelV5e zAZIdn5^GYvBG|nF@Aq8NxE(ng!O%m5jk~?`*F|UO(^Yy9-vbI-H zldtaEmqV$HIh{oLKm^zeWLDjzYz!wkY+&sQfiyQxObmRDpN4m2jvF&Rl-@yiz6Haa z-`5;GWJb=s2Gz(#R*C6TX~N_v_F%=f!wtR%$|#vqRnax9ZB2RVLqkDxPzStz+9>t3 zC&U#hXx)s=hLuBGbNVJFKXDeoGv9CM+-u{y*;I)|XxY<_{j%#4{k*I(J4FnEjzKd} zzDn4`YoZKQ%Gl<5IrkF3G{@4p*mrX`7?){GT6$O`Tl-{pnUL0jeB9guVmVOLpQ+1vLlmuSh)@2p?H@VQ9;qshSf8WfW%;i3rfK~kz9X(AcMT^mrh^%e&1X6{ zQ`lI8Fdkp;&Z;*bg@;2H4+lTXV95IHlb`#Zy>tBT-eT!dI-ec;wNUaEfRY8|8Fh4v#D8X2tl8ZubYTKg)0}?9afF zD!Gzx5NZKKiti7_YVE+tt!`wV=`|0umouZ)%5qfVsKS#Dwvbfdu@#m6QZX*$#~%Q4 z2w@u=fi(87j9>ouqEfzMV2|045ic4yd7hqaJ>cS$x|w})>CX6>0$DY!{;U#IK$&Z$ z!=G5-XLgek6ZwEnS^aDi85Y~It>P+2_6xT{?i_$lKBXbfGFM)=u% zO$DZ4MrqsDl^z3sJSw@zk1jq9)=&(*fmwiFe0n2fs{FDcPp^E1`RAELK$#?XAy1I6 zOQX9M%pnJ7)}Jd@w$4>t)TB);v{?1`NtUcY!#&Y^^%+8_>6q~kFKwJGQ*BHiMNnyq z9~_Rg@#moyHz;N=rXVh5st(Ls{`&jD7N1=Nv(iq^xZfyhXm3|v45sCOoS~eswehd0xYvzy+S~_(EL7DY z|H;-zM6yd=OU8&BNSknDc&h?x1;4-m3|bn5XIj1#`QPa!XntD%9F^2l4g8V`y`i_5 zy>)=>RRB3jQMq@k`0}8_Be0iKVn(MRdCqI&{m|8;8X|RBn=vHAV<`2YpIFQjc;8FS zCq9uFbgSFixTobmH)}q1d`kSjWkD%cJr(cPp&^p(zS#*JT4Np#*YbQi#GBkueB}~y z`|>gOY_&J=A_<=_b32o{oho0U1ALFgOdM#30%b$%XicE*+sO(;HM48Hbf z?j<$GKD>YbLeBl{lv(J2w6gGU&DSYniO8JaJaj;=hs~5cNRLjWyd?>h*?#2kt`f_( zdoPms;N0e}&EzE}^aSQNh2n2vbehIg^ybPlU$w8VKQ+XZt&UTQ(?Au-uAasSt*Ns^ zdLZwi7S_$8)P%t|U%!BKO3`;*!?!J{4uAA{n(UZC6#t(7Wr~s|KDo8;L+#=g6c%{c z?TgUs-*F{Gcl+;KTH-oPzyG6!(79{W;~And`m8_Z!=XSY^*WyouNC(4*mJ*3DO*LR zb9P|!33RUcsc*Tpq9vHUgetasMAKQj=bX_;LRN2R5}@=pd77_5osW(x>)k|5oaubv zr4JtRUMd-olex2KLimpeFKqLKYw^?#zM@8B!SMnkZssJbDs#qX8X@Ws!4H6rdAm2w zCt5Esn)5K5td?OblNqjzofB8rG`(CcE5xxg)h4zd!ZJ+y94m^Xs7m4LI7mk-S89iD z=Z7zY3%I|t{8WEF@ln8Kl3}WDj45EuF(7sK&%b)zx1mCaaALUjJUQb%Hs-Ae;;Td} zf9;jGB|_^OkLNZD$GP2_L{&5qVajV6Hchj$7L+|4bckh8x1L8&?+emSq%t{Yb@a#X z1@y0ub8lPAL3F2v#CVN(&rCBw)nyIZ?eFyVPw`}X2B7q-=V7uv`FEZD23cAKVNa?I zv+|5NGWr`8e9c)yx};x_=XL2&Pw)Ge=L7y2%YX!plvl$n)?1th+-s#SWp8dW(tyzL zRSv1}nFczg_{G(3C1q38m(@;ys?JhX5k`ng2CAUNLuuA_@#xYaeo*7TG&^ScCLBqT^Ao1X9$f{kieygXw;i7|#P$sp`!?K;1IUP1C9){j=31kQE@ z?g1Ct0!ZEp(<|fm-Qq^oH6e&~3#{bY-^-(O{9;*Wa<;K*ND*-&KouOmk6Y$iw4i)9 z7FarrXq;!s8VVz^0YEIN;)-2es42>M#tw_0YT~Or3+IqS6|hIa+g&~M>58t9kR)I? zT-kR=SY0!1Qr)am9z2NN(6^pvvmj53nmjg==qW3M>KsS)aZYl1IdyH7hkRl$LU(3) zNc|omsuqIa?GrXmq_;$ip5|a%;}2u`#h4_~DQUzcb6oUj1qeakMNNBx927m9{p(3f zXC{4pAniQmvWSbpYH50&PY3_Xo;U9d1Mwt3VnQk4gqnICffFdnfBzd1hDJy4eKL2( zkR(JAsTQ3zU@JN*Ih0`+|8PpBJCh{;83j%??mTGD8g`!TVQ z78stM{?=d*&}~+>%s;4ts^itfwD6wW7BCDG`nbMbA5Mp}hK!1Tf0t_aH?jJBiEfiK zwBKMAsj!sqoX5>y$JdJKLomw6Falc#e1%YaOXvTGq_+Pw&)kG3{33F)uB#y!GdpzU IS?ulq0u7?|TmS$7 diff --git a/dist/icons/overlay/controller_handheld.png b/dist/icons/overlay/controller_handheld.png index deb375011c85a5f4d615c08e33fcbb97f8532944..38c38c0da4a2167ccb0ca5df842176db3a82c232 100644 GIT binary patch delta 2174 zcmXw52{e>#8y-IfnTWBBE&EpDMOi;Yc4PDtg^5>0NO72uVKgH<6CqoqNNBZhQ(jxq zs4Qb^Wb2y}Vkmq0-v0Cb=RD^*&wW4J_1xEWKj*Gc>KD2tA=o>i$t|ZokHhKG7KR@!$5`O)nUgS>U*d zjXZnBe;xmxxU#!djoU?@cXe^(-IjHk=yDg5%0CuZmi&_ty{xkRu>JP;1Y`QS@=NAB zQdbxu#T(gz5z$Oai|rseGOwja;#dnf3arRkrP;-r-^t#$AUSfEoJtidvtxQPoC-Vh zE*fN-C8ka;v{sJmRI8S7?B5wovc8rTIs?+DyVQR<^Vk8clHZ2A)Me&R?uF$+*s)N1 z)quu>i!OePHb1*Wu0Y>{X@Yi(}h}?^X9f*+FA+>jW}iHHSuvFbz~RV z$sqjZW2M{|NQ8*gv+Nqin~(dZj!Ds0QYgyyc=u3|#jKzyVbVTAu{`xS2D$F|xUO7c zP!`?g|FxQYJM9Dp`JgC`-$W_mT^2bh4PJ3Oy@7-l$GLtjHtr_+{M0W^@*c3kP;PRg zQ51Kzin;sM-s95iSs-?c&#BHUdhv(Y-)>PaPm*V;L=0gpjo{~gwzHK*VNxSjV1VzeE=svt~!@dnu6d~w`Tst1t|G< z79Sk!pmiDR$lBim^9|Jx66Vq~aloDoGV@F4O|820>uz{3c5&LL_@a`?l%Wx{s@_LD zUhA;MnPzx$$bfUTh5#5iSU{Xv2ND+nGmz)UwaKE~)u@x_n!aYgxb}{Om=Z)6)`sev z8{5kHV^}t(n8SlaiQRn1P{5QPT_GNjNFt>~04SBJT?bC(>(`z_N)d|e9%-@d0UKm^ zM)QZt$7H_H%n4lWf62aC$wUD4uoq3Abc47GyO%$m@Rqp|z(0o&bzWID zRM}d-cW`v1H0uHkhAGQ7iffYS12O3|R3c0~EKgpFykQ62`@kW#p?#3fkOFdH9zx#- zZGGr_qZvhp4RGa_H7byzC4r*web{{|T}KLrxVk|98FM_C*~=YuNOuj5rA0krka-iK zy#$$lPlv*_(Qyv5zYo(dtM?g%iT?sI^pI1Nw#ZYHGmUtPU9>kbgQ6&lqO@2Np$bDJ z8S zxG2z85G6e}FFH1T3FXxpU~8=bXHa|snWspYp8K#SIk|D@6wwT2d{}C4{q6I-MShr2 zXbBXe3yebfY3I>R?~FoJg7A@3w8MAdsN^}Q;c`QOVOptcpJZ3`oW-4V9DwQ#)G9ke zs6WUGW5>^jX!`Z!kRHL$?>DwnQ*0YYhEt;{LV&$ZIo@=!BA-g7{cIJXh)D| z*@<6{#@HfpTJ3(Z?a;;v(dIHa@+?QSk#tQge$z>Yy2BJa-9sCD_ALnhU#OQy zuB2vhS%rd9#X=XI>7HINikCUv9H}l5{6G1bmVj3-!fJbC#FLHmWQ#c>(f%fxPpXm$ zdfNDccNHyU`8~iC*4w`d#9%E}MhHPio^xe(DZz>)p9l4_j!(WOc7qsfJnK?e83l;Z z=JEYrz2wuX_o&8N4MRfe8jouFZVqQTH>`@s!F_|c>8dm;On~>EGu&M{%bHR7v#H(YcjsdWUY=XjKbAkKneBkOq3{)LlG8vh#bIya^2slg{13l638nx5 literal 4645 zcmc&&`8!na`#&gTZIKYA8cSiwOvV~5Wb9=R5t=O7#xiBBy+4sXBgPO)Lyba=Z5o3j zDr0Gc219Q`gBgiTO!%I@zkILj`wx7c>w2#9Jm;MIJkL3=`+nW8`=mM8U6K@25CZ@} z5@~ty3IGVm{q3T{;F;8Hb|!e(6?q=%Bnq}TQUCkkd{4Ngdn5ozb^YxE`5ID(!ISb) z=5A4rVW=p~b@UAYgTX)pLxUszu7}@%hM{j3bBz@MU|%`%;yI_=k2oysL$4cmdRC^$ z1N28o%cO$`{B~O}DC}3omgW8Djrj2pqNl=&Mf8u9D6OiIieD-9V&}cau9}HK!vYrT z?^~bqaUdZa#IHbo^FPrakzvUD3R=x6&jZfC|KPv;EYW6vD&>&@K3z>SJvki>P2HHQ;?GR*q3!h9*mwY_C?aGjz*c$PK^7{V$_g}So(XpuHJ)J)wFzhp; ztJOm2Srvf1vz133X}#_5Vl^qX<7^$+G2$rHD$dE zE7?31t@p-f0M7Jj7=QH&sUf1O`=q!{om(aYPV->oYS~Jf8U1#K;*A0W0=_>8LIfis3n;jB+Hh9`?>U;6!jvB&Tc7^faLW$~;Ui<)&p~tH|n@yNz(i z(%5k}6zS~EuT3S;F5?ViV`HW8Q9&-r0(AMszNNm3D}muV$>5hfO&)l$!LQR6?|h~4 zettDrWwuyC*e8_vaA1BkTN{d#=~+>tcyj%b<3*W?L}H#G%wx!38HmiJdK>HO|K^j% zQc_Zmpn1JTC7pCTQAl;D-rgjGcT_4hta{`azSlXV_ES{{J-bhNFySDwU5{LbjJ0G; zO85!Ax6swsZ=qgIwW_Fp%0$v{-T2DwOZfa97phB!VU{Q3FH$_uRyv(h@`mDbjwmU4 z5$n{Pc7|`&1G+=|Qmzj#OBxv(ruW%Rb2AJfN}n!f+M?FG(-7%W@X)b_3k+Las95KZ zTKZB3u9Vpo2k82smh;yN*u@+Zd7H?K%IsGb7O(j%(CM@v)wI}v?-_!`8Behg2;HMe zHZ+6ua*S<+vAT>HUZeO|v-GlqANItSF%n0f)-R3tG__)3VUf@nG+c#OL;f`NrOEp= z#UGdG9JPG?iGJiX`384e6*I8##@->?b`k{I0}(p$yV#;zN>VbUU@h6ATkttk7G|U- zdbeUDZd(Uq%A~iQuFE5o+(MNVtT|)atsG6Ax9<{4UWNbrcH`;Or-|bF z4B_FR(kyx-JGLhaKQM3VO>%-dU=wJP^ro`8)eT40)_HUe5&KrBb%MVAWWcHV^4+ymQN+9}BAp}_;`;GoISQ*<1wpq44M2zty`V%^%B3zZ(M8geW9->#rRFw(kqFHiT(6z-qw$| zf?wOHrI$zJN-tO2vv@u~H{#ru3rP^45h8^2(>gnpD3B>#}-G(#= z^*>uqlJ9(10+5cAz>!*R&*#9Rw)2df1tml-f!4~D15_hJWF1M)Uk2B^vDCFYk`V3!exi5xgMmX$!Sz zo<`K(bn&D>3bh@UJ@Z0MamQaS1z*Ix=WfFod?tv4N_74>m?fbpBQ|dx8~x9!;QQ|R zN_8miTZE$bH?F&;{&0Bm9{N(s`6oY847=TA%nwoc?RxrZNlS6%AJ&WD=(L6@W6Ta( z#(WV6q4@jze{&s*mgo%6CwLuWuc^)nJ;c8m5cSi56`_6)Zay5R>a50J3{e?%|Au1 zo16?)*e$FNSMne^als(IGb3$r8GOc+N+41;&p$eCmk;-35i{qh`sO`is)|-#R8&DT zLR|+ar7o*QdL9=DkQb9kDtOWX{J+76&C|cMC$(_+5e9oC(xI5^oJ^_lgxZgV#cpl>%F*^{S~?>xt10Ki5tkGS zI6#n*gYn--g?KcFvb@w~YNB|-^@ZYQs2I!5HllYC(~sMB(U1SGO!Z_QC#_yxCs94$ z%K1<&68Ej=T*sFG*T88!?raOVV6v7+las36`r6tzkUjY-TY24LcAcPwl7h$Wpt0dV zdS+N@(z8@;&IDz(`tNckUa9fs^S!+VTGTsl?(QzjTdTBC>z!`!-R`n)Q`03k%F~4& z8#0A8{mJ-1z};QBri|dZkfr(emvU<0&RY_Y^Rj?RJTaPY->MqSwT+DQbFEW zN9O?q`LiZIV&TdFEjOJ|Z9f#1LpTVro#3x8WoKvOK^UJAFR3VjN+v_aHX611fY^tV zwN8h}NQ;;SPwekmNkIwT`ts^ncKLTxm91!#nIJ8lveRU1s|v<40+A`%AS5;0#seslK;s$!=7)H3Y`LP4P_V*`7X* zj8I%ooB2JXFy7PTVx&@X1&VL4h_HV&tv|_Nv1oSt`Im&WzH&`J9=F@O%anwrOK{o| zWb4XcO?)q+YW?jGCYoZb>)iSE>(}ShGH*XcAo~3*I>&$CvnwQH&*egb7@ak&cXMpM z>8dK4SG5I7mNeqj=l$YloEXm7nb`xAv3G!OUAYd%o^GDaGrJ{Q;54Q0mJ4iShl)w6VQr1#)3UVE>9dgiCsp*4Xs zmpzHM`hE6e+Uu^0VA?0?+j{>{0Qj;c5@hU4n-HHU#1UI7oRQdr~T_>j} z!t>W|3K$F`dBi8$?*tm`lu2Pz_PweBBe^CfCaJEA4SJx<{fjDjU}W9FK@bCFritUFCfL{sP^*L; z!1@TK5;dC-7!Dr9Hdvp#GdgEcf?38k6fWK z*k;kUv;;bQgljUtQ>Rr`^zL#CzsN2l%=8aE3c$)Bps2DI*YG0WprN&+ zkm#qkH^DXEzB6&J`KLVGT%)1% z*S+L~@bFg!8RXk196lm>{&^UbEjn!~$=101>AqR#Y|JKc)7aaN7hi9plZ9BAm?hpSOkQljammzz4B%HKf63~oD`${kQrVGELN3V!bzY^l<)ZxZDB01Xnm zh0S^$z=4f2iAxI-cq~j6LK>lr#DS2&OUmpw%zi;Lk68Ev_-rWy7?gmG%M>yW?NlOQBg5}?H99O*vw;NZlu}+BZBis zjSj@C zGtiK1y?*FW;DkIq;j&D;BcoR;$O;&HV*-j&ZHST^4b4DHB5|$Gj4)}G3ozHJhJzT` z&wzwyo-8jff9Il~hrG?z3x|M8UsNJOSNCT}*NkH*y&+`0Nu&Vk_4*Qnq%|PX;fc9H z@oWpKN(ppr(8#yK7c&o5_wF$}+)@W7sJCo|7ol1d&}1ABnz%7mVfNx3p1g|5$;rsS zf|PCexdCq}W4>IlcDgP_JE`taWbOT#-$F(IUJL0fK4^Y9s*)HCF(H5W2GG6Q^VXWs zlsHGNlHju^PU3r~r0zaeW(Q}XgnhVZo<}6b^U28c@!<$z*tI7P`kz>}57){qx`hfI z^$M9_{lYM#2^BL3hCzx*Bhb7U8t+us&5V%rrSZU`1ikE;Wzrkov@TP}P@>Zu2iE5> zl@evrB^gB@C=wEq8`Vx=T_riSQSYqIar%TqK4hv={P{r*5yq^q&>i0+@ZbA60RV&O9qEf(`&=zt5`X{Q2h!Z`V$FGEr!=KLzmxvYP>&ryb>UdY<>(uyMn-31GIPHs9ZcEvxAH62m01!t z_B6_))r~Mp%N7N{mHV!*CM*6k{uo2Nb-%OMr_-TnN%R_18o8NyZi94W-motvcPPJ1 z8W}ynYl}*!!&I6iOL6#9OkCR}y5F{jFBvXd73<`D?=*~kdv{mDo2=EYoHh zM>Xe{n>*Cig}*fT-)soCydCNPu!hzuC6KXzX_tOQ{vjNV3M`P0&O5QjUHQSZG*J1ub>t7jSVJ7)#*T7i2cE$Km3`;P1pb)P3VA z+c_f^O&#G^<3Ea{4eOCandqVO_-p)Cx8v*|x@CWD?VV*S;7vi`P?SzuK(T)}d0xY< zEy*{+PC$jRq> z|8fGlGlBPWGB^Igb*hJJuucxI^bfY}ciXL`%3jDvh@sw}4ngU8Cu58oCffDTD#*{s zr>=uDj)}CI)!65PrwJw&TOF}LRM^qq1JHk7oJFIhj1_1*!DL1YFXX#t7%Gm(pr+Pe zu%H!&21!ou&trj4@VXtB(!-Ko3D_`=zP>Gj24d8G%xhrBz%N7XmpP! z9d18(nxT`np2gd{KKq$-fX$NI6S$z04qfv2KM#4Dy z1II2Lfs7w!gBr5dz+FWe;NiJDCQ<$>hO6QQ9P5GLhRqRvYQQ> z;`{z!m90Ry{&=E+AM8bGo$JxEFZ+Zp#pqP#2ieR((c>T>CctIz9TskwmzY z=MnMeU*)emfH>JE=Dz8X_(xMCyEoZVyr5)+xcPH<>tMCgalq7PZ6s# z9;t`$iS6OHF|20ES-oO-Qeij34M;-6q*q&zblL2WC@c3dM2i{r)3qMv6K>2Q7kQg=F1W%oQMInhQNqKIsz%i9r% zvRvsh06+Zg8VUJ9>KAPOR)q}gH1kg^eS$K7=09fDn)4}oW5fY-b$YKEB{uk?K*7F` zx3(^l5uXqCicG=2tY#T&vuC+)K)#G2_v}A#+`_C-ru-4Ssb14-23V8+JCba#2%j4+ z+~pkq2{#)P+w`cT*!FX2y5|}E0ax2Zt6I2j!NHn-OEx65xm)>OK*pV$*Iks7T%*gF zV!tL?aN3M>g`yy4x=U7(8tk1Bf946ru^bz%|F3NAJk$82Y5?@rMpJ{rDYpWl Gy#E75S7q@4 literal 3745 zcmc&%XIE3}5)KIgBoqr5Py(m~I2M{j4jpO2rh3#(Nhpz?(1M{TDiA%2g}Nh%RJ}w! zZkkFH5!uIvhyeuw4J}uq5l94Sp?h<_-al~P_0F1^XFVUDHEU+AnJY)UTomPZ$fHmw zMZneRC<-N|{*TGYNRZ5&i7bheW;g)HDv#~&|8zUAViFF@sj?_; zCQfu3k#c!g)NeXFd7HBpy5$_6#1^O93Ww;ci!G7Eg7RmX0^g+e(zb!ru_sBZqH_a5 zXAJTP0Q7(RRGnkvJIp(i5iP_FQ60akLygMCC}UE5`*S7~!mXolRe%Ukh=jK|plZ!7b%kDRXWahT=)|RzvQ)F5E zFka=gf_(PmNTYxl4D` zs7mRJi9MjWDBF#)m9CdaE0h{Tm0Ptuef@0)UU|-D<*7npy7Z6ixu_2#NDx?%#CqEP zDp6GpxE8M*5xkG&OYtfRFog7kQwZnKVE8YLr6wgJS^r5T8SmHIOP8fnz?#3{8(@De zQimC%oe;917&8oBDrWR~22_}HCPN3FbZ6LA!zCFnnA5rD8%@YsSa`L_q2`uKHPlKyel$F>9EFt z4Pp-_k-(87V3i=K8g{9gP!?(Nbe(vr2sr#X7{EM;mbAXrkBS*-9g)iif zJLV)&Aq*6gA9jyW=RSMLPBU|l@jmj>MML*l&aM#U7wF1t64%H$*sV7q?qKH`S^U%j zE~}ma{hH`+nZ7Xi?J@1L?bLnvATe^xjpmy%78tcfldiQMA3S>>UTO7Gi%=U++HcEH zJVxQB@`aVr_s<32hwBUz6OwiQQWjeb6xh_JLlK;z^D+j;-e?UW-j;1PHhlf#Qk-_| z%6ju)QZ=0V$hX8E_YQ_rLl9dWHT2Fc_QFDaU0+>;$$(I$%W#CNK%z8xFscEK+gAbu5fCBU7S{-{Ic{InGanRTRF?pKm}Lx<7hvS&xg#IVb9P z2#kK3aDW0ca<(D=Xbm!Ps_+UL?KOu6nziX?AX7+K6jntr@;qrnU(5&%Eag8U$lmey zuJ007P7O_rSNZ66W=OTm3VlF3jR#}LAnS^VP#))!W+*Cdr6!xC*E%5`?rG@gP z0OP=+eV1A;eBNRn`SNjk=NA<bZ9e?ns( zVCl|K7fZ;kIRUACs9v#3e^~?_7E8}GYQ=7}W*@4Ii}WXUIF%Rj=ZMaH4dexqOl*xP zL*cV7I&=ME&@r2=2kvkNVx?)(?Ze@SNxdCp7Ux{=T04a6=xE;Zrd598=ERyI+0y#P zhYA&ps`|uD8kWw0E__QX;;R>jhBe3QTz0wY+7qyfZq{Aan?Z33Zw3Yj;jd#4_s&J8 zlLWp5AT*SjPc&0i|KA|i?(T*jU6Zp*yf1^r{A$pV*WlFPPccM|9u$9M64KEDQl+0l z@PNA$IXJ(z8fFNzsYgL&Or6XR+MXP?F1yC6NW4GsHteI@viWht9Y2Ws^S7DpJTjsj zHENft#i^C6!Fzu^%skD8Q)7b|WL$z|D9K1`M(*+c$z?TGjMAFkjDU!ZjEX($%q0Q~ zv_1zsP}z$AR1epFKOTUBX-Qz9JtM96Yo&B_B@(j<|FM#@#o%Q=vG^K1`3@%adym5; z5R61mYnxHXtf%QFX9!yirZ_AY``W*dUue&o zW?>I&G-kCU!hxgZM(RX*okdXvdOF~H2pEW&Kl5Ns7M}beAv>Dp6Yif=ZX$MLE+l^k zDRKAVxJ*-f#wU{9lcp;8mvq`kfgiLo_;b^ANLF7JGQZalWjAH&V<%eCbU*qlnMN&T zR>+9=8m}&zyuP-Q)t^*N%zaF@Ibgr_y)~!ojd`2=J1s!f+TlQ_Au;#f>d(7`t|tNM zrlOIzKq_~SLAL299oh;xD#agK7o->QZ&1x{aN!=mRrHO!r}JqWesxmaB8H^W7KWYf zhqv$S+@+APc-9UlN=7{^;?sBxB2J`)3hKIaO_XYZF(-7|p~X@?GC{94UvZx#OWhz? zJcT#n-**E|(uOCBun%jj!{gU}>nvdjp|C!Jk;lms&_RRO>X)V{4S#ek4gOutNP?Kj zX0CJb%iI%~CXkYr7beDTJ?jkVldhU7;IhnU{!h^?LIp5~K52Jal*hmO$N9#EN8?uT zYoX7vo|J)v*ZJ1P)GQsBO+LVZw|p^}U_?0u>fE>76!meUx$!2Y9_g>)7Ztf|Mrg zMr?9KsxuS|;q&-gd6TN+t|SMpwU=bSTKlXG%yv8UF8NUi+1c2`#c|$fU#SjEYabpu zrN#087%IrG?VTrKN=e`#XLW6na0C7O`)aPH#x#XJu@I?w6^8;BHgkOT||S;tM>u+tBMz-qdc{}SREDB;8NB1^9%%m*=9sm|7L#?zKn0d@^Y9A;cnReN==@;ND>23I8ugO1;TZyoo7&= z+F-2<7DiUxKBWx3f&%vaneL=l*l)vsSF`x3JhrnZ&`VrG&pd)tY2vqD*l!kyFLiP5 zIy=o;M(7KQIqUQb>lAp>zPM7sQgnMCQ1&z~ErE$OD}`Zilpxzi7wF?XhbK?HUXTgS z|K>Jp%k%DlAU2h*NKXZ8M2Qc5nR|F4JjGB~kKW#7i=CN-Y>42RN-yr4M?$A4a5;JzKvGaY5I~23^VghmL zDaMh(Tl5*ftV1Q_O68*Lus>?x{%zeZ|GxtXfY?7*(}P-}57Ju{cS_Dl6yWUTRO>)F F|6eLh>Gl8s diff --git a/dist/icons/overlay/controller_pro.png b/dist/icons/overlay/controller_pro.png index 67cf86d5c46f43eedd96beacd789af20d94a07c1..78273fe57977b2932b78ce8a229a12e1e2973334 100644 GIT binary patch literal 4531 zcmai1XHZjZw+#?N4NX7@QX)Nch#(*k2sMNbqVz6BkglL8y&9_YUIGb9Q3BGDBE-Tg zRgfYgD1wSeQ3S#7yf=3k=YBu#p2^PId#|Z0WQ2r-q@<)&RaK>>rGb(X0H%}_@OK8t3JMA+ z6bfMe-KSt+deQ_iCB($UL_|ao2n0oPA_SfRAe@vZLI42$zYT3{Y-D9+DQ=W0p#7UB zDJco~033=Ng$dvj3B~QdcbzPvjDSorp{%2*{$6zALlFXe;9S5>0;Pn6#L1KbPL2$e z02AO){7*`tqx3%k^noT&Qi3A??+iQ@5c2Z!f0HR%paWb2AO|u-Dz&7eEYY)yTG&hB zHVzFhq^>hbD8-pAh#v7Hh+o2iTDxf7Y#ALO#egbcu;C-TW5ybLsb!I982?dOOBw#>Gx6tBkU+ z!K`brpBpBn*=k%EPXWUR6Ln2*<-X2n(f$&;ih{+N_`ZrEyT{oLD@nBSmqd3ft-h^f zwi`?o&OpKxvb7s@^;Q!ss(HUGJdQCavvVB;~G{#4&jn=#jqaaf+_mE!jfS& zMNxk4!ns1YryaF4`>9d3Nv1sS{!I60^cOTz;B|w{1|1kw=9|Sf}@P{$1yK#nTvY1 zk8P0n=Nn1++?TA%snIh7!g!H$GWWyJNri31kvWwU#6nH9AwCvoj#BLDRM-Cy))~OM zzL?MLe+1u5%oMkO;vhMH_&I3*9OCPPfU|M=RK6y+j_bwcL3LPskkkH{q>R`pYnGT< zEYl)&h78hM>Oz~vVz|T<>Bq6{oHmo*fV()MP?2r26rK0*XowZF#KU zSIpIhNda3DeNN}k(|4n@K5WUsajzpA-gM>Hv*3$9Nszl$vW}l}#XB5u_v>PX#B$Fn z32kXMAVbHoAc<@&-c|6)i%tFmb7=Vr zqCBf+-dMSU!q#{8C}@0JvE(C=jt#MaEqJ=_$w(~kD{-m?k1w%J@s|#GK>p>H9-8r+ zm2JpXHT^%_81mlbk{{u>cR(#kl zJCg}|fzv3-T=jEo1WYl1{ey*sXgi@^sByM=~K zW_XtOkBA_`;%2!zC9+98)W+ToB<@^TFDVt#>j#$O(w}USQP|qfOvBg;DHj<9q>7+j zGfy*3SWN?&WG*#$?E=Nsm}Xz^2aTL^}`k=^&+Zv=B$vH z@4~HyY#ciVtk2CdJEFYZEooG|_F;cxSxKRGg0I#zp;^M_wGQIG>}{jOm*) zT83TwjEuR2d4_$JyO+T$O*k1pa6&m@US~g-W5j_QY=sYQM1G7jF~^uyIWWnMx*P>YAkEVX9ZH;jCOiZE-n11~BBn1^Huem;FJ;HavRVoPGp%*<%xt7vR& zCr3C`EP&nmOMn3$J)LJh#Q3{b-KX;L2cJy&yOkFnTOMF`^S1z zY2ViV$(6(1TJaZPjtI06x$t~6NGXFk+QDL;4qa<{^NuwwXgc7RA}(T$aTPO*%rFdbihF6P*1uW=%CI;5tcU&>0Kuv3X~_DQ9Bvp34CCyl{Wqzp@?r^-+G^}lb9R2zrV2I z>nNetG1FtK*R=Xc=iUSSug?K?Dbz4)mD?}>FV z4R{GuTxO-3)z4{+`|4np^OkN)arE*Y)zvFxKjhkJ&=sG59GfdK$Td;WI1=9g=ED>i zT4_>^rFaeDVr8J%DqeA_3#LlL{k28ocY9GHqlVp3<<=&e(SlTJs8h#J_>h~!fwPxptC5>gzx7x2pS;!|YuCF`n8<%Own6Vp zh3kx)64aii_-0(9S?S4vE5A1htoWa6U1IK3G+7LMqZrPO}zn!!FWW4L?#SO?-*Y^v0rl2mF!m#lW~? z?;*3y_dZYN>0$0ZsBSRU<6Syjf=3Qp3Js6x#tCibKGb9r_Vi(jeg0TKm7{rn6tx)R z;#RTMd7laequFJN#o9v%IS*R~zM(QBCTFW1MxlGI@Ow;?Eo4$=OrX}?ik*_;m{0EB zI z?#Ydk%vR&CNBpR`)3m8gUtp1N%r8x+l^0&CwWx(Q+<$-lz^V%NJM~T1UElX*h6c$6 z5t1psmIgy8kEK0X!~%%}?xF9AC^-&QAblwg%?y5FYOR~hYwsuV!AYZ!CsJhL!1$S< zrBV2P?n literal 9493 zcmcgyi9b}|`@i-zW^CEZkbM~j$y$u;$`-P3LzcvleMw~O$u`5sT9gW*$UZ9j2n|`r zR>UC0glzfUzJJ8;zFsr)y64_|=G^Bz=Xu`m_j6Kinj4&B=4S=~;GB^m(h>luc>jGE z>A{xd%(ZmzhZe1CWX%X3(TtuL@SN$Mp*{g%A4AscH$D^a?!e-}+e0Qrg{!_14rz@SL~r-pTRu z^S>0WPzU;`>;?!!RI(Bk+$~*ov~RfZ)PM?6xse4gN;nx>Utd30bUAPK^b6d*w&OJc9pNQh);Metr9$uYPFgo;oU43R+BEMt3zdR}oH8g|+(n z`~Pg6pPw(6JeI#@U|`_C@wLW8A}+wk$7f-GlSHg?m$pWuDKq}6$JbzyEU*(H*Og7= z?~8~)>R${a6iYzu>E0UX5I^U70g(kAg9D|wu+`dWIsmC`h_qigNXGb_ zT|mXk=M1n)S3YvDpSmfXH?t5I6B7f=Hf+zs$Wz~IsEYHbFQLCD5wDR#-ZCz3d-Z}g z=Q!kOb@n;2zG?~gFl)!{w^gI(w8b^{Oi{eX9ow;+`_`yv$gnL8y->Y#4B3|aD4oz z*!nfQM0h$bB1V?y*zk%eeL-|WeZcWkpp!MmlrORaK%pw#{T&>JEru_0xD__?Fs& zlOS}kil&e3I?=0{LK?!VLM%67XgKJ7H18QMskU=gm}bB!w?`Li2B@FNpAZsCWP8-4G`tEd+I%8XI%-e>Y{<*A&pjiPum(9lM~* zsFi^+`qtq9J5j5&dqn6EZ(A8WjP`I2MWf8VEa+yr}g4)7;lGY_^@Ejks6zkG3d$xp9K zzaijQ52$5qyCA~q3S9PVoF3W#{aaI&l$7t)QG)5DFH zsw?rN?YW)IdO!)A$dF-hrbStJWT%P-JTpZrY!Q@XDnU1OHBJm!3e}m4QAH`?o`Y*u z2?zi_Gu$)&^zEl;MI2giwKv-3ag_2(BoN4~SbK3_iIP&Xs>OMWp@0 z%U#MDC{?03yfv`F*0t6iSHIzBM?WdR9?n8Ybw~nX^>eNxP}bZW_M)n)iUz+enm+WF zXah*U;$0kWT?6=qJya`TF|$1JSHToYUl~DpZRrSThC;kPEU^53Hr%C1bR! zWXjwj7R6rn3e2xgM>*0hUy8nNlHrENC@lC1^-UWi50d$Aq`a?bZa%2itn&hFllfQ? zab^ZkiN2sMV6XM5re2P@TMTF%GY$8pTTS_p>LPSaDx{vIS3`6#{t{Ku~G zsd)wECYyA&#E>E`V8-Z{YU01YdB@=;^cAXnWJ4jK%Q@p zKzG&lVq2_N#xie+=al~>GF<{K@#=$5^M@WVmu9l=QZF22G09*p%X68J5LcvM+hNE? z#hYOGyAx2yOAs84Qhn|44591WTw!BtTgtZ>Obx%l5m6TBAHUH5;5rsP-4<3? zZ<$Y&%%UZJ{n`ZJO+uSBF2E^$4h{~lc3V_&rkzQcTS-DWcL*oULk+t#$jN`f1*gO7 zu9m#v{~DaOTvgM~x3#tAQ9GA6nNU7HV}@DmTSU1<1 zG{0^Eb##v>Ym71bz#nd?7J+aK2tM3-Z@lI$pn_V7qVl&k)}mU3FC379v&>{uB_D0K z)gI5g3)IVQz+p!+@p{Uio|zPEwEBgpzEJ5F%NfwaMF5pSWh6#KoJ7%ZtQVO0H6FH) zHoRWKkaK|fG!FGHhulO~=q1qMZkvkcd!3$#>hJpK0kN9GF7p!;l>yZ*elXV+Vo0-n z*@AcDH2nxgn_syFh+mD!YO9TUV7bl(=%2z;QaKs!dHDJHb#qhule8aVw4W)8J6Vh8 zTru-|s4hVBOKEtM?mBV_0VD~U z_e^74Q7q}0x1GtM61>DAV!@S6iuMTQdZ-H4xN=|CrSoJMxchaYN-_>iFQcQQG))1e z*lm+11IukxAk2CL(UW0D_{FgM6+Il_CQj3#bDmyqsXbOre9P}?e@g`#K>KSKK65_X z4Tz)vB^m!*v6D5H3(>`R)r4v@W99nr0@~@a%3e`TO%02|_3NKMg*!bfyCLTtoW*%! zEcH&VXjt0}nT8=t0-Jzy^3Fh9aW6;8r-sj;KX<6}6LGHQ2Qg>OturhM7*z%5_CIgU zic~jQ8cQENdh`?w!%cu46r}^>iozWlFC5hUTw9BXY-K4dEX>U6alg9(j*mZtWz5&W&vN7?K28p}4}h7Oj`d)IjZqvjH+&$kC>p9yr0` zx)>LOfi5b@AUOxK<=EgkU_vpI2Ob+&MiSS4-=>x2F29zKndcR20q{)$Bzn+3e8^|s zmOD4I!N(Vf5HBp_agW58mw1HCBl{1_=v=X`@*7`s=`xY@j$r2OuZ*7Nb{*_G43L8n~ zmdT$BTZqxOH64HAT3A%%LOzaNo@l}NG%l$rq_?^ZHSlnbHA>bKIk1Ngz?P-4m-nAe z>U+}hBe0{TsATlPKKsahi`wvmJau)?m48XkRWV>WQ((;A(^tGh=GN;sX)#t3IRniI zN>=K*-h!{j$kqd}$(7LVB z@^1r|Q;s!NDnQU~X~Nvl?vL ztO3lW8;V(+CgqME+?GSbkzDKSapGW(??!e3SCQGn$IA~fyR~v32zqY4O6Mj+=Ps;9 z)myGx0>sP@)bGptC%2z_AmPdNFJDSK6+@2@4ibrIK@d8?pj#TEtS`M#J z|GY8N;5C%P8|U7BGZvOD`P(l0p=9OO=)_l6)s5)CeFvutcYLbM^aOe&u#Ux~1Z zZ&_I(He(-2{^<;Sy{*Kh#>Z))$P`^RnliVhmf0xuO&Cf^{M?H@+~zN=c6sM(Gj`mO zY7XBCkCv+_h113>3iTOqmFEr$<+tM7Ug27>4$mIIoHc}W5>*dGVKvRKjirQo7OO!I z?a}Y65P`_)Ra0AshJ^6SRF>s3pOld_^S3GL$BO9goLuDF*Q1l4$HjoT0Q0JMToMRB zsh(<&yYI-f9|=_}E~7-EhyPIL_VK%_BYC|*#^9iTp5v|>rAQe(Iyyp@*Oh{{&SR`+ zl|}f~)V$99@}*?^Sf5=u)fg@$B%}yq-kcg}fmnQu{@KP_>`uBrZ_*0_aZ9d!+0n#s zShkPC_dSM-6}IcXe)JMkO*NGI#L0D^M2xfbPbXaqrYaCeQ_h~Na*_`EZGR^fi3E++EK~Wn zm%s$@#G?VSwgkBrOpTLwr{0v8aaV&T{**w+5<5cBud4$6b<3$U`Vg0-E&o^s7c8ON zc|L`#k-6k(P2NZ=Ue=1R)DX`+y=h#1Lx1iL6OW&~48b2dCV@TlP1gwv(f%!Q9#Crp zEjpUaP+e8E(q?h#Jm9(#N<%|q+uYpDU=GRmdiSR@4)Hr~v{dup5<(`NHmA*u_YXaQ)&%wOiF6OR{|&6`($*CjwQf@it;*_OteVZ@FhYK>V?dZ^&Us#9}hQ_cZO^llTSg9$o zFqYeoIpc+?b3=t@dI{SU&A=FuIytrOsiqELe3uzT&dTT!)qfkUcO@sz2cR7^(*k) zig+AH^-GwnGiq!Np#FBQM(9sE7=d|rs_E1LOR^DSgch?dmY3DZgjkttA9yk^+0R=i z;6_;&Q~gz=k^)E_fF&SI^Ggug1Ii=Uk$b#47fndtY$}wbD&G#5LdNJ(@@ceBbEV1U zD{xL$c!{HFrhAod|0xv!M_`OEDO=F*tE#GMlhrcwdokp@D9#O$Zbh`8NFB?`)?o3q z4uQiP-}5aK6jL!J?kiiGby`nwHE%pP9{g^peHD=7b+^%xAEo-7KF=P^;9tu65DdcI za5N&J29h@;*_*pnUS9t7B4VfUrwRwkM}}|?C@wDcXbNDbv3uD#_xMxT_WWFWJO%y$ z@qGK(FQJouL;@F)h7p!>#clt+nE*}ta=YhEZEY<*o})P6k&zVlpNHi6~5ye8C3xxstIki+y^j+W%p3(p{k7sCAf>Q1&E#?_YUd?DsZ ztn$>u-OJ*Yw-`2&jAlnvCv2b^*KQ{Dj&yJu18Ph09_Ub%GkqENx0tdH8hoAdTRmXD zs=Qe85{X1wy2k8IXAEBpiBy5!dG%EuSrTi2EV!(ydYABA-O0*|xC2JbRre?I3<)Ez`F$wWywML3?w?w@bCqk3=5W<>!UTxsjTRdV70?sqdxss=>IG8EB2*shGjd z5{q0u*iqPIXx;c+?MCYe$U2Itu6l2-6hOwBJe3Zor`b{iL0Yd3!V$W%G|bzQ?><*q zs7=dOW86Rk8P(hQJ6scCf>T0ERy#EM{rpL>67d^)%+*{@8?9SikES1Z+3z98nNHih z8-c&Hix#N)@?1+ov{?K*$*zIfmhsi7dF%OChV^!)ce9Y#zfa7cD=@Zkj!=&LNu9KK zmBOM-Dz$U?JIKV~2uwYsh4ieQ!*sscQ0dH45XS6S_Tuqmt_-a8?G$t3A(*gkgKZ>a zI!nVh`F?F|u+;YXq?A^r!`(c~2d^6XmUc2cJUk{Z;_sE;|4&%bp_1yS1+u_cV`C9B!vn*a&i4~m3o}Js2q&r_Lgol+_9WfE z5YGwQYUa?~YTep7bi_`|S%SzR*f1D3>M%HPNur-T_%_C*K)z$*Y8UIpT+zEL50=2- zTn^r9n*fH42=jCfLIb!{j6o4+g;4Tt%`@6j$lMv?xLKVhoxMu~KYYD}4Lo=Djv9 zxyWu7=bJi{`e<=%v;;a)GSO1H?v%@|KhL56_>NCdnW;EpHQ>vCOxMN3^|9D#)u~fo zuGl|i{T{;NrAE$t-57X>;0p7Ht`9f6@^noSvd!)@4EixVb^8HXO3WMZ{L(&O1b`yp_G}A<6-cAR zWNaTBaE^INJ{>SYb&gTmn+>L)2awDF&HfWD&b_P90iwEG{Uwti*SKVybh0tOsJt96AiEHqDq=CeYmSbXC&WKQCP??fWP3~ z>iX3|>`JuL1r(cR#jlty|LY?9md4d*=`RsO4bF+X8aXe-hVw*GW^kk!({*G5ZUju_ zT5bo#FJBs1@mV+V)m7hJ(LXRP*Wuy?ke(BY6RC;x(YGCBYWV}6v5r(p%_c_QnCN#g z@t9lO@uj!(D^J%>!NNf}Na-ek!F5ft5`5#LXYc6xBrje@rANHV zP>cgHzed2i=2|{+O*EoieA>BvFw=A@0xBu>u8Hza(})hnuiaL1pG}uBB7W{x zR#6dRT6{D50jg{LguBIN^`U8z>-U}a>CVbl#@v=uhN&Vc4UdOW&xLwSqte7UJ&zPA zIFKbq^;1TF%{H2xPZG$3L@rlZZxtWy;W!AsxW$P~>|saMLkLWaxRr$4=Sjivf6UNl zv~c6EI{t1duqoEfD3X9x^%l$pxw1Md{w?BzE~DZ@QFJAo?cU9VY?7!5QslaU0eh=i z;TDe(M>+)dW{qBgnBiJg{2Ho@nKmuZ@T3(@9QE3wT{`X*;{|U=b?N(ph#OWBmnttT zf~2bs3=Gt9rEF0#p*%gG2``0OBtc*=)}7k!7sC%(tOB!__YD~>-k6G`ud-$T>kGAR z{Dq3?5$;Dj*qtIu7}VK}#(H6iFW?MZcBPSNViS`|kRGgd_wwh`E1J9a5+QlZc{y{EM}Cq~uE~SxaM~ zK7tvLd0FTt?ghi>Im3j3^E6Y2$jh8#byn5mY(%OQvdZPlb-!cI{DCv-MVqQ6DI4SJ zqm(X_1RSVXao|_AMmCg$(%>=&-Mch^&KucR`ciu~DD9yTN6--uRymB|2m#j>7R`}k z+}c&-+Ca==`%J^@GXvxT6$GpjFgNQIm^xS0macp_T(2{(E&%Zt8cchUAOOWLnDtre zvYEm`;l&2XRN*ndvqp2zS2}N9MF=Cwp1Gt(XlKJWR(VK4`)+93)BvvA^BtksAOEdK zJiCCmuiY3_NM-y)Y1ghC_k(x7R9mnC#dbmVWe!tWo>vHd^ETEgD3p(`SvD|l@v%s64fGW^j)eey z#hMNArzNX%{_2bt_JVzf+VMJtnIFqmIav`G0#tcC8bB9323~BN))Rc9r$Y zBF`P?tp{!^0OhDrwAspm>`yKvhxm z&oM8)a1!P~4-qC_V^qX#w1y$j`e)8c6lZ;;h8Y8gIO^})HQq!l8^SikLK=3I?7kua zHdicwGSQp0i-}nCu&@I?U`t8Jzz%!JM7PL9e2}Ow?88-QF0~gyR7?cd%rjVp@tpm8 zFr_hoCj8`YSv_6~_$vz;@M6f@gF*jwQSmAZrQbEKR={Om!$gC1LbEl0kf4imL|CsockZRso>GSy#SZN8Z-N%HZh3&fe;6j+<=EY4Zh!cYewP$R!7=v zc^HUg09R1$GnY%6-wrkUf?~lb5Mln-x)lR#T^35F-$EMAq(E7t7rN#gH6tMwmC@9g zh*X@+x3E99oN*R8o#8nTu7oQ3j-8?ABlH}m(>u!l1PXE9e3jGhxW zsD5TDSA6%94v~0|>s$fU`eZQu*`#Yngq~!VY*qt(38USay5)3dnIl9MgsC~{r zyNDwXcQv=^m+$xFOT!|;Qa67GfDrrFod_y6?!6ngmy2pq$0+ET$oLepE^vqVfRKnA zInWFYZ{6-eMOwtg#RYGQIe9 z?l~O@AY#xnP(mVD?@Y6Zd_Fq29x)#VIu|n6ak7IWr)jZ(8n*jo?f0cdCxl>Xjf#hwQFiQ_A+p&goKdFAzgJMJ|r_FjonS12jJ9nzV$ml`~7HM|eX9_(<x0pYKelzt!8vwmdm@;y&PZ6iHIkq!L9PcALiaTznqD zW7dkI#aj{DZKQs{DOw*temn?V=Dkb} zYxF=A0ugx2cy3AUb7@&nDQ=4vQ3IxjmtfHD0n39Q|8aVY-%^Qc*!J^D|G!&vAi4yF aGluAqkh6)lJK5kCCcsGF99gUDn(%+W&hcab diff --git a/dist/icons/overlay/controller_pro_dark.png b/dist/icons/overlay/controller_pro_dark.png index 7be655b961eba94566216874cf4b14c633c1e084..8d261f1f74ed96b04b4dfd20c0d82b848efe8eab 100644 GIT binary patch literal 4531 zcmeH~_ct33_s4^X8LKE!BenNRDT)v?VpFZyt0JX#i&Dhan6>u^iPmU^+G><^_|z;l zs)`a-RijmkZ$IaJe}Ddf=XsxV&wbx}?oY3C&pqd+T3Hx_nIX&o003-iVqgOR(D3~m zjC6lHEmr(}008Z=70S-=Z+ZSd{g1$Z9|6tulhVK2e->(EVG96rGO{vBo8F4fs(JqU z)6TE+PCp)?LBaf=ADgp-jd`(HJ2h?wb)W9_lQvqunBBKkp+?tpU;X~zPj{8DT&qZ< znS7cpepy8qbtC*M}Wz&}ezTa&}>#K^nORtQxV*Ee6vGd>?oY(Kq;nO26_UikUm zK4edpayF+?ahvyLCRYnRa~)g>6nKX^`aLP#+Cfr|quE=_7&jFlY{7r{%u-<^!o$(z zvH6P`sfCBV_3mCPi7prA!$i8CtPSOh8unbN94|c%E_sI+GD|CleM=oFeDDZ)ZS`x8 zr+%DvUp$)|`##!+dN07zOpcuNO3CBeBTK)G7P-PWYJ`=-J(y-+mGI z7YiV^@|1Lu^soDWb@X-vG9gjjfEd4)NpFqrP)xcO>r2Abo6KL)i`Ub6Wdm@CiLimC z-#Uv+OAf!AySuv`BvoH6dgYM4zP$SWBl2Bt=bfG2J3CkJCnP3|Ru~!#24^cN%Ai1i zvkXR-6RzK)65G1{w6ka2g88fFPX0VH*Sng6c^U%k6X0*Hnp*rB-A>D4z1F_)y<@)#d3QPF zd#_<`2|2*F(|;`d+j)p*KAWA%+hf$|&vU9>Ysk7nW4DCla@6lq+mTj|^THU%6idGEK$hn##%tQC(E1_PLbVV+gjGdo;gxhK zm9y~Co)p4n)TA&3jF?_h+E7j+>f_x;=rUF9+F;6O$V0xtE?B&^rBban6Rtd1m8=a$ zl&9WGF^lE}C4fCC>1Fs>(-6K=&4UuH=9$+0i*mJWr(!@qPFzm+dk-(PP3gQ36mC{9 zfqdh@THL#LW)CC0-byauy(Nb5c)O94myhW60V&vYb$=ByPz*?}p zG~Ml}0DX%e4QSW`5+bI_49DLHd7yzBp+l~RkLgG<5abCF13Xx;l=Vj_kZ8d3)zKdW zV{Ib*x|q5QdFt|#2%@8bkadOROSbGbGf*jQGjoJ!rGud3*Oml19q%TW6?&nUb>LQLM? z43TVm;>s%g{9-+rT*9nq>^!}C`JmGPTyRStN)YyULvnkSN0oQ=xJ^i)F~n3VQh*o% z)Ts4Gklv+_i?72H+mTjRLbUgbPLc{-6rz7$TIXthVvt0#T!Qn@KD~J^aD-eMYPNYv zC5^8_ncLFig>f#PF^=y&q*df&w6lYIAO9>3_vK&jYS+&}oI|p}sMG7n zCEo^|Iq@Mxw?MaOs!87$QM&tS4NzyEOZO1n!+&Ja~_*d9? z=_Kq_wZqmljm~+Il*5~)Kf~|yH)v@KFe{E5%loDiDD;etEeeH`SsDh4ro!+QwT8#+ zgs=ln7IVZRw`sDK#@F}=h)Mx<-C9b#gV-SU$Y>(D`x@D)mV!x%4sccqgW5iFQG>sf zY0#u0D6+m4?XR^GWz@dytn`?s-}MGP(#HMNY;CkHJ#A2#`CHPs=GS-uxqilMX0$NW1!0ncZWp3RF9wA2;%M%)_1i=AMB4l1bLs7nSo00;Z;P<9>lRy!;s^daU3LroFMKNvoMN1#6IvWl27$` zDl0~LduhBt&FYBd=w8XO{q&S%#admGPPz`FotEHAke|lsdv9GgntkTO)rQBMu4T?d z$?KYQA2BoM5f_+_)F0jit~TT4vB1O%;)4EB9@nT7FVL0{+ywiAGq*xoR=YGw{g}hv zR1vt$t!@(WSj%o0t$h3uC7Y{3Q^ZjCi&;;4(0nN-N%xjK*vBb`A(xLn(Rx~u|BPHy zIEGs(`x3c$Csvp$xGjhpZN@5g)+|n3+34;jHOSsK-5|QI@DZ^VS`7OZ14fjm9S2yu zY#4`5xsS+C_|l0QTb-reJyT3jF><4B@J*!<>#zb5i|Ka*MpuuP*pO;xXUCe}#iHW7 zxWd5_0i1T*0mcNxOx9yK9mq%9PW&Mmt8}Z<&m>3&|HMV8h}YHBE7s4%YAQ_)TqpVG zNTOWkvu6L9*M2|MNHA-jey@%?cBX6va-PwSI~z$0;d9U)xb{9>_MU7PXj zNS1VPLz^)ZsIm`uer9ae7FUdeVU6iGRq@A%$+6Sr*XL}B6Z>D`%yJwe5|i^OFI4k`+J3J_i)>o>0mJ$RdhLJh14wSFeKUSQ-7oO0ax z_34|eW0Sa-P_4D;*vj%DdA@O1EjC|(p~x8~2Z<51te*KYxWID)8dCo>x(y;PhV8b0 z92qDrXS2sQOh)FdG}hp8?xXxxoFQrNS7q)M!L=BM28H$9gT23`?q!|Kd>k0>)u8`a zcb=z+zrX4)#2OK3Eq3koSdeNaYpj#?0t2GXBI2c|(HvMS24td5z8WIF1c31BhuRAMr6+r*o#7*@zOV#il@N`z!5aAx zjgN7oHE-89h9+Z{iM9mqoc(wKW8b; z&e>iE!{)V3{p+(|=gA2t-ToH>?agn5Txbj$8FZ#dtGdytR$~%v@_XGEu-@($x^-$J zmZ3xJu*Or>7QA+^yk@Gzl3}abwz>t%l8A*Dn4cDOAlwnsGyS z>((;$dt5cY0T4jLFPyy)fbEO~&fS=+f$c>9F!|+~FlHkMmM#Jq&_gN2B zFq*C;3YX+O=nc6d0aBawrNbjf4q<*ux0~hgT|)J~<6y4a?~Z~33#>Ci+6f|ok3en1 z;*eHhy%jz@Chi1-(f}r=!F&{T>Y(oX6ryj?ANE-Sh%fOSw#@pB zd9lC<@x;J;fGDp|8Bi%cMN}C$Jgx^XvX}Q%heOmG!xI1cxltNd%fc9ZDbCHKa<}U- z4IDyuz!r}}gNV6LTL-_xvm&PEYMjQvhwjivEK{u%a#mcR?!(Ic(vrAMPv40S+4yl9 zkO%Lx9tYfUr`l!4Q)+e04`Go7u`LH?J*xQDbAAmF!KT?`g%O!6OmGoaFl@R@nso${ zoTtBf-%B{G)XC#~QhS$ZnP)KN@?@+a*>_d~zW z>@r7u^%vK)fcZGDEq3t-{IXCE>I#)HsOpcR<|Ve|p_Ra^{~@iqo}pf7Y?oC9{rg&( M8d?}M>AS`MAI6*SVgLXD literal 7488 zcmcIpi8qx0_n$G=$k-`{8e8^#$r6=)U!tNI$s<|IGWKB2K1#y3J71A!2v z$3rg=5{Xpw3GfZ}^a%1&41DNaMA1770*UmQ8(qE@Uc5<+K;Lr0jO=X9-@I|H!U2Y z9*^nE(-x7z5wA0w6cJuezYKv$qQMI>{D@q#uQ&qU8n*w+W`8Liw}F$zjh!xxU_*od zp6wpOmn!K7_>^IvFz_Q??n)2TG87l`+vwkG2pmZfpCuSS-e}V?7q(W zDB}u64y?-%$k++)oH*9KxcefXVFvV_;d&8a_x+}2(L#(q=>qBA*Wt`btVql~uoNN4Twoxy?VGlhsGj%~UVc}8B?KJEY`0qJpmwP~l!;m8i zyTvI2ef{Kq9E&LU7-Yo~ASD57**-K6@k)GvgzSSBPm`fm;KLl|y~2w)CR`7&Z+D{A z9(A%qm_dQ#+#YYQl^TYh=54EK%sz_IKyb7W+a5E$^D=PteE(`jM5@^-;n`f=%_Mo| zHLX7c2gJR^k}`d7?pCfF40uRBZzLmEfGz2jYktqw{^Pbg_pQ00-Vb+3$z?6`AqA(cvO=%Z7;`5`BH-vu<$Sn<_pgL9@QEjYtmdq)os$wmmT~+Q4w8_|f~JKe z2`2={TmIC!Y=@=)cwdM#M4Iaz%&3#Y+<0aY7D8*z@IK-|5xbcBS;+Z10@*_%=u^>coJj*Es-g3F*fPe2tegy1l+A!WZj;h)x`8m6 ze5sH2+Z^m-OX`NVRe0gjfv|uL!@?i$>2`S&EEDt{N8k<+ca}?Dz_a`I`A*6eT zvVM(*0=$W)5!8)QFo(T2glRCZP#PEt%D=&LW(IK3g}%zgDHyL+r~k1a&aKXqyRHt( z;BE~YGKA$K;!hYjtzam#iI_p+6HA!Un=GNE2h^DOpX-e%X@~1AAZ!r?|Go~Z=o#XQDcct80%J3Rn9&=vqZL)%Re-w4ejZ*`iwEG)kP_r zKK}-H$PYDN$S@}+VK~X9s?6Dn)C;XQ>z(>XBb6z~NSv_4ZTBiuKaS!b6i6%(p z$Sh0m8IP#0OM!oZn#2NY2MqR4yL?Vvm2!ss@&3_$iqw_o5~~ zIKj8g!1i467L;{81~!WXm{Lu$T&(`W%ZD*Af2JW6bg+hw*LLV!B!wmpoe_ElZ8dO`Dj;nKOC$YuKZp!4HI{wA1xH)|P z0wuS-6z`AI+_`dpUL18g`$gt(9tIHxIlkdcyS>4q78NH44WVj*kl@Pk(=t6wI00aP z+6;=nG+pgTIpflv;Q#2$%?DH@=*?qZ2M;WU;P0h=Yt3Tut)(Bo#pZ`Jle(B@pMo~N`>L%2Os5%G=6b&9@t$O>q_}7k?q9}F-U;1Qr@ih0m>>W=F zml{Xs#E2;K&9HWRTvhQ$!t+c+9jnE-+|d_C+l_Bgr<7UB|v`0`>i z*{GDNk^d(!rz`bh&fORC!nLOKpgP5O4Fz- zy5T0+(`VzN{6_%kele`l>o;PN!3f%6>UX+We=@!Z8^|2pQ1~=Qjbuvc-w%S09Z!4^ zC!ffS=Vw0wHmm$0FG4uR%#=$O{M{{P%_Ct!Y#w|k%p{dTc$ZBO$2KoA@W2PN>4V|q zpr!m&bp=fkV+u?R@SRn+PDOWr)0W~vS`J?3o_}=yFiPuiIuHAiaZw0MWLV?M8U2`0 zbXGxwC5RfD(8VAIT4a)_^U%RX4^kH9S!r&X=Y~F;pL99}BhGp#P4ePgP<>`BkQLcS z%UEFlQ^n(MnhWX~#g`8%Q-}dHC4nS+-b`!@+iSza6}xkOw+HmI zX&n%lF+Jjdp!t=1&MzGK;-ptld=n25o9NIxccc^hEbg_#v_P&Hf_X$Pdpt%T07kfa z-zLXqSN+UnRvcr_oBv9oL8OP+WeP71Mrq$qQrU#{^o3b5Enaen$#O;$V7L#cuX^yK zYOWl-Y(&{QMHeDC)f4r;qCeGB8a+KVRZ~Q;2RgN#lL+Vu57a|2{D_k|U<~+e@{O`a zIWe3y=ya~j=I7nh7xCwinom6D`N3?e#C+-6m(F0Mn5EDy@-rSb;#h(K7zwiFU4-N& z+>VFM-wRYJpNTHTOt>uHS$5t44WixN-fZ=dmt3H@gZ0qEx{4V{F%ogeeWH&MjR4s1 zs0*kKkXimJI!9e3vXQ+2_VL$gSX<^}jz-6|&?-K5R+i%uPrz0yG9DYqQEXKERIBr` zub$0?@*Q~(h1nFGO~>_5J2kktLc-MvQ4*yxUOZtc9eeC;pjIUCvhVA{QHX6#o2we~ zWCYdXIQL5$s+QDrY_K>++_L8T2}hooeOMX|T{t>SQ;F833RkA1_0c}K{+Dy(%MaC} zY@GyuwdMp1wE8>Ekgq6l)o&YE9aJslZ)0<#{_9}64o+R$B=*uaP%F+<`SaC4K6e(f zPj!n|-%q9IQEPrftq;)YjtcuKA%qmOo|SU{e5WR%8co2sY8NOeSm{5Glb)dez zSq|9|70`(l)t3FbM}h1sx4)JYJM@f%d6auPy)ZqV!1qs5a`pXcx|6={e2~t3E3YtX z)4|xz>r)?0^Z|68V(gLbkAupHJ~fiX#_MO)bxE(EY^O)deimEOeG(LC;OAq4G$>Ve z=6PE*L53Qi-EP$o2eRt`QBbGcVRaZ43Nd)Z^^x9Oyis9~f~lV3izll)?riSA0(tt@ z0MSuyS`*t>UASqqpi%cCzyHw_f02*BS$8tOZHW)heWyT8(!QgGc7|M7+M}w%c6%>= z3IzvPgm0ScP77dMUHBdoPyf{(S+OmTP-=7%6o@`HREcDKd-F=wa4}(&y};z2>{wJU zg>9|hXvR8bv-gj2>7F^TFX1r~j?L#~*7F?IUoV_0$^Mvr9D~x_Py5eghjGON;q#02 zWk=<5WQu5a7_lQaGecrqa1J)>|CM{NqN`1hyQgnNyyImLR?uyWzmlb0_cK(8K|cLK zv=O-E8l`pWReNvmdxc4Mrc!Joxl{ap(bb`TugCTri)lgq*D@J-U&wt_(Y;=7F=;z&v&WlDh`CsMresedld>yIfwgt46u8~0BkMNdhh8(FfEkn2{id+V=$0@slr9YuHf=H}U_1Jym$ z5b}W;v(Dm0y|gdf^HJBU!op-C3+y^r+WCE)xArYVqnJM{o(~*0g6j6Cu6i_Fee^4@ ze~Th+!`w~tz>SFCQd%3Wa4L3)yqjs#mO!}iD)xE}!ejSJQoE?8S4=_3aB#C$`xwB#HvFGh_=UyaG9>k{c`~;d{QQ9v*D7vg9ItZH zuC%HE$Q5w>?H^2SkLO)MG2*g;{iLw6Tj~SF>~u_+TmT=HXVH9Y-=_#|3HTG<)yvW? za$tav90W#^(&KFY`;5cl8=KD5`H6gIhU2R{?wt)X#LhG6fRa-L=zrtUh27* z_(yzMEH2`U^P?XdzwqSsf5kFWIp`Srpc+$>M>QwlKT+f3(!?fP`)$`(S1nX2{o>`K zj%2sJCg97ykVT+b`!Ptn zW4g8wD?mpKrM{~Jl;g`+ocdJXT@?g3bYa&T%@WV_Ce5|C@YS69E1+H5_z-in5Tm!O zA$YU$yw8Fuy5!KQeJR%u^!F<6d;H9IzY~=~|0OHZnL!#N<_h%W_BVG26+vy@gHlXW ztEv_)eo62EI@Xd*-2bovXIr~K(!JVWQZESEzNn_#)cCI5z27on?2)Uv%pvbU8LOn$ zA*-hWmK$)FTJ_r!CGn~G>PUSrcbm*cfyrUOHsCcA!F)r(*VDFMw`22yTG8AG-V6zJ z9nL`Oz`kB$1;;Ycn7464IX1rXQX)InF=53;e|V564Ke_8%4^ggR2SZKO~yT1_(0aP zDw6pSK{1dx@>CHHrG6s#VRAA!ehLxg-QyZ@#9|ZtleDNBICnG}sTi`}Txksp^;WhDGF(zj{M%0fuPQ8$2}k>5IPuR_@rP z=Z08|0?yKhh2%%_L&n7`!%r?O^P%if{s%?8obMk+gg*PWdq>|9*;Z|WevQmh91hgbZnd8H&f7+f z9Q8W_Ywz&+-88)`H&HfPh1-j7G(P6ejTSr3h1#XrH$bZ z(Ej=I)TlT0jN%rpM1(^pvwZ$U?e&lSIgG!mHA^9ThpBu%cjdWO?GEs|;+;;(3v5y| zLROj=URf_@q#5n%E!(|3gG)k#2`Y9qTrU zF(IWc8WAiJUFWZz zon)_b+9hV&XzMZqdoYJ z)_ve30}Z0$tM%qg_EMm_hSM%&qcM34W}F2`S6?$wYU8WAzsqvm; z{b`tqFsdzAmPEP_pGTv2%lpl+3YiWxVmEW(!T{ftO+rPUF}6O;;?pVA#bVnJ`WmWc z<2~nWq1B43NA>@?U$1r=XKRN?+`f8HH8EA<{$%&b%d|~R(s!rZc>Dsy>A!> zMhD&FNg9-6M%nj_%d;5@a(_D}CWuN5m(V&{BA4z026elM1jALo!SKXyzt9-}g)xY0 zukl0bEBl8(sD{$g1y-etPWRc_URcXLgY`R)ack)##F6gp&saKjn5P~Vm`T(ad{el? zXJ2yvYgl1^{rsy!M6XYwwaz$ufSNd zRjU?b9a0nLOYVs*>%H`Nm4gV0lJL6kqi=ks?IF9uR!Tm9W_Z!GMdBw=OP&uFHM5wq zZ>#mpjLk$}NM>s38{t@t(4XEsuTUW&q|heZ_<}z({qy;EvApJ8$c0XDFf#Q;dRn?}inMAB>@UkIp`8k_EU+3! z6oeP=XVcD-nTObC4oHJJJ<%D#Un>31Mu}xDkM-?x;y zpWi}RriSYUHF}&#m1%f@6l3~}J``M}NYGQilhy3mJCH#zbsbA>2mji#d-LnLF0r-b zX*HcDp-;*tP}95NKN+To`g4sr*cPT+Y;B1PT1FxBhBm8YCh^sg?;oD7C&SJ?r4%AewR>qKdZ> zT70^AnNq-QrfH4Rq5a5KWVV+}QzbHkvYH~NNVY&^vIujcgLo6YbE%cx@H zT>?{LMHNKP+RUbg+)Cd`8|qN-co*;#U^9^mUr{4f)+KStn7=LlL>BmQT9O$KCwe!S zEL}_F&K5emmdSt0#*3!uCa*8T{X^52vuSR&mZg&3{yIF9=OrYCxYNCo7d~AWP#0KE?OwUCre-Asv&Pw z)Cbo0&5Rxi?MCY^o?hbufByta2mXd#KfAt}f$^Dcgddg6sJ)+LI*qS%uz(Z|=pp<$o5**Te# zA_Z;Sts1Ql=7R2f29wXr?)f^B@hRlzv5z>Zr6!sxBa3YHlnpjPAq zY=JH@;4NXcsXl0gX5co?ZBKa{P@_d`7*@4o2+#!mgb~M)$;)om%FHd43b*(VR3@~w znc~N4PNR*tL@ky8X14;Je9l(J8=vv{`&5MHECFoQn#|sRfO|p1Cl}>gk%Qe$z#)(&H6BeWZV;nkxyVv)C>>a zgbnh8@gu0_1?QFCcj>5`@7+4VwJcLxbOPz&#Rj?vdPVX0^l= zU=B07lEi~QWP_OdoC#{g{PPBjCOjl*BO*3#@SMp*Xj%!GSqK&q^y`wIQtkps1_N`L zDO?zY#=!<%TlQE8i(;t4yN|mj(9&G`uQ#l zK{e9*`*D2?POh%97RNy%H<@P$p``ClBx|Lc(TM4x9 z1i5$mavH~7V1|Ks9M$t28m8J=I5QaPeGs1f1gBBAa$$63jl%r*8O1fI zs|!QS(_zid?xu-`xF0QE#G(5A#>kZEPex%SIT_Q*80#7ukVjEf+iDrrmGilCX?T3Xr$~}fj zQF?U7U5_=hLAIWuR8j=d5}Z}_lTJe6VsJy#FFO#TtuHY8`rAcUM-a;GHMJi{Q_HSB z%ntAZ;=ndGFw=~F9rYf=T>JY-FY) z{7N}SB#Uyqj>NCN$%8q8*%Le+SEfgmk+DZqb629}KszSBHtG?xAV7FE&!FrDi7Gf+q1jQXZDg8;YioH zWw^#FMYxx?$=@d!7((5vkMyV{9^FNjxld-?+@N@O)6f=fPtB}*Xg822kFZK|Kj>zN zwhyTrTDN;?Qk2+otWx?xVsjW3CL5;$*H~N5|1p=g^>&zYe3gz&_k4fas(VLtoBEDK zBj4bNNXsJ^qYXTf8blTArX^}FWjw*sX6{Jl*YdY7^g(A~;d087w8HnK5<&~tzTyIO z!FbmDK_w=1*Wq5a%ReHvFX1SDGeL`MU!Yl?Kh2uKVab~wY-SR+JN`R`HOno#%Xc;s2;Cg6~P zFhnh_H>6hHAF2#Xz8}8P=e$7~(4_B|+-cMdqqojk7CnAy)xl$1OSv^82WowJvy;+& ziLOvX``GfGhhm#`E>juS(FJk|GtVZvnCh&;Z#+@aS#pi9ac9&#{B6#5E9}C!l5&f? zgfH#A^=(V8s;7qCM(x#!3UgA-5gNWhI}Z+Dj40dIfL}QWt_-7h>*`Ht52ctQB;sn- zG}`TWFLDA-Basw|Z{A#{lDri>4UVCry3nQ&LB=g5^2E?`{Qhh47ZZ*B>D6fYS6Z%o zzex%atElm@6`WNc~0f^5*4e zJK|3Iq*ob}$mma}e_q-V+vbe$3Yssp3_8Q@9)ejq=jKM3XZRJQG#X{d-1iOBuHOi@ zOp-`rC3Dk~4R4G#POCozn;T%4L%lF>sTzQ*Uq%Ife!>?+XY*15#4aQHiK-yRCq!>2 zh`&OagTRLqT@RaIj~!jEn@3>3q#E6QH5fJ0$^c6wg0ao(Nv|3}biR6AJ3|UJa~?sm z+pv$V%&GPlBwKH-ZMy1NC6^5_p!eR|{bgX|h(F}V>E-&%TTIDPMd;S>A#h=6*odznG8;JHNcy@F7D(Z2MnZPlYpZA%#p03%Xocl54{6+KgncUcfj|N)NDfadyh&A?15oCgsQYOw_JaXFiYsKBU z+Y&$%DuahK&`tPw;U0NhE+{BXL6>C*tGOKabM+mj_+0WCe8>iQh z=%HQ?I;mrkuq?0jsMkZ$&R#jfBP6;jRjlAlp@mKcNSSzU{i(Ow{3>Wtv(jIR>d8&W z6wNJ)J$Js5WCX&`QEygUby#>TTE-F%Qbs$6cqQeizc*Fmx`F0b|0WN<&?H;eMD{a!V+5n?-eYvvmdrb#z_phJ?EDtflQ`wg0TJUZ9X(K6GZIusl=1r)%=Cotu zkrwi(s6hKP50QC5vGj$v<|u%daSR91qlL#HeQ^fuN=#>~D)=nJH;~U0K?>>n=`3U~ zD*<8jPv6Zg8@nj#HZCdg58oT#81RrwWs0g z-R(at>qgbf%0SjVd&cEWoMYxRw*c?ctmwRnfxpQBDgUBH`01T>XFv9Sk9&Rj+Gqyc z{@k#wsy=?C4dy-Fuog_zwb-TOAK|}IlJL=>^yTP^vyelK*1525l&;r52IBB-<@S_J}gLB zl_hTzb!Su;!{6xGTD4f1Is%w%E9rXT)l?LkC_(d(*WWph1L*AZ^5RXCw+V0dmd`We z0^LSq%Qg&4!&JUFyKtsjB1u6YBMzWb);hpO{vU*;+hXo9bpPpVl@xKOjX$L#MLU%d z8U^7CC>foRU&{OZWNr@fehzQ;v7`~%!$16^p{9!lcy@yAt*Pz^&Vm*-%g5W zTz%m6xRI^wRp;|lTL{R2-TN4+@^S5{Q)r@z&Nk`qD~V4MF*?T2FH+9?eL2R6+cr!) z`7BNWHmu_b!&bJly+*N-u@(xcX&+!~8qP3-sm;(~c|nm_GB2jp&}G@S6}J%tzvq_4 zpPYG)Vmr-_R#5ZpVCnHPv(XTMFWxbEA?U4l83#1`+WRSHubHsp zXzyGSKoe(H{T=_U3e0x>p}Or>v}EUvnrcU233Gw*VrhcBe~xFb-CgpSDwyC&$@k~1 zd9RvSWG1y>u2pVkv5*(YWwZfjEAD%cr_Z&?Ngrr>f#{8}ymWUH1!|2*b)fZ7H=4zJTq*6$bv|6Cp7_MDD)gY<5S$8e9jek*=?BkZVwy z5$Cy!P31BMp(PEb2)K&w*>Mn;D&D!AuN&G%HUHd?LK%bTslc=`K^~xa%02Irya;5+ z-V+CHf}lANzEv7>x}b7%XMw7lLbsz$lWjqnfUw=ZdXmeLjZcN|?Swuly7C6FM!0ue zsy5-EYG^wrgWL)_3mx1(!9(=#pHkv~7?iZJv!E974f2a}dCSXWAA*8ZEOfMbJbsc> zUYiL<>i2)Dzbsv{2XURkWAdk;e8x2f!Gykbb69U`5fa{$s@!%QaSax2RIMu%yvpk( zHBsStYx(@lCJ%nGn%H=kt!Dc@o^du_oZu5)ob(r*b`zi8+Euef|CfQg^<@iV-ddSu zA`P4_OVNX|l1IB4!X9l)#Yqs_>buz&XF{}5dT8Tn_{mvNG4R6Cj~(+}R4T>2>+s7~ z$kUGte2%xq-ku|8={h8YcxkU>_(@yNbMCRYR|-@I2B1F#eB zSgrAoF%xWD?=LvhRt9c8DBed;S=tc&+X!E>qmf)Ck?=KJ-o+KQN{ip3Yc9h{E8wKh zD#<(W0q*>u_)?ot_ZcOa*B={PnSp=~ed&2Jr(jw0eJTjw4IfTn>LW-0N`U) z>^tEjH82K57Xg%W(g!(b@J5LUC=7(7>{S`o>jAS9#IC*F{n^lCjR0vaWpL2uJ)sY? zulv|9prO?=ia>FVAcE+ls`i|^7-V`8bHD46-2)sj38eYf>k370vTlz&0b=-wT|q5vF0k-%%i2n$zlq0#z47l5F1OYPX7~FqJk{wr1 zUw178$hB?m*sptzqXtM=6>9oILzw~Ec5G0|#w<6{Ym))~Cy&DbghM-i3Sue?4J2-e z`{BYPGGof*O$2@ovqL52DcvW11&`}`nSVFCB!^>O{LE$+u%Q*jX<%4TLN{LUvczM2&`KlDjGQ=0lj`Ro9#cE-|pP|j0@ z>KI-!Pm68#JVpL-+~aAl^k*&r^T4|lk*N^KNOk-`yJ#9v=C082lpkZ-fWD}jsbUzC z$^^Yn>X|bgY|*Qwx*imyMcYgi))0~;U9(ujp`HZ69D*PvMMO=&a&~afT z>)QAJnq{RxsY3THF&U4{*+W>Nuhq_$A{h|9XOI_+X4#d;^L4JLJsXm3@|jaj%w$@r zyWI_}`3c{HpS*detm=hAQqHQB)JMPZ{EnrL&oDZT-pds`t0~!gbw5bYC_r`M>87jZ z2Ygs!irL>j?RZ$?!NQMfrjlH^Fy9q!-1 zUAYmMo1%3;Q($}OXh7udKW*)e_gPP_dbkXT`?LDW5qf*}F(Fi;s4XP}V+Gkz8*R+> zi7s)W)v#Le&KpT8S{{9$d|0Ypo@ppYq7Z-jv&Z`dR_J&C-Ko(L9P9SBEUvl175hi5 Nt7Uk-P!sp~{{S&bvCsej literal 7489 zcmcI}XH-*9(EbfosRpDsjZzeZ2qLI7B?t;qB%zlm(tDE*L4HE$Mg*j*NDECVfl#Bg zPy{ic(jy6=AiWF7|N4GL`1cHKsxBmjL|C4r6YBq=gfcwu6P{Kk(HD2SDAO^g+GGLI!hYdrq;c73csQ5jbX&)I< z-S7K_;5F4mdaWJT<~8v+8OvnD<}XN!1P*lQoD+lwq1gAHGi?#pUai|-y)qx(k6$k* zk+*(PoP}b2)iKO~8z87L#{ex``u+Q`(th3ObYhoN`65N*sb&R!D1q$F*%3UOZf+OS zL?n*wToAbE<7mf{bJ?N$1@dAbFXj|xvo%Rw6uQ2(`rtg?>X*abFieq^BsxK8w?Uz{ zovsK^oq=&HU%mR*5HZp-SxO4_WpQ{}Ogu%p1k8t=Ryr5^U|{v<&%OGYkgZxdcX5pU zeqPTRLZV*(aG|cbylvB#SbN&*!-aXrcqj);_0@a+N*G;WKv$e!ppSeSb+lVkQMn>(SA$B82mbv@Nr*Z>|hXAez7zjAWvhS9QpLP=LmIcL@%FQaTQ&#_z(Y$7b zc5`!+dRaWihCT;SdL2ydgiG)Znd#}v*sh2677bo`)DdFVl`5+j*wT^w@_gHPumijlvZ(ZS~szNY7b2)zN74ThU9h(7Mu;P1r z)N$y$Mq@7Iw!2!|g-gFq3tr}+#Q{|TH-s)m!u(1cEmaQkSVJ5-8F)EZj_;r}X*!BX z9wt)6uuDMnO#HKh*h_F_W#vu8+p$8M(f}sJqe}&87b3O!L6w8sJSQ|6NW1!EP6^~_ z+_y?=^B>@+^Uty;VJF$qEpbHSU&No(4>8BHK|J*Qo9v)Qha8x4wZW%%DaT%(o_X_N ze@YtLz(|>?5ftZrfa6Gi%Qit+HAiXi+5KmD5zfcQcg%r)!VS*UG*VO16)Aml@J1Dh zDdvL`YCCE#og-Zyy8n$pAhfu(84|%ag0br%04==l>D`I9=JpZWq3vmBoW*3}_4W1X z_&*_w}<`^9@@lBk}Zo~{B3 z{%};6Lj2faL5H2g>5X89+FoSqe*CeP_KS<_=`CI4Xun|qua%Y6;3iz0*a_#L6@nI= z=D}GM`@?W#DZn;GT8Rm||I++DS)Bz_2ENQcXzkfll!8%O6LeNCBLfp=?mR2`9UB`P zRFm7>KWq@Y`%SzXmiZ!{d`+W^0BJcvH3FuQqqaY%BsV;uWejVVfjNyivjL9&8bii+ zDv3l1pX*t#OE1p+RA6uf26t5Fr2u!x;T7@JcIA;*uIN(e(yPv8^4-MD_^#uc(Fom$J zz*rij+`;o2U!Wb!++9;)h1&LLHAIb7)t?XqZWBedQQIlOYIbQ}zSm52b#>LmQ{#|< zP|Ok6f_Xv)qHs9t7nhfp*Hs|Aqbr<6X`B{JXJEwStVW0$j~c^pdu*W+D}8rhxL{+H z0%Xl;r7XP;gH>yC`jpLGqST1TFB%57mrJu7K;hKs@z_dy!M*c5+&4l#BVfb%cZ=qM z;E7&Z#;ZGJ$i*HbFpXHF9GAOpqfD52v%zwPIiz^>(v|? z8Ih~;Jjm4ZkMa~AAp(((mS0y9bSQMpZDQvDADWLH&|YOYxS+A1$2^eEP>9z|?V$xV z`TmX+6BBE{dT&!4u=i0igp9FKp}-w5V?26-w~L)#cD`-p+>Age{A$-e|9tq4X9-?| z4c&}wsmAj{26%DDTpCWs8WOm5p5_<9*GSKWiPe{PCSq8wO z=234-UtgarP+wlDD8dwK3Y%6!FaP6_p?c<$ro zPg|y)D_)@f)sU79eVdC0Dg1)av03`MBKF|9gzwR#i5;ae(6qUOaLx|0s@Kf*PqoC( zqL1_~p@FDt;VpIF2~%^rHkx9pK4_z>o5;W*t_R#ISFVVzot5X4(PdBaoM=1!QtQlG zJ~&xGw~%vg)+aS+@0{evVOg_^(yf0|B?`E1)sPmB7n6*(Wyp)=8PD<8WiuNcx|4bd z>5iWAzGZMQDbk$(=So?^IY`z7z)A@MWF75Akav z*-SPJ)n{@5=AoiW_PGudJGEd!r)YCp5dG%HG91-^uC?8c;yb~VUn{qi26i_;Uj7e{ z$8*AJ20k<*(YcXhm*H+BrR5NWn8wO814_#YtqZ%}>iVcC56iDkTW$C9PT^wkp(%K~ zuMM2#A|gw9>{Nja$0IwG%Bx+^l@4G~nZx0xktvqV{8NVgx+dcIM^{PL$$% z>V^_waM*@|o${MO|N01f@|)$4PJqZC@fT_p_=Q)yev1@AqTT>MZj3Q1=2(1WXr194 z%=Gc{I3MKQmtPJArAypCSpFtK;?VMIx6C zK%hzGFG;n&B|h+O?#a1I@zSlvakw?&vQ~+$fI=L}sqNk2X|b8{CF-X86AT{#wt^sD$KJ z8CE;b{mYyrQ*dU@ERBdzcv?INsGPIsF9Lahe4u3Wmxl^gbuu~0tB^D0{7__*jrD(G zNq9R4!EWVZ*R~VrLJ(&t$jT-L1q6sLT~-X)pqmuxrslIz{ZOsKq!~<(&$V8Q*3fMs zV7fQp4K`^(N&dx2BlZD6f_crP1K##<;;I29T!0h?7%m^M0Ya-o+OBL&47Gq-{x3Du zCjyEwy)=A`Z=D0+J&2qeOXx?uJxjS3;q7&-BJkxkEJJ}gwk1&eig2sU)CW&bPsi8? zs>6%XcRAP%Ton=Y9|2bCTMae{IEa65!}eAmNe{GU?1ln(tB)&PK4ugXumw5PrO*u|i2JSqe@ZOzovDqZ~86mVFw zvK$;i7v>j)`EjyyK#8%;u~?f925m&ugXpucZ|_K_aPAi9|1EY;gYRWbq_h@daHE#= z7N`SWhV@-2L3Y#D->O-p~Rl*hO(e>Ld0^$C2Kq3y&PLx+c=2Y zNow!n;$j$by9fB&m&p+?;~bg$p`{3)nQviZI5IXi7P9qoSRZIvF|k9?;X~|6_~~4| z^LF=~e-k_>rj19X8vJ3fpkQ0Ho#A<&R7b_LIK4v#l9K$Kc9icak_4oMgL-eP3J0C* zq$q=-^4TXf6j(~Q27M^0`^~auikh-8F&W+=4n?d?MbFqYc-c}vzt)y88I^_Da8h~R zF~S_lOL<}qPD(@YBu@|8&zC_6dKFl=gsYrp6BBkbmN|X*bx`fWGSJrQHj0mTbad>8 z$mv9?Piut?EGfNQ@t7}Le5QHg1r9YeHDzx_@EB_|kOn;_KpNu+Mg>5NL+f@}lBw&) zRvMYGeA3cTeP|eQ*N7W46xZ=1r{;n@wzT4{tU4SunFK;bFWMBNmo}FRdcY!q$yfXwOZFjykca$^>K z@lNCfO|hi&x|N`}(~6Z|FHV(3U{FKjB*n-w$kl z{{f!lj4v-=V0^~gCj9~J;}sONYx=WQ@~)-v=&GDu%bv2r!!g2<7!b7st{L(%DC`k(mB^-}nw z2$n*;Ex<3u6TidT=Ga497XVsLaj>i^bI^M5i*#+v3674v0A1altAO5l9sskw^sVIc z!QdAqu)Ns?m+AmW_`P+o+TyvM)uNxAW=Q9GV<yRkv^6(t+hLP=r!@ltK;E zaC&Nzu=ZZqS1lt+p7^Ac;jyuOB_PK?$=atoS!6*!+61M+t=-exNW$uwC5A*SEBNC^ z!&ztfg(G&Sf{qrQ2gDTva+5&!^cqt9`ff?etBVf@UEmuNPI{4<8AZ+{!Aka_nH%O; zGxc<9PM(MTnyh)OR)tRyZ20~ajp)n-QDxd8R)1eMhanBM#DvOw`KiT*mY`SV%btQA z*@K#+H0O0~`s~L}zx1${rjPjDM1;p%W0Bi#)ecoQM6e)u5dE6=$P_)#8uLLY%i!hp zz{P7o)eyL%0uV(Gx}?4pQgSkYtd-%qL9I9t^tAs$G>T4^mSf7)a^Zj{9)GpL$hWHJ zt<5cJZ(_0@0TBPjRU41iS*wqXj;0sgFFU|>Q~J6HER5)ZQja%U<%qT$D48%${R_gUHX@3l_y_;Kw>y-=eH{}{M+lsr$1%O z&8s!K616fMBltl9{ICOJJE|Us_dVr%JYbYGP}v-rn4N;EHO!kjcb43=iG< z>HOpal~48T@t;3`Fs|9=$s#H%A*iE@4qx_9maz}QhnLSReXSHZ2McNxi(@mbS{5!Y zTa10ANR$@j(2#~T1Gy)uw@{zxX09rs&2RD`=bBk5RWcn+Iz^Aikc@Lw}==L9|($oINg8!xum{mlo7e6LZQhg_O4E6HR{+iS_^&K z*?Hku*un-lgVU=6{b>R~ChC1GIZWtfH7y${cDZ7;Outz;{1c3_Kj@ds_JZxu2zs*C z8F+PTYs7cmJ=*%7M5?&@b=KAc$>uy$lc{3yP9kgrc}3Xv`BJo5g@tdnqa}9&`J1m_ zv*{ZeI2@_`WqSH==yit%ko3|;WUG`(i|;~u;g><>gpmn+1b;A z9%vB7UiO{sw1oM60*N(#hz#+Yyo`#Sz{?6Nbu8Hkna1(CI~FBT2)uQb9Y{QRt!GL3 zWm3}-_cK&^Y(P3$m;<+C7J0b2L4k&P zq5AVbR#zIwXWn*xLt|}dfzc-gV|}BVD#gK!+=mS0!vt7sKYHp8N35*TP|@JhQbDe( zn@h!I=l7Vync7t{bYFiQT*?iFS z!v~j17W4RG@W0{5#skjS$@lh%eB;qeu#Gl+J|~(VGe%`)5BZO|c<(nKt{Oxb_Nt;9 zuJ$Xaa$A3_SuZrzAx?}1} zEAv|qmOgbN+^um|(HP8QRw&!~$M=!r7U+ck@R64(m0)3xWu`8yJVLx!$kc;v#ClFN z2*zH1nC3G<=t(5+0tSOu452N*zbiimpD}~exN5vxahMgfNU|CXj*&OOK%23$2SQyG zG@h7cs&Rj`;6Jcxyy`C^ESwL5HE%$*=SLlO<1f!I+sqzDnL~4C#9Q?^BHOEu4*}Se z-dzT5SL4wNwk04;^v-`jndIO)S_qA2w6lYL=b>+SK?(-~&B85Df`tzwrtrXwciSQQ z0t3G3U1jB^a^{10UTHfHY6n*df(gT1Y?0gGzc46Ln^f5#z!(L9CooLWW(osyRFMvhj8cx=)Z+8%?Cr#cePY5w*kW*q=kkRU|Fkn!3Qa{`Q{1CWNg zf=$c6Rs7KbbNuW@!Sv=0NFNvyt7q=)Rbr=gE=nCzWf@p7=7)wKy~CfYC>)KgGRXwR z;*0Bf&)|9pfAWC2K{!VYbL<9}=3YV!?ru@dv}uWj52w^ImdxMo|7udmn&9E8n<0|H z$6ob;p0@(>3kyevPyC#LcRKW-NayD_@8CKXx@fFtJ+V;M|+!U{0=mtZ~0 zxr!V#YndNib{mOID$V?2D)G3kM1fjj5LDzbt$uKDuoCdvl@XX( z^uWV!6AK%MKF`#v8kd9>c(}L(gB%<=Ob9&uF5ccV(-evlJ4wo!vd*f2v*A_YpwZO) zFW6f02?F1n0++;5tv0AjtedZA*MmWPEiEi8{Fe?g)dC8hEwEBo_?`SCcwzk$k0xlT-6`oXzx0~<0T(*YQ3o|k@axgVD?e*3* zX!W}uBP4^2e(RlHI#}evHF{21BbA}?h`*h)`iTFV ccJxaTWG!3dl~5wv*h{=LDf6i)^Oh}3B1-lp z%S;o(WC_LJge2=vlr6G+)AxPnJKy>K&$-XN&%Muco^$UxcfD0ahoEQJTT&B9mPPTWZ zn%X0JC!zyKYXigQomv;Qd;>?TYX7jK>-w>cz4MEsIo}J@&QxaRya@aLsU&lAdGGAu zIZJ3Q_{hsP z%bqL#Dz@ES<~fgLdX%-l3U8Aju@R38!`dr8By8YD!IMo`gkXG$d6>SRWD2O~&Wq?t zNHEg7c-H}geJkh|3{ZG_pai3nOKR5q70}!d1w=j8T-LiVo5sFxEYix2)U1{E2y%N2 zAY-3!3tOMvDW(;r(4Q#2Gz~ z{}2H?sm8XaYH@$TXVxf=GwQ=!b0&aYX#;XP1R{AjH!yuPs3DRpi+P31>Ho1d7FtfN zMQPF>#kLHK2aB^1BTX?^>T-zE6^9Exh4QkRd!S85ZdGl!<%=y#D?sA)&%u6S3&xpk zF=*>SOjozl#g+&j)|>cDaP@s&wo_%3!{aetU{nX=dy-h*8%Uqc$AC7W;;cf-`A_^R z^L7$+_Ln1Y<=j}A-T;c)*v<>6F${c~sF}e8!M4lw;=LEhb$WBz?-0|H31%n1aKma| zw2lgFQQ4f9-0`=8#biEUI&<@9f*361j?hGRt{k_dq^^7^`i?MH6tYQa)`YoY2IjE> zH}R|46&2fsDMNJ|+apt6(Cy69qrYV`l;Pa1-fu%iq-@cpPkxU|c6o4Hme6h5H_ntu`hF@|^JeZ$$!9u^N8)F^f{~KS_r~O`l2{cb4?$+Jl+RmF5YnBPKX%6u%A zK9^-P{Nig5EzA7Grp}uhc5jU75*?6xEi=A9}vG zgKd?W7Q3GOp|Uoh;N@L{G4P`*+iXKDo`PCQT#ED{Ppy~Hg;|lLUhh6kxz;hb{EjAS z+kvdgz(ESpvLlsAEAc3lqu>a=FF&UfCBQ(?3Uni85)JQt0+9=l;~g z{XuN8e(qr*+qSo9hPVd=_0`4<&;(kYqf5*h`PIed^|?{b%izuTQw!A0npDfu-6+l^ zq$pX&x8xpHpjSkIf!vk!fOXeVC`b;Mu+3hxe=td%m*!#!Q%lDBZY?;3c+@1a3(e7J z3K)-69hq5GtoLgfqToPUajHFv&3;m5&^K{2Er079t zlS>mx&P0t1Tic=Ah34uAjKN#rd2RBwmeB+}A@|+F&usGUIS@S^OzhZsjfHc+g?eny z+#hjB$ssO2l}T53O_YPN0*B7lAy!KDo(wA1JBFAy*mb#b%dRsp!BmV09dcI5U!2DLB$EQpuJ#>-MtKNsRt^=ifswhbbs-%6E zYP4s#(Qy!sio%xWT@J`4lu>E%=BNVOS97Bu&$U>hD4-HW0(CucG(_$y(;01pKXgPJ z3HN-M%E;chi=lw#UrkgJoqCCy;>d!8nI;7Iy&v9OCcOmLN&UZx5I*y*6pp~?NbZqMWLuG58n_! z6nEarA>_w_=3W9^QQ_uvylyW-DuNT?0qOZ082dBmX=5f_3ih0)R78G5O$%}ideHxS z30*|xC7N3&dI5e1OjNzp`P!$}2k6Ttp09giUSCo|^5`e#BuL@Do06r`?Crig9idGF znC6tTUkTA#g5nU@RkQI%tbl~v`In~Uk1Lu(4&774ZFb5SMtjzf+*Of0O?<}P^$k8+ zP1Ik}hpyyBvlVwfGDnSiK-opo>x`{;ibzK>PCjJcQV!8>u?RMwjHQ|e$GtsSzkg%W z4`H+mnwR>dg`btcH;P!lq0;AEE(YZChGKIn;vk0$BR#VH1TG@ znjGxO%sFuHI8-9UsK%RrrYYi}O>qP97o}Q1LQ*4R4Ap9q?tZYE;iiDBpoa``T}W4c z(*AeQ3J(z&#jS5nS1XjfV0|6j;V3wCSOk12hl5DV&o!d&fzOG6-y`!H?e0jV;~{9% z|EUubs@5-0Ll@Q7YExri(4Pn-?pvWyM@}1YtoF?1YZf(Ml=c3}O9W0N9WRX`IKwv(La=T8`D83F8k z62Q^s|H583cz|)yDO)vu^C75F%Eh4&hpKrXR!|!I#)S&TIgLhOmRF@xcq;}>RfJjd zWC?n;h_%zxNHQ1ejsC69=i6fLhyki#5AbO4or6s8%?TT*vru}2jb5KjdqApr&FyP` zMu%BgJ~^4A%m_oKHyZq4^GC?T)lXB&{Hz1G&M^4Q**Qbnj6*Qq4?Yu~JL3e%C?UN) zW5jxECgM~{AJ-_KrljGU_R|91&Miv~IQP>)6Ejm57H6UfArF1*dxs{3bC?Q8!|%^r z?YkqP%T##Y`zePWrVC@3`~*g5b=pkKs}r=s`lq(*`ga*^SzZ@HqALf}-Qt8|CjW+R zm-X&T@9!E0{jV_&ce?PbvdIo&toUACX3|F^SIFES8d=6NkYV;#A1sUjK7(&*XPgZ~ zBKWa{wy(v4esCQ|bKs`j?Coy;=_Q8^8lmv^yC+G4kiGn1%i{Y%;f=?0_8p9-Jc+_Y zq_Jo%Gcq-2HR7Fta?J;$$tP-pbfXSm*nXZBuDH(W;6o3lmLGcL6*s7O;ModXZLEAu znhx)&!oTdr5r7E>H!S*>EMr0@#Emv0=ScyjZ2#1y&# zR)z@d>f>S}EjD+rcP;a0wfhS}yPLaJI=|}COn?=%NKg1P0>#VTSP(-U`S)oEfdDbr W`e5@R3Blx!K) z5QZ71#m-Qcp|Sj*-}~wP@c;VW$MGC<%zaw51f_gDvnn%#I5f;av9RV;f zD;SZCnGs&u|H&YktE{krC=qDd5aA5LfZ!F~fuTmTbH{f{XipKf&Pi6>J$v?SYh`6+ z`b+YMYih-iJdFEFk;#y^OQVy^B|W_e#^>zwMdpg-TUZO86ubmV`#qobF>Nuf`u;NC zMC^fL_wEI26_NA+MjkFKp~d1DtxSjcp*%*9P34x|cp_}Ue6{`)l@%?+MhCrMHNWY<6vFwE++|^9 zb=6K$SWQ}7oP2)eNhwa=1_;3pp=w>+ggW2xzyYco_4x7MS>C4~O-zi9clveCELqfv zh@oA7(Oo&uVJ7-6HwTrv46Vj2EiEy-A>p(A{iZhP_EPg6z>(epT@=h5mXeDJiL;KXt52LOv14 zT9}ac9NJoiL8Y9(FoN$Kar1v|t@MJs01w`ca3IzQf@ZYH?tQJkh0Q(P1HlV$|L7MANoY$`HD~pknb4Ssw^4ebJ)4YxO;0Y&QJx z9Lns^l4)%qBl#obio(hjIjPkA5Bqu>JEbQwI`0x5^xXUXsrp35uOUYuRyM@N0=v@m zUz7tgJ%v$blQS@vO=vcK_xj~lbIw9QtCz8{@%)2=1UdJ2(A*JlSb28BmJC!k0N4VV z`r#XyP%j|DX4Z=G{N$~QqgyH>f}eG`VZF&f%B^#RE62i1uMV-QnOHq#l#+)Lhl1}F z*9=ReTA6is#STNqLrwSZSR*JAf;6i5ob16`(F+yF%tz@Su!$yj$T~~NknFLgu-ZHC z5g+QSvz|TDa|LWgAF>6kEv z#X4cOo`DEdMrqTfhOG+MHfCQmE*eT)Js_Z?m-qt2$3{Ck+y>u9nDXGSnDvpl+8NJo z2<*+PFs-8MZ>vZz(sOQ3XFyf)BSfO4RZSf>s8uz`w8sMXOr<)#WU>GE@85pjeu2z@ z*6`WhR0%6BTx~^u9{t>Q0)ZOn;Se49^QLfteWl&_+9KgrmHp7K%{?5ZsQj5Ih#nc> zR43=if`UBWDXYyT|1m2IaC_aCV^*egddM;;ndj-pL#5uVCu{_XddurJj7B79N_u)N zSUN>7YmF<0_K#;z<}Wsa*MAnXp7&`EQC;)wRbZAN$=u-MnQz~~)vkW~d<{J-gw_cB z^!I1OJy^gWLZ^67TC^^PGrBLDe=H=CBJt?t#yQ04*ObSOLUG??fX7KyO^I+^C40wX zmr@UZO_5=otxPcOJ}v0s$kL)zc5Vw(0<(TecbGVndrfD0GfCJD zb{&jhJikX}&qpHt)NvO^ZoxssRdFb@3CnMbAH8KJKd6sD9@UW5?}|wqx2*&}oS0$- zBPyP_X>XjNA)sEBrnzJnWXLHL$A)+Qz;-1%?(QSg*PQ8TX&lI%I=K@sdxn+zve?+h zoJ7veF~)|S^gWO>^!vyVO%z|Fri7=9fS81|^h`xY?8G~lunc@fM^y!ng&>!yug_WV z%w6Qfr^}Vt8um0+>GZ%E{jG=SE;CS4x0E%JDmD+kKQS>aRA|q;!{C0!U9_<>y(Hn+7ke2Mlsa-A)7Ww8+K$F z*5&yZ3MiK>NI9^fMZkKOMCg$h-e2L0348l(8Ha4@C#F?29T3yT$!RQU>d+F8afbB5 zpFjkWx*o`wWj8>98k=j6g!PbL7@3NYW2n2}&EfeY@H$weRJitp&qW+VGNAvA!^j`| ztp~BOv9-j7&73x-HPQc4F4;9_a89G~>3)ri&c%ycCI&*`0=G{E!k1)W@H_AaNU!6$ z3?t$yH|>Pyr)6u2fL6-Qxq#57NHvg!rR8B8TrHk{NEZ8f$f4a1S4;o>;|KU!nN4dH z0&zOoUXWw}gi~aN7?$es`rF>g`AFEP;(}}E{)};2!6m;LCb9bar8f)K{4t0};ouXTROH7ex08IkZ_**ymw3;b#O0L}^Z6D=1O{f*SzR{>FdO{w=o0|I*^yy1Kf! zqYm>j(sTULy4Vjh(Ih+}ypQ-+eT1A~NVgAeDR)*I?_F4-DPPUR9=Gmi{|3p z3TtmxL6h>Z4;vqwC*PRX>83Pz+Mf59YH2KJAr<^kgB)F$-#fL9F!qN+_4E$zC*LUY z&@9LMb<*2bB4a+Q>$YqBOGiGr zAm;n7xW4{N$GpR0vG&ep(leHnaQ)ebl70wV<${tD-+TnB3W1dwTtQm)7Hr#YGPqUB>Uy8+?}+P>yKE z>8|znVftJ#$Wpar9CNhxH5ouRpaW|8Eck2?Zlr%p}btrg)u zNPoK57H2D*-UQ*BZR=o-i;owraltpO(ieKg9NH`>7j);N4QN&BBf2?fm(~w!!djV+ z#NyDP!Pkm^pJIK$J2hSZbfmCpjaM~4-n}ChcU2u1AZ*0oFnHg3$W$=CX(xv_idqta zwf_@=!AEryk;ikxw2q$H!hf6E)g7f8YQyP!!{qug1-72YFo$+`cOfXpq)?b3>@8uH6~07=HT~KbMtQ_+ zGU^q%Vjj4;xe02bss;J@+9#-fW>?NHU54%8^tgBL9jIAVQx2}v+FKUZMCp;iS*oIM zTj6TSy|bjbOyArCHQF@&FjMQB4+}y6RZzHCrrc%L_oNGFyZqj1^Q3x#r z?YCPwN^W5q5Ckzt;^y`d-j5%*m2tSxXV9q~rFxz|d;UVGEWA|vNrLmKq_qTxWZ-oD3O1}n=R!}0k;A&{+y4#X7GwR*l-JI2TI@(HJ@RHtE zi-eXzSC{C6-4M7J(v6^pY^rpsQPaSk06_8;b}c_Rlhk2{U_;d5FL0pHL_XS{tC3Tg zx!f^W3QtOcoH4tx;qlktv$BadF~>4Ky&)u+-m^m(%Bd?uWHZH1iv73QD2yFxJIHQ_whd{kw*2bO)7qh6~buJ_I0Ut+-j}>IAG}J{DD`c-QV;z zDLVjS$F%TWg1ckDrm)-;cH!Db>#Y3dSVDM;<}Nr%HdFoBJoB*;N<|}4uk4idMpq{R zl#P!L?1t>eU?XbCdc9>=%MP+UZYKZSN!Lt1gt{Tum2w8%n{D7tTKf8teMBPhJ92z^ z?%h^K122&z(pMnnC2~-MUwpAb?i); zP!38;f108{FNgfZ5>)A(V^r7o5wGxh_%8OA#3|LAo(4i}MbZEa{Bx>KL=C>{N&9~B z9t@p`BWO3;&KVbh^n_caA5BsR>4%|cnb$kAzXUOtG_|el(j_#peiy3dH}|YcPp&4N ze>ce&ey-*bNpDIOGD;AQevJu5HgQ7Io%~zaCMg6-Cd(O=fOTn|Giih+2ORHrf}uzY z+-`sQ)hiEjmit%g9W#(&xmt%^XyE^Tl;T>rLJHMxFj&9uzM(PXXCHDjUC2{F#vhb2$7 z3TWV%7&i>lIR#e}zwEyd9!wz8OU9RrziM*%U8=J@&Sr4#Lj3ZJM(?U`;J`a4R7DHH zGvDsz`y7;EYy2Q|H$p4_@U(4?>2_Dxa*1Nn3TNV0f;|s$Brn2(Li8>_UB_y{@(Fg*P|p0aGq+v?7N_Mz;D22rpEn&7{a;;1ZVfxhNhQ{YQ(oI^kx5{U^q(;&y^AD~hKg}S^xj_hVYTp2EdD4s>36Al z!`~AN9<+z(BAmyVvl`qood2~Y@MqtxkorUw-_>dK@nmM=qy7!5`Qp8kJWtWqVR|=y zu}@ttlj4Y6rhjj8KS`HvVcO7;wAGN~`8_Y}1r;Vn(wfrp0vWcLX$<<4Wb^duNwyEn zECCH*%o6pV2T3kjuF;iILXCRKg+YwzXnuk;K>~yH&DJ-mnfrJhF^tP~)(AZqX@Rs$h#lHti{v~yF|3DiSp!e1HK}O|{?USfKoSuh?H};G{F}9x7E#Bqh zr0)>uG?}=gjw`4)M? zev2|6eA=an(``Ovg*tm77qTp>;v={Iz}1@HS}X3u^0NT)!5f|4bGc%xP=$y1n!t_J zS~$Cp+&NFvCM~UyyqYUrfrbr=%lilkcgF{&0<}v~n8yLnG+FAv`yIytH#s7AOwR&R zK^DPPK3{OF@6u;q!UWL$Sta%S@E;NYgafQ--1p0)qoXUt)sf@~ z)qYrwW~mKkOn`H@yT6r`%CBkWBJl@)UmsS*886|@s#vK@{-+7Jh5Lug`*_vLf%q9)np-v7h6F&lpz4U)hjlr=PmO>OksK6*X*gv0s-xiP5D*ICi)bHz|7S^fMc_v@MJJ6B)Nl4cUa-9 z-a!oA%0~Dkt9HX-RYFj!3NKLv&)VmnA1e}Sbo|x1q5m$T5Hi7plEv<8)6_M?5JUFzW{Ql_ z*UD_O2ULJ~N=SrpX+!zCSE4Uu?c0(_eL-(2x`jz4i#~xTb81r4(_Ojq^H7$g1v|v! zKIZxC&j&+%VlpyaF;j;&vWbgZ=A|Zpykz|H$vSs9hsOiKSH4l?-ybj-j9-Js7oz?u z-7i`9%(2CN@TL;9>f+<~_$aYmrtkxKRMd#o$q0 zeJyS6$+0wkKmy@^f3Y~+5O~mjbD*1Se0sgOc>6gZ@u+PKJPFLypI8!{(#&(MiYoGr z%0wqSV97&w#qLNNeU;BPnFOjaGjY#h3jPm?Me`p^HrR4W&h3n)GrjtyhQRyyXOeYF)iL*(z0if3g^ zw#BP9I-`C#%WG4jAPJmh3G1Tzgn%3J?}rIiFm3if6R;V96GmUV9T!fo@xJNcQy|?W zuFg~kKHI4zn3`xnBV2Z(K_2c_1$T-ya|LW`?AM2NI6Y@yfJf<_I#f$9NWoldLCDn7 z4j>Qhup>oSt`6%Q%c$8ewNpMY8HbrNo7$D%D-X))M5|@!45rsxUi(p3Mo9TOo5Ovu zk6za{5UTFi;Y>6EA&`PB&G++@xd^qry}b>Gq_a(td4HFQ^+J__3_oN+>?ijX@TLzU zp2`e+K910dUDymgc(rn7L{tnKCfTCg4X-cN%pKmK&JE_QaL-8B`*c@YmH255Bw!=1 ztVqh;xu6I^hKO!-qy!9Ps%6wSH^J3rp9LR3UQ2dD-r+LzRLE2>XndfOR1#%)B&fGw z#NJD^AMNr|&rH3QZB~{ESaz*x9&~~xpCe^S&9XFBK?Q=W^(j2$xP0zZD*u6*4EZmCv55Nv?>It#k$Y_s@X}N`xt5*5Q)E0 z@bN>fznwaKzOyUMWy-tAmJx~im-Y2Kl>zn8QI|sgN<2KdYs1reAC_aPq=A#z+}odZ zFNl|`{aWL8#QD%n{`VsUKwBLv?MP`Q5wD8DZb)0g65+-`d6qcf&FwX|K;!b|JwrgZ z)H$UW`++FUn#L||p^~?{cw8xT1f-BXI2TqA=$)e|3=0J3%BlcPm&;1qdgD$xCPkoB*;2KPOdQLjl~12O)d1@1l2;o6&|4x1-?uDrC>L4zpYH%6 z;K?xjuY)-|2Z30~W(j_mpenH>PCujqKyk^+(o+9fuv%up3tql4x*C@3NbvO7)MBAT z;P6FJi^cJUHplyLb!7|n|+!zn{ z+H4D=*6T zG9WYA4EXQlMfYsxn!e@HQo*(Q9sWOIk^JknXIfHUvb|PL%73(wC)4iurkXS#%O2?$ z3e0)mmCBNLuJWga;^I%-xhiEfH?5bqgcx~q)B02$&1_9#=Kc27sVVAD3-RNQr=@1E zP+DAP8fsR{IibY#r{G~DCk2HZHC{8*BGW2Gr#H3PK5=&lI&s?#*X&y^|7sQW|Fy5M zdpxfd6myl?qJQpL&7%HkyGQgT)uPYaE&83Hm~YG$m44Nt=j;}pRxLVJDOx#oD>tLc zWhx&iiivDf-9lqPHR&-l={PCXJ*c{o$9akxO*#5SqO5x?$ z`BZFTd{?POeS4HNG|VJt+YPuuIR;=bhJ|!2Q@Bq~d zuYKj^*frm{Ze?;MTR6Ztg)qi`nipI$y&0?M>xnIySj9t(QViqtvF5@>lRIAgNt@Ww z5B6LcZC3k@DvblktnKkc8FZ=_$Qv+4I=`{&(LnJfF2C+b45g*h~s zLkznRT+A?9jB>eA7lPG%g(hFH%A^ay(`fU!K^KB%#?WSzMsqF%2MEDlV=e^Mj1z(} zs!h2NJV+?+Gvz|?DWUk-kPAU0^MqoadNVErJ%5Cx(~JwjK|-?Mj0?fLgybzV%PM{+ zB)^(jKhjS~&Y4*#GfhaQtSACyQ?!gu-QUXNW>iLx7yAQ)jtfCIA?YyVLQrpZsMmHO z_=He=Xv&4)0YY)FDHnoj#%#VyQFkHOPYCu{aUsIK?i``PpbNoMX!BU4g0c(2D!xFI z&wsU661pyg{*vjkNDXxt0<$~1Jlx_hADLg=sd@6vY<^j(N72qVO0 zn9cg`fsPBY09?WsG4He&>$?YfF2n+`l7|>6TBur`%I`4H$Uep>gfaHgkm~O+P|f{( z#C(oB?1$V()e`v*19fz=pSSsmex|eO=XodIWu=()AG1Xy zaE8OcVc;-u7_xB~I1C&H4nrUg1BZdbz+rHVycZ4whk?VuVc;-u7&r{czH%5++k uhoQSYD7SYQvW>R~?WWsePG0L128*@!3zv*}W{C!@p=XqYA_v?8+m-pxOdfuNjch?gTuqGG;0zoiN z=rht7vuTR*QjaZw?w1C+Xa@{dQ94M9mnqV@QiRiQ(IAk@t4)*1MW|>=!QC;(y<$AV z17e7NQT`wzk!TWp1s{#`i|{uIj|wcDv(c0mvN33TY;4hNad;>|+}&lzv1-c;?frX4 z4$G{7kc%>x8(qGn{}`sW-=&415us!KaWDlma!H85wf6gmH^MgDxk>t+hb^K`oFYaZ zQhf34B+AcWvhFoCJITXHu{^79xu{EH`mT@4>}*?8)AWiq*X^Y;_Zmmy(!N$~*DkRc z2cz^`ynA(Zb=!|_>j2=7wO3{96jy^j8yLVB4gor61`M8ybVI_z;t{2s)Hy6lKGL`> zyP>IRd?IdapTH0B@&3qjtC=wwUwL-z+BG*0`T5>v!)-lNQOcd2oh4iM$OwDH5#_X} z$ZZ|E(Dc_Ez1}{LE5`bkmzPt~pYE|{A-0QFszakZ-bzUF?V~jngMADJgDn<|Er9qK zi!YQn>B`W9{KxU@%k@K#`VX854my8+#Q3?WU7lo2y%R2HpM`*?d+wxpqDt^GOzyxy zE{5-%s6ZMQai*e>ZMGciYGh1oY^>JX0;?CEzn(aeFjscPWtFk@y@N&JEOsE!XFwxc z-PqVT+0oHa)XX*2scs<$JQ-S}Su2vb^*Ivz9SvUb6__4FPoT7;Eg#gh{(R#WUezP} zruy)^clCR>b^OUD^_QK#C2!y7eGux_0^PY^9f1W0`ib z4Ba4eI!-I9rKqLwq=dZZ=+&=#eADnckyye!pU;otSx@Gn%%l&&Fn3O&jEc@VYD>t! zmo&X(lEG|xpLKILGqKuz0Ls&p=3xPimO*!!=wY_jU#VdR(3T!%qGhmYOs1w&GSz^w z6)U)dmx*L+8No%*{>=jyMHu@Up)bE-k}ZW0NQbx%5ApCjT^Q#WKW_KwTnEj;zb)+vSB*49=95UTJx{$Kg+2^+h4@O=jAWLb70N~DmO()lRben;M{ z;mHhYio--JB{Y)mh_*O4yP7O-o>o0mm9(C8jS_I!ZacdwbdQ>)!gn2*+z-X?uIL{= zczUSCNIC5`%7I9K7(z@)*a*;I)>KxH9iq*U%GCHE=arq`)G8B*vKP0O9MJjb#yZ8i zqH5)tD|qn01&70x$_~CaY%{?wHEA5WKr(t;u;eZQ0|URX`dK)ZF@zhrn=jL<2k##X z%^~N4Sz919JDv4pJujjM1W_IGD^M!;18jr&$d+qXj|E#&X>q_mPL-lSH!WoL0g1?Qxm$+x-q%xBHR5t@qKXyH{#bDSH--k+P%y zRIR$4wYu&~dBA6Ue7rqya$^dyxXwqZ-S-gnGmht!#3u;PrBXuic>J@*>d6<(jODJP zYFcsEse)oZbBW)jl&f5R+t85ktZ!S*q>hfxDT)Q;k$AIR1PUA-QI@+r4vj|v{3u~^ zPUli@)*W=tkzHPU;TxF8LBSd`rLc6R9oa}17njndM4*D>OfgHa`h0&7scUn;F}oou zoV%4CXd8v?vIqx7(_}vbQI6(id|M@yJT7925ej`gk8$SHEs}}gX?a$M-a!0^Vsp*B!~?ULoZ3#*bOQLhl@-QZVj))M!T$;l2^p5#Yl}7qgdGI(%{Q<*=&GKf zpSh{54VD{3cA)N%v+q-C&GsGo#}CHPfx%$6Hw0u}Abi2(G$%MKu`Wa@(KUUo!51N+ za#`UTg0J7)$+AB1g_@A|tjiDVwR!p5gJEySv9lSe>CVY-Bl6eWU4l9_1*pB_bMrEF z_UH?xbIB%1h|vUGLLK%h47gvM?{9vScCRVzQrKAWTrvX*q3OS6r_zoJ0j3fkz4*xb ze9aPAY$GnBU`G+mJHfsgzu%vLs`lKu`mWihvKx)^<#;{`_BHHhX%aXMIYMN+lFhbJ zpRW2{v15pDd~h@gQ3n7TJ0ZtD zoLZ!at$by8T{S9OHU!^d2c9qo&9j1i7u?LGDUoU$d zLL-D&PtyZ5y$sY54dvxH42-VK48p6!;SZObxBQfl_rE_#`l%5&=l?GvJ6Z9f&niR@LN4ev zi!j#9jB}(1YBoc6bhT1p9J2_z{=dlJCNhYGc;hL!|Lt@9)!~&!y>a!5_b7s;CYHF+XlU6XAjF z6$k_u@0$2LRg753!~)M&W=B@8rw}Hie@bnhlb4ch0>t_uMmc?wmR2Gv_3lnHX@fUt|XWfD3s~ z7X<(i*gu<v2SO#v&S?5r9a`0*L^KlyogTn$y?y9ddG?xek-GY&D3r)vz?sdCI^%tbhy+7BE z7B4-ep_J!^Mn)S1kb6rTLZ7BivRh2N^uERFN{%>er8&*(4~xrZSZw!sGm3xYS@n90 zPx~EZy(M<38d=ALhk=t;rxi+;1AM5P*=^s9IQV>{YqkU0+nzG%csl>g@Y%fA!$Xot zZefy1)(ffhMvY%Gl*C7h(muyrV`Y00@T^(KH8B@JP*30aD@ffLcILv)K<|7;zupe3 z>Q)Hqyv@=l>kC;*T~qO}lIc-jvU3*_Mn4S5qvoZBA(gJ^+xbF@EtgkhSjls>O|9~Z z;G$QY5?uEAfV8b00B^LuFg%ahYi$A+9P!GLBn3imkAe-(eZ1rXue5a{2O?^^M?GdB zWtb5WrLH(PuOtYp!{Tp=oWKjKCg;MVzP>0?3Ue@F4Fiw4#NoA>~HFt ztYpsgFu?{S?jwyFU0l>ao1pJcvob6CZp}lDc{MXLGLQ6JG(dr|YQ8{mXA?nTbW%Ar zF@xV7AimChT~su*qpbZ2eYCrIbUU!A9q0HJJC255UdpLfMSkvy&Y>7I=1tp=IJ9{i z>2GEWAe3Zr7qxj7K%ZQEHW zp*UhF&Z$zmFYnk-yKjWJ!3V$>*)Vac3D7s8$#_ND<+d30N*&cX^$qK*a)5Eub2~R5 zcCnI|_-nKvq)d5T#KE-SiT7I$VRWsBB3IilisnZ?y>SfLhbvvBZhFV^H9Xjc>wMQeaH|dg<*}Cb@oiAxt-qLUiNSIcc(_?x z?6b>sBR8xo6>1!%_Cc_&M$ZndR{2=F%d_csUKvi1cuH0)4V;G-{*)T}-gt)IYiQ@c z4Iy+I+uV??O#l^Q-t5=qRQVAJ5hoQcuSAIPEAk8hJT;o7ZkJk03Y_0cSIka*27Z0t9s zAsH{%C?>SVk&Q>qCC8V(TUTj;$Pa05qeDV=0I5)WZ!$WJuqNx%81<%hz^1a28CI?( z)9>%R=RKTBsa65-k=j9PsN7;u$#E_=z)1pW}*e z%#WOf?o$Yf#aO4qccPatAk<_8C4P1-QyI{XiqZyPIC6(Tr1&`-C%}N1dYRTH@Mh%U zi3U{;f{*MNo>+;&nucF3!Tw6MV!($(n9%@TiVp!!@P7z}5r$(MhL3!iQfy;QuY2d| z+|n0@`$12LzlyVK=YJPrM9>%z&gM^0CN0%hWgu`zz*Vw8ceDy1h~j9&%L$SVs)!rd zOOfQl=G9p!17hM(>_^7046f0Bhq%aWiiF*R5r>yUK07CGI{}K+qDY-jE^q_s*g})e zRCzg$+Rsb8Kz9}%p3-C_3XBAHzv)v6xqE7lK?C}Od#dw^a@?-%Y6K{zTaQM53j!4L zMUfImWQ;p1)EpGE`zyqNff>EDU$OH!>9G|ura3`JS7D_iNEBq;E?-S*E%1$KFDmHX zdy`gbn=D!gi3vi&+b+4hiGZv=9Ij?|wXQb#t57MKozp=p!s)5sz{)|Bi)$#Y=)I#> z*(0*_6D3M_cNB!^TEN4coLN%jke@l{aBOc>h%tm3n{TpX1Ot&~2;he4TBzqIGo*!e z{`9@A^r?B~EiwO&I^UM)EB14qaGn8;R5vyCQv)=$R?9CQZ3npCJGj_yICPb!2ov5l zKj6;H5*Pq&I_>T~{c+BD4LmeOuQXI}vMO5_uN!6(^2_@Y4o#3AMz!2QOhj$|hysJD zS_Dx-{m>IBHo{ss@g};a!yHQV>ASI%3`J9AhWgxmqo%^rrNKN@0z3Hmw|w70MS!&I zUS=oAPh8>>zO?>5k?Adf_YdIREL#0DZMWND_KRj{QZ_|sMwk*@g5PxQ zZUb<_{=XET$Rj(mQv5?J*6&odQBpTAx{k*^&#)XN-+A}7^GoO|)2<0R7~ba^NrP+O zwCK;huC*FCmV+TEpJ}iP;e0>x?>}$I2hlBzFHinB*U1!gb6S=0&O-->#mij@*yFYB z1z@vhUpM}&I(*`JU7cLwG_I)n;k5oPrlo5`ZkDvObNxf0fO4)<7ejYc=U!am&TAOf zgcRJCm3>{gcVU7=-hE8rQ0`n`IloTCwlY0S+M4&3#nr5MHkb4=?}fHZ^od8*VVxNX zUwq4wpz%t0PrlnAvB3D)s`sI3`*U&Qxs=ftP(4S|#|QKzdEk1hKhf5NxgqNB!;!<^ zucmSRIp6+35yL>Vzv6?MA*ppSZpVi_+WuSaNI*jL8_E_(n_N!t>E(n6JC2n+waI!| z+!}JDJttnj6()(M2ri2F-w8wiGEJG?C6qa6_k9^#*Pv#wgNGBUM2&@wWFGcibE!5(YZR)a@^gZAx>%Xs9+tRmxsJ@$2S4ah!M!<+NP$x-?3x#lkv4<8S9Z-Y<1LA<-*pIylzUKrvXRC&nOBzV_eBk>s zt!EnD)pW!6Tcd;hF#}VZg*O6Pr!|ubQ<(+K@cXqplvUWf$mi3awJbbp$VzD~^zdPXZln{vxTLp&X}^>d)_4GDgdYFD5&Qq3{r7qH zXL>WhZ=0I|xblIz<776#ISy)!5zl5sI3|kT{}B#5wTFUz1enw7vIu!L8HxhHZ@I6@ z1}uoNAkz2W%iv23A-RmW(rbU};}PirgG?AN@|(}&Li}xIZ$L(R=;g#bA-bnWs;sgdnq-DqZIaI7blK;D#>>y?7`P;lS^nvkGa%>FE zaO?t~hIfvCE#72B&qv0w$zD#%$-LujvqiD2XKc>E!Kw>u&HYrt#fu{8X}qp^E%hOR zI!Oh$oPP#6WYf-_5hC7Z@zx5u_X3a#OB)LN$~%!y4PSun;f5uathC1Y3Wd;qIk?<5 zu^RL4W}L(9%{n(hJwAt2x46Ymlw=Rq$dB88rs1X2kE(OQZ_*v_8CanIjAzj&mDaFV e|7B1E0GvXsuHkM{Tv6aZ&qzHJ-6|b)^#1`J2D5ko literal 7497 zcmcIpXIK+WwBAsq6Qnma@&Sep^3g#d2qK*>`Q{=M@&`y`pn?3p=p_MG>9H{RUTfQ?y@82|vbtA;2`0D$rT zy-qPeJr5r*Btai^0lHVwr=S*b>NXzw&E#un8vp?8AOBvkG%0o=s8b+N|9YU6k85D? zO@9|4I5=3=!`m~!`KGUntdGB2_KKz;00?(nMO{XReM6jRacsJLA3PF z+wYFLROjj&bc#xJlA{buvyv|*)t@}EUEPnp|KQqFQhASkOWyqC`Z?Ry?Up+m{Wn_Q zMXhq2Mf`uXSN@172>`%iv8&1*5omCLGlc=nWu6EDIsyJw|2-2zn75VHSMQAJ+t~gb zwV0Ka6(5*89jEVwUTyfWGj&H49;y%4l`N*Ww6vH|iP)u}3*ulVQs*9lAZ2?T+?7efoiEOazqVSoCU$Zf+-p_|5llfTAeg6<62EeU@KWo~0rH&9cqQ@Xec z{6VG}9l>$ScV*EpG@I|mcTN|VF6sgSu$(EE-xl^d2Xn;=3JJ*(v4=xZ^9mCn zP==bBL?PB0ffvy<<_rEu%ta*9u<;Ac{X1}yEhu*-WhC9=yD6|{3m$pN^N-v~@_2_m zj6ox}AB@LatOrz(S-I1D^k>)sw|bt3P3IAi$>;=%jdV{#YeU0<#?nyL_++pnx+zIW z;!)?cjd7NuO98%|A@Q1;Aj`zH!`;=BDvbPI1x8-={p$S^Rka(KbU7#O3!=$&Ws9c) zX^8<7eUEbd2ogb+?m76AqZTj{Hl_bhcZ%KX$B!SA$C|@WDq34lq<{`pKU3s|PMW>T z!NI%5MJCxh!zS7PR_tn=L$cF5y?;w2zQ{2xjxHmN1BurPHK_cXb1C;kM107ElBL=vhGW*(wt82)R99Z0~2&-L@tAJ-M1A7 z9_r=?{OOPTle#HvY+ztuqMjRd32gHgL%GZz5q=6V$IVG_#q&f-=#BL(Fc%dRID0`O zxPU|!mojTG!!~{P;0q&C+mIwR_7Y zEsM_dSu-lCNxFeSgI^Z5l415Mm$lKyE39$4D*L%~q0Zm_uCSGLB2{!oH|;WdTf3*m z^)UAo$>$@@>BM$fhEKmukm$=aK>DUu)`p5#jE9ep4n5gy!qGsP;I%q%TQQj~_Pt>1 z>2tfRb{H>e3m+N95s=>D(CXb_BKXZ(4ND|Go=u12uqJT##^mcFR4uuzmdAJH54Yy3Sa zv&R1o`P<-NAFXdrGKtfKBTkp^Fqckk9us3RXttkq3J$y(Fb^%I!SP608~Pbr7(9t#$>~lwq!>&Ln`(?#srzRxTz{jddUpztd2)3NBk^)9`R0y zEYo#+qf^!YWTCOhEHCOtbo5CiVrmxK6ArVdo4Cs1{PY@wPfF#wGRsDrXGdmjYS)Y+ zYc1)hr^|j(nb#-+bLbn49nV3KssjRG&DG;f5?Ry>tI-Hsq z5S>x}rO4r&lGqCcv(WosCu9O%^SPl|RNnGB?dCbhyRWiu+kWp7?Kq#o4<0=kzxt#O zt^8ss^R^=C3K(v0=%#vRN1m7T5l=z4rk?fL-rcS0bIVZ>ukzbBAM|i+N9^)biDsJ` zOdHRdLpSGCHUvrt8abw}stErEn`(*{i12@9R*eM)5-BW%10+ra5XV#2fufSdhm%Jd z{AuJIzYc+E6|Wy}G?J@V12nTI{q&;+N@#qWIu(Y1>n+hI$9XdL^=t1Y+3$FG2sqi; z1ZNQ(IVSG-K1y&_%<8oTqH;Sw)#miO(4AR>7W$f*(a~!|*AiTHzjNnCR^R(XUYRf! zavLD(?c-C2-u5J@caCyP#bT)^v}(MW=8_lnz~-DP=f>}f7~orjupVp#9*+{Nm63hO z^02MTP)w1J%m(bJK6jw!N(p_BSxXXr06FThCzFUnZs$6Mo`LZOZ^NtuJWJ@&6(2sh z%+~GC?pyiB;sK&K8xcyyQ+m`)^@9fwPS>dYs43T&JgR&lE$>1mH=f_-$?cTr|cg=`X_Txn{DluAwmEq(pBAKx03akREwkX-L*65RM;PJYe z0rI9dPo@(Jk)q+Ct^%ADd~{q?QL*d|-*3wHGorWx$q9yaSZX+6%$1Vpi#g1K&kv(! zG=cf}nIMI1U@a5x1*K;Zcvd-qFOd?>=%iE<;?2!(2l@m|HW`#uR2+>*Mn+ly)6ICB z*LM>s^IY*C^;G@Atw_jm+|IqDgs`+WzhGc*9ttmJM)8e@4I2EcQRb)>M&I~0zta2l ze&N-xN=rRrS?0Aqu&Io^u>xD?^~(|O^icg*#kdTX=4k-KLQ6SuuJ|(j8ArzH7U-!~ zR?0HgesDgq2Qz|YV`F$DkMgs*SgtIWi>vTN0Lw!WI3 zXyVFk%#+U+5Qe;eyvcWaOKVeUpC#bFz1BqQS4yYmfVn$3I2cMi%MN^)Tw5X>7<89}X+Elzhnk@8G;%F@IC zExCjsH@dEvW&98E=+A2x+mmN)>sL@MOP5AUTx=f=i$+Yw@ma(6i2peyl0M)?+k8(q z`FXa{c|=W3b@lo;FLt8zlFnqW=OGAKo8+o1XLFpcg&Y@--agO%5!!fQj9PGe0?!N6 zIb{_UAs{1Mp!RynVmuLha)FyU@>adGbdUO2P5Hx#-z`^8%pDw#pn`iwXa(2N(Q!53 z+?@xf&kp%-dh1r_B`2rxG~lPO_Uao`wb^I27Q;t&$yS4%GCemKCoDh{0p~A7Y@T}( z$q=lH)eu_BrJKk|cd4L%V+5)sQncY}UBu;KBG!qPoN+Z8De7Hb)bg&jHek5iw)Vh5 zbr+tb;Ixe}Wy*wM9^fy53VQ?66_C%!;_zV=`HZ_vI5W%^!jBynz+k7v+96IeazVa$ zdu!_=EQA~va0bDfn9H%^Y>yv8Zx^AF?+zr#?SB6Fp$ge-^uyRNzRDqeVKYS?w=I)0 z+Y|YQX!c^V6}m^pq10#a;DRiGgA#tm)2^9!z{;k)9P+&PyKK$;y1L*{H@9L>8MV$w zLc5WGTSL&XqFdQw(CFwWwN}+yNKnulw)U2K)nebTBCi5(-w=7Y!^rSrLlr5SJkBuT zT0CHPS+u)w_Ct4?lu`%3j$pJ^d_d#PyMf)$E^yS<)wOoUrv?QE(*6ATv!$3Y?mHF1 zBRwx0;NajeP_k&P@-%;AR^Q;*>?iTa-qX`l)+bjqzqz^jT(0SfHR`z7Djr0_yUH%x zv`m-ylm0dE&t!1!{aSt9OBqLf_1PUsjj5=pkPveAuET0DucS@WX`~p=)UIm*GlPhG z$P62L*H0i&nb3Y~00MC_mnQgtr<1;WnBCyIcjW+YJ3fMX_UX;Vcg_xykm z<&{cWFbFa3mv0~9fMJL#_K|(9=Td&1nKcv&8Xqq&O%3Wa;|*EW&x4RICUdJGZu0q|Ow%^~iiG2F>Nq)Gdd=S$^-<|4|+JqJs zrwRF1Q~n6C!H@F>O%f?$U^72C4OskRl42yl!U`h?g1Opz8Nxp`IUnKc3{f+Jy!;4% zHWpSm=>h!%ynP#u9;)(Y%sIr+8!~5#=XDI8qAV@{zlRlNG8_L~rNdKfqj)ou@NWr= z{|573sw0!$ zTLOhzS!*DB;ja{w1qg7mKa6IY5XjibZ@oslCxv4J9O<8|tB4>O9S7c*BQf^h9o8;L zX@J{qh>iRBKq%v@Jd6tX%Xlj6>W-Zuz%)xMG?B}A_tIfbwq^<>YP#5R)_adO1nTLv34;!k z-_%MqLme=(PDHO^#sGmg5&Iig5&mg*Od!tAs`T4w)4V{;<02+T$0RXzWEG2Xka#Mo zs-WO}8QV&BcD4z$cP_x$zQ4@=NG!{^fmj>}flP?UsQ@7X-(pJ<`|B4mchz&fmw){# z5mS~(}}%j3=fKQh|YOlf?TaoK=zwSeED+p>al8DHYh@YFEz+~_ zdI=x2&&|0Ck@H}GLWgTyc3l;-I$^3img#t}h1J!QkN4(@)>Q|24y{+)LD!;~MCYXy+XuPguMbQ;j`D>r7d$ch576SAAz9sH^4q>Fx2< z&EO3S&iFBoMI+QD^VBk%Zb$AIuN2&JqH>(W5_w~(Zc>Cw3#ddshj`kW~{2&4! zM6L7jVU+KQuP-!2dFQDIEh;#EWRBC#V^}*Ucgw<~>+q>HSOrzCqX0WQ>fl2s@S=)J zc8F%wRV;S-3>b8C(nA!X={-{K231X2Pj7E+u@i`zU(yt9)kVFaUGsQ2o&J29P*&DD zara3;znWVTVQliQFw!KB=;_Xx0zwqtMw&OAm&^{F26ED-3%)oChp=p22hKa@va0H# zq7+=ZpA9kI(E81l5)Ta)$HkM5uRW`Vl#MWCmhtiD3ShNqv!BC)lxXC^ZMq#BdZs4R zSgThXxaLK5{5Sge&pcymec-~E1*oJd0mt+Ob=?s<4C-TP!QuN_c1O^Bhkh3t9&WS`R2c-<57so zZ&&s5KUEM8Op5yrX1kI*6RLpvJ@uo>pF*mOe+PcZ|xBZ@ameQUuWC>3rWrMm>t{&+>u zNK!ZigOUiQyeU_x#N&$?dzTUAr_`nv z{q9og$;(S;pZJtLC`VI-SySdUW#ke5M%vR})XMsF7St?xNh&irRP!-DVkRhtB+Z*R zyEQd(DUreEFdMxGP4H}zm4^Y>YOvd%hKFrkl0rEzS$EDS2FOD-c3QtNx#;Uc-dhN$ zxxJ2s^ns48tu1K-`a1iw$e@;TeH!0Kgv!MBTTUy>%MQ)%pI^nAWvFXtl!b+bRT>t5 z9p_B((4I9wv9FbpFM-80A+L$VyTO{G(V}EoF!v3F6_xaF-*XCurvyYu$?G-QfN~p3 zFw+e#Qmo1I`2U4EOcZ+GKhf9$_R{cPG;5Es)XqeTF))uiBRvD>&D^N9L$$QH4ZJvn z>^}aH(U_lq(;rfG)N%J3sqjY+=!=@XlsfD`4-UR_si$pdY^HY zHKAqC_>?Sm8X#r$pi(@GsG%&sE~%*|KVyRL>sH#OigCqf5{^GqW3+1#DdDj(+`uR77A$)HPyID z=9fLC_V@?L9U8siuCh1mmV*wa|L9)iW-%5pAKc{$VfHrBt;ED!;P}eI?+M(`F}m+A z3Hii#JC4sWbLf7gv-30wF!fbEg8-!EXQyTy8t?WExV^+uodVPaUR{Pn1YNLI3H$SH z&RwrJ(o0S-`zcE!6x4k?2meQ!o7qJ^&knMi zVo|pf2*(N#l?sBwzf*V_l0pI-Tx=oPfQ$PiF&#aXaKLB`R@pU$`29||^Ii~H{_$55 zZCq^$kIed(pqm5^YCy@#hlL?Fw%!+M9rKhgcXf5S($Hdd>iwpVc@x_m zg_p^J>XEPTYaW#G!FWpN!%8LOI$wGz9Zz@+jJ=6&ZZQ#4*n-BDT<6&qW`(Ey`+p!? z1>oS1>Ahc)8;hR|+S`GU&Q&q|<6Ft&Oy-kG__b&;o`Sj~QyjA!@OwvtsWq+V#%uo% z94#9d^mgkX(+@rosdbV^eXDdOzE@B+Kbf9ToU{KM?nQNt$$wQq_(PJW?clefyij+E z36O6$K>fR)OUu*WwDy5T$YP>Jz3_2>ynb;ogewOiL6oN-%I#F(cp?~q(kq9KX4$bd z_2maPS)S|DZd_5*GM1nBbH|yUD6s-tySq9MXv4W}#KsUjeBhBz)cmD>dEjr+_hwv6 z8X$4P$WB(J-1AP|AKwptBOE-*i|2&l2**EGg(+%`ERfC_!cQ(4J?w$cV-g++I`3V! zxx&ty>8tuj9mk_RHzm~no|_SjR7^2kV;fWSH*CA$-0M&>(dD=?8VqeQC&(~{>0#Sd zpmpm4-}oADsic>2p=B;4y7sHTaw)=(Fpzi7{r#gWnsA`Jwn+LdY=J?fP{Po^>{<6+ zA^q~k@?UAS;NK(O6g${!t^PPzUTe~aAH1H*S#MtjOK6#w;#$x`;TZY5j+d_gJFd)|~HER|SI z3KkIZn|@bRRMZD}Z%h8SD869m(N_K|5eo*nb-G_{;3vn5&P)l7AwpMn2?s8aD7owM zF32k~@~9POQc4k!+uGiC$S^9bFL0-)5Xil`8g+7^lq`+;zZ$w5yj`FMdfPesU+h6DWMbD}RvzGt zM-dY!#?P+S`i>z;;vPx+r>g9cHjdC-$kmk)n7s!3m~-u6cN|{R*3RzmGsNoo{)+!r zAZ3CSFuwzHzvJn7NW#AbRKI7vExEr;yEceI`0*jjml;JatV(6(Lj=TH(7B&8WnA4) z8)+*9ZvVcmxbN_$o>LepPmYl=hBmGlhG=jwno)kNi{E=f%xCNXkcOwsp%50I53|31hTfzw$lQ1< z$u`js!8B{<-aderCUfSBw8R>-xLZ}(9lNuf^~I1z^-pVR{?x2djKD<*VTb@L5G4CE zmnhc~Qi!D{qGoQ@O+S_L62AU=uV8F!tdoePTBn4&TX<{k>*qlsqJx9vp?6RnlSu8d z7em+p^}^(KL}z&ry*VAALY6$$D4nQ5W?)OEEDlDI_{pH*$BjC z;4-`#9h88FG%-J5Ok=IH&n`3YET<$vKZl6jow#H+6UNsji5n#xq~n{|Ca{1O;^|v# sc&dVUnxU8@pmB5>{{PZG<4!ml(c)=r6Z+QBYfs>+zA36w7jy4_091bf?EnA( diff --git a/dist/icons/overlay/controller_single_joycon_right_dark.png b/dist/icons/overlay/controller_single_joycon_right_dark.png index afa80e6ef0f54acd62711c65abab9b9e7ad204a9..81cb94a1dcbb2ead1ad8c5049cdc08e24e564621 100644 GIT binary patch literal 3406 zcmaJ^cTkf{*AEgPv_L{J^eSE?>VqgEAc!;r0hA^P$`h5KMA`+U2|}a@QdEjh5QqrG zFEkO*Borlta=~2SiZqR2!WE>bAXQX+<6qx=^Ui!bXLry3&hE~c**(8AC-bDMqpXyw z6buHF#htKohrvWJzdHghMD}LrNCRE`I(cf?K#X$fv?Y9^ z+pVlKJ+*hxP~l1EiX}_ej_ON_MM=b@$fE5BCjv)tl3eo>73Y}nnhF~q^QWf8ZB|tp zQugm|M0}A}&!wl0WbNY4B_Q=1;nR~qN#9e++@4`ODpjE(JlIQ;Xe ztqo8*f2%Ons~ULDvMJYw_T}`9cJ?#z!DU|Z`0gw-Pany9nNCc3e4L}?_gSK%RjoEA zffu-4&E{2UL)FpXVH6=gn+%>G$zA&^P$OhFG`_a>Bay;ed=p>;NXE-f-+D1;D@DeQ z;{+r=QK;}RSbZyHJk1)F?Xf>L?t#bPN%`yxZ!^P`Xrl!9G>>sf`Ne@=Lj*1TnLM@M zrS>O+Mw{H2kH#}w$zbr6v&*{^bHU)x4$D=%X7d3O5Fo%egGTD9j(~E#?AMv2Q{g+) z7@#9*l!FR!%8I>&ot|qy$75@Q!7j`hC zn&iMqPcyeuQ?D#I(yN%r`pJinKaPh!<=`*-E)bckZE|2zc-PmG&+1(5T}m_r1K0?5 zJS@^wA6PfYe>H2BtRhJMpyzS>FYd$mAO`&!8zM4jyfGKI7|(r_xF9r` zIBm~CNXEl-1npucN8viQ9uk2n+i(7SBI3<1VoKSjG>RmtFsr3<*!MnL<%UXXh!Yav=1RyjlK!eFAAY(bFqFr0S$z>d zFPMl8%Sk)k8%n-~dPD_+gg=sZ40zI0yT_F>_Y_irkhMz7y!@Rf>#kyVMr`sm$aE`F zDHj3syi5g>(AUMzCOPa}K;zd~r%Fs5gTxK?-{E_V-}=EgE}EnZ?YWREKiipOB5KlP zmrer1&@Z3HDBz!7ri*YpDJhg=x*U~;Lrz%!pJe(0BIk_el{+dBC3u`*P*2Fvfrf5I zvRDU16;52-M68cCXuWT~p#!ydvA_v#2hR{iklc{V0iz$NV#r5)OQabCONmgS9ulxj z3(nyP+;t94N0aI1Ghz|0j|XIAh7=-kzGey$TjD0CA6z|h3>bNI^l;ruq;5hGLi}UK zN%J(7a6d9fZsDH4g^?x$bfk6s#{XGG}lsJ-6{A(pfDMCWXU6HIhAnTv3+& zL(S~^)1+4F)Z{T>JE}3&oDaB?AN9tTkX&2Vos&x ziJ`{PdkuC#Qj+LM9UA5B;0w0}>}!R7@x918kkij>mOu;C#*js}^a2`t)MGJ1{mGhD zuC2&@Fou_T3dWYtwiTW+v9ruawy-KOgy+1_<6Il}2 z&1sQ^wwcH{{K#B_+Dvastn#$GxGm7avLkkeiV(Gty>=+0zf+7(;uU0KprJ;=);k#2 z3p!p)P`Os3i6KwCZ4?YdBZ=zkMHs?)-hDKN{Lgo+(d4-?@Xeiks9cg!DVh%Uh_5(U zWu3ox_QnxAbn5c*4ktZKZ(QS$=Q628%lox4^j4n$iG^HF*1&noIA7B>#o_OM zA-F37jPkKs#EeB#YJnn`WR=_ZVFeU1?88;B$V?4!p*0ib&61Gv3JZ$$VK0~ zjk(FX!aMDoQa!N{Yks#z?<(ZChd479L1|A{$;|9FT+FIQ$#mY6o<42f0cze{X?Wq3 zp*4ENbdb1dCDXt#OBHXs;j`>0y78~U=lEouwgaE}894^G@18Mu?WaT`&t>$fkG@ki z*28>!RGmh9A?H|E6SnKy;@yd*$Gh#6i@CI|JGvKuV>S9R3Vc$5J@8EBL0+-iYI#FU z*OxqG!otU!QwwTVS3-vzo3HmhCQ1;o-bHqNrveAyvqJ8xm9Ne(zfg8@eV+}tMeh** zcq?w#RWr;9X&Lsl(3yL8qcm3{-rW#M>N$@#QcuYhvOIp!CFo8g_uy9a;Z0!s$1mnAsaJiKsCCBjJ#LI-^pfTUApJ8bD=d z9pd2uc>epE{v|j&Di_?R=zPg;KY31_{o^L_rB{uJkh`ir5n1zw?Ilr;Nq9;%)9qfFk*8y<{SCO12r7zGZX@Mrw4xkU@tGQ?gDiyD;x0u1Lr1Auo z>Ul4h_WA2U1&^rD&ziPx(nZL8lnYr0^MB-90PO<8;OU!0n8|I>opf6ce7+lcy~UOQ zz8JZyCRuZPHIt1u{>;GD^;*Id${YZCbeRO$oF?2WC)WXYUOt2muKO$kjXH!(BlDdq zjPGpTj!OpJ=Z-8_EFGJ?>rVIj@rQNB)$j~l`G>B1t6pR}>v14F-MfL_xu*hyV@@*r0q8drKk+bo;NV^ofp?$YJ@ zcWW4U?+34;2x-e}_GJ1S2IM`i^&#lhmpJD}Q-rkS_eZ?NEsYrHFYya;getL>VydJ8 z`K456zsbtN;kI3cBYCaP7m2EgxfXbw7GWP@^o8+RN8tJfh9>X5*JH?(%>F~2>_9O7 zHL#jJ3r_Z;JJGMft0U*io_`GCurDbWHS@aoeDm=p4Wi7x4$n@1BYaTqFx7H`uqBA% z?x|h(Fe(41H=6eggGUV#jPTmn_ED-ncTQ<$_SeK-y7lL-PzX2vqHusRWhXbRwq~Cg z@M7dy7d1>3#l4S);(#4}^T*Vlb0lIV5A(zS1rDpG8c%IiX_(3XE^g%i4Qua&iTzid bBLq5>mpCCPtNrJre_L_(u6FguLGph9G1w%t literal 6729 zcmcJUc{r49)WB!PzC=YP70MFXYh>Rl%GgOlgDGQViLs6>G32!sA(^qHvXq2mH$#rmIrnnzxzBmd`Q2yI9qlje6Fwpg0)h6y ztt^~DAn@V8w;(?tNzGci2RwMg&%<2=fh$fBl?J>EVXWN4K_IdBe{XQUn%Gf5C>>$x z7I8TQ9f7?b<_p4Nv6}wDf#E*aF}|81VSYs{<0C+yKDfm>m*`?9E&icrWc<%HHnx;- z*CHqJc)q}a3O707v;)_A;1I>e{`+4H?H7d2O1jJ^P7No!E)<#L=9^5|l9!ZHSo%Va1hg zv0_^~7{LcNrzjU^vG=JLNGA8GCFtKl)~9_*qI?;FK0ZE|Kx&jAAxs+a5-#laeS(be z&T+>b!ZC21zT?V$)~*jDL5>c-a}yo<1W``*^*{=zyzL=zrnxS&zV3Z9(pd2;H~kv4 zkt?2wb4NzbXp;rDaJ=l6;8$27%mHWK472Oz2YwsX-(`gP*Y=Q^M{k8aZJJ#AJ zXx+jHG}_^=olVTSfEJ~>xw(-qU%vEzZ3+wtXI~+uzi8cPPsVlg$YR*kR*u(GiCoqr zYuqVG;gD;@D5fE-8j@OZ;6hT&iA{rNdl?P(L3WFG z%Z(!9GIexyn-gkWIgbhP2M-mT!zyAjiP-{iO!IcDOWnLF=dZUBY9iS_dDd1jGy5^J zt1?)+Uc{M$u2Xy`OR1$y)&!(*Hji*je453&lwxo3*8jFxrJMRt^{0S>HqMg2qGJ3H zB@&y)?L%x@9#l>25)b%tJ>i8g!W+e8k!tw>GnzoZ9ci(I8L<9;WBhq0MaWJ=o zDQsKsHk};v^GeRE_*WuDbvCKZYT8X6OrCM#!*mqW9BBz?a=#`*>F@79kKHrxN1XcA_N(76aB->PsTEGMxY;BGC5Il&>Dw19cEIGc6Dzv`-E?!FH z9;yFp< zR@liVm_FF7eXOb-e_Xz^?fw@U-Mz}C*6vq%g|ok>W`U8ITj}@P0>d~`4a;=dPytIH zpw&T4A9{LR^CuRY4v)`_GcoLNwfv7RM;5%A0zOf3Sf_-8O3W$fRxOdf$nqm{;H*21 z#O`-AEuK5z4j3kD^xde(Ic|-0Mc5td1^y=p&AmX=(r!V0qd|R>@4?7J80e}IBl~vt z^DZt0u^vR$F=cL@(iWN)*-^%THa|`5?dwEqua2r{pT-0WFT_7yT3YHJ{1N8om{8~A zcnf3-LYTaJxtK}g`jRY3H{XWJ@$HJL5GJ$M1EFo4b0j5Fx)3>EHn&e>^vaL0wyNh6 zC545BF``=tsrvTuGLjQo_p%ujL|M6NF1zKN6q>+u>^eFRCRv~n`cuKSS^$WM(8DlJ z*yuu#{LJy8UVTg7 zGR)?O|CNHk=Re-oj4R_{_eFNpHm6jjwbP{z!-%jC>u1%n=FXn@mp>}JXwI&5LI?hId|CP1rOvj7j%SCJ-u%das{$6LYb}tRePTy=>o#g%Z@i$g# z<9@8*mMtW-LO{s_`Aj7MYhrVWva+>5lm~`@G-K*@Wqtij z7paBDy)-WJfkx`1^-}tVVscgp;p5C6W!|73-5g_dbI%oiw2^+B9 zw)W6~c^3}+s6}){hkD4Yin|RgNkyflXiAE?>TE78o@PEMONM^_W59x=_k{N2KG$+J`o5$Ji?hOK^5ux36jH zfc^fC^KYx8-Q7s^?t*V`T z3Q~Kk^3ywA4QS(xl}9B6*800*ChYp3Ac0m6*!)lv;(=~T;rD#jYvtD;X%kL#(yk_> zM)r4E7mh3V+NO1*e4>gZNP2od+v-_FrlNe66YfCH_zO~+Re%f9Lh-DswtVi9Lx&H* z?+f2cE5FEh(Zab5qVT`FwO5ghWemXw;I91;*uJ+%l#`uwmQ__BD_DzN6_r~Bj{;ec}32f>dTGuYVffI`pIl7NT z<=cJ8is?Mp?pE2Y0+6`T+;sfs1i{hVI#8W!dFI}fPxd|H7L4|`X52;p z)^X&mwPqBLmcHpqGVqOv*m&Z~ukPIyXNJ_Nh~w`uKkLO_&Fz>c1B%vhvL7f zWN2ziW!l}sn3Q@&QY`NyA}Gb8yuw2WBVb$fljh}_p_&l{8~ zHw8f9wTU|KggoM&f7;IWdo%oG$eRql9m6uW(F?h8;(fS%q|8KgFwQFkO$V4?yq!}+ zih)fH(CAiy4)<@&oAjqq;%(tC|ID z{gv16<^g-3oLy&ih1N>6a=OfA-IiEJ4gmjcKmz>}>gvADZjB zC16jys-11?;2_u1@Gr!%#`WjXBS#FSTI&Y&jbe^x&CNx*B0xZnm2r}k;n6uOa2?HU z7CSNiucOJEN6f}6$Z0Njuo;2Jp25DRHz>ejLz+C|%LzWp%F1$4?1n;vX7P|5 zSushyDy@S{5==>asjF)KBQJ^4;^GdWEw7{X&0*{!>k4O^g%>WWbl%`)#o%n=E65Gg zdqK}jAY&|)SKDA{N!CxD;JVPan=n^#=}CJdzWEtlA7;c1N|QAcJZ(VUoIG|9+znOv z9gO?ex*(tpaLdXlNN=d|g2HmihaX{8z07w8ct^f@l6h!_O;b4EnCtRgfOBv9A)xP> zmZ`iMoXY=N5J<`^+Gin|CIp6kJT!LP>NA)YAL4>U2`~{o#cnE+Vz+!rfCrh9GIVd; z3JmJaIHbYMaRKD~j|L+VrGq^n^+ zCX-tRx`CtxxYCqv&=-u_ruX)_X6R~9V{$78GCTM8yBhy+gM7V2yJFNi-3i(6L=4D z4^E!Xl!^6r<|4gB)u-NIonYNiR@Vqc^Zv8Nj1M0^OttLX_JRDNv38*pn(Dd|Ix6G0 zZQ&WW#giA6 zNC8-HG}_FLQO>#}wpvv_q|80_-lsP;py{g+>`=k2{DB|dAhHu(%iv1<7Cz<@2cp-; zi8VCIA^x0aUF9jA|HHJmY?gRND!xsNoG|%9!2yawVVt~13C26}IBfFJRv~e(pOzgM z`I<-#XXlSkJnU3hTPfG_^+X1rDj3jfJoCgwl}nsi|TxWx|ON@7AxHjO|)v3ampxib=n>XbB;yha_1yI}wd*EaGY1p>Te;oGSfX+zs>8F8)t2feTV*gdQHGaB0H z-*0ru`)&;I!DNrm5w4UAT6BpHhmZf z%>rWMq^o!)R7*BvZcEMlGP%VjTw8ugaP1lG8nA_Wk2F2ElS*V2JZ@CJkoY76s4a^89_RPTcfz!n@0gu zVvb)kT>*D=t#!+f&YhRSYe<2~skY{M9r8<#dFcP0*~j*UYv*Rs!E`W-cQ-GlC`BQ$ zY#5q}vf!)S3j&#f5kY9`RKXNY{N_%_94ksbd_>PZp-o(Wl%k2RY6amZdC*aYn~*$) zYUBaWo{enfMBhP{EE0XSowI+Hx>Tu@YwR-(-?mPI~hV09bKLpci(k-%|51gN((P0j(y$MiaSFBfg^ z`SJ#O>{eE%bhIG8=}(Ilv3*B^9FN<)@r+OL7T6SsKhQv#dQH{nHh37Qh_+nTbHgsQM#`H4|w) zPJ4(Nj*P_l`*=OAxt(SjorldV;4zC}XSZ@{VKTF^)+Sth(Zl47unuPGUqD2cJbz2POgB+dsbD4O>d9JlP!aD8KVetIKphVKU*ay}VB6 zhpGHK0RJr^B*|+gPxJN@dT9TRL{~x-Xja_?@y9dfTA3!1+bQ$N_R*66+EV52Y#AET zuRee7FIKs1EHZt>P-qFE1E5OLWH(SS4f z+=eC+e)8X0EX|;E~X(BzeuGj?Xb^WOGBgbqfT4yL5{2_9a_20(%L|0LD zyzI$gA*(k)(xRNC4wD}(>K@bS(K_$i*wnN*`}@w z+ctT4@`!#%+~aNdN_hzs>%I?u*&w=Bm+%;ugle9_-w_{?AozoJ0W#vJ9EH2csHhwCK)Tj>c@F#t+mji7=?cFf zTI`PwVA04??Q36OUa4jQXTUesCnag}e4#i(jabRu^1z`I8K9g4rL3sAD@tSp=bXVR ztWE&xH_kRit_%+kdxHY3mEc=SuisJ{%N9X5Uy^KXZG$qNE$YnT9 z1X$(GhzkykFD{OYj)~cJW5D*}ErI4}Vf_yjmT*ZL>iH6cNBb|D>t_|gV^KKV_E<$&TU;fflMah|lg<)C!!DDMi1D=INejGghvLjLibZe*?^sJk34E$W|skK6!hp zS!fHgY7OHQ6F!|+R-`EbKb$l^K0cOj{bT3bw{O6)Vm*;wen9zDdy@ZT%xoMZ$K)@d;Q;E5C zj9ka(CD3s!+G(!g#Tpz&eLkme+PL;l*i{VsDr8Sg-4-Ug*_-MB?!FbrdcGSrT|%NJ ldcWMtNc(@e`la^d6613Y`I`V4 diff --git a/dist/icons/overlay/osk_button_backspace.png b/dist/icons/overlay/osk_button_backspace.png index 4ad284720ded558efda48766f900bc4426e3cd63..b7dc33228250bc2af38d43d24d05c2c25cd73a2b 100644 GIT binary patch delta 1263 zcmVCEmkaxCG}RWF%OnckANy6U%_W+heyPsJ_)Bnp(R%b9}bm&zm{ zQ6Re|=lFTdPv!AIqCj?A&h_)~YYqnz1>#MJnGdJ;1ri0)JbWYT z4kQWWO7<+pV}GA3cLI_GawT)V;^9-jbwFS$ox>JYT^j_(EYEUf zy@lp!$-DWie39YPipS9jqWD0rwY^vInCo4ONIXsu2_8Wd zALy6%d_wW?BG)Vyk025}f+#+a>B5&J&nZAG9zi5{1bjLV(mdfnu4K-Y#edKI-Vq}8K%{xXfn0N5C5s;vy9D(^ zqn0ATkO)n88U3ncm_{cp^1qWXQ?4M_86{=dkPgqMHo0Hpax@t?So zJbymg?lk}L2>e}3h!_Uy!`uxJl(IDZzy3OfNnvfDmDWg^RBM}A`fNOt28 zuuF?=@71vYknH#iV0I82K|h!wh!V3r zt9cGk{n9?K39|#lYx8-nPfkSPBY2ue1L^Q&LG&KG=0p@e#Ho40fn14~sHKo!+UG(P zJ_f9LG?0#KCci=-cdg?Qg$cmjZAv3Woc-RYXgBMLi;AbH{g0pGXfvwk9bwSOzc zBMLjrB6;Ei0p(77(@(^c5sdFK7jTdMEcZVkl9LcmJ z`HPomKDS632r!dr{F~Kw?WBQ#Y-!f~OCgozk_G~@b;YPKIW1`*z-}!@9)8QlBn<>` zFY%NAof(`o5MYlLM4#1qCk+J1*=X2zOqZmAT#1)^fr^{Fx#qmH08%%sc(?!(caOHu z7eM0Ln{O9D;y%Fqq5$%yGKk+5K;D$c^N#|^8@o8O0>~S#%$Ws{H)eg#Er2``r#~9m Z<4=bU9t_=IpXdMp002ovPDHLkV1my#P;~$R literal 2919 zcmV-t3z+nYP))5!Bj4mi_(@$p)C|!MDRj_Ml>qkTFcU~XXc%A zwzhlD%;SfiBMWpd^S{hhuOh?%QW%GChQv7My^sMrpWh8;V0yd_DJwE)hrouy z9LG-pc(?5=CHtnZm#`oR4kw~b0FJPorDSpnc^4K%(a``l1327rl#*#EoIpVkya&LO z01maCrDS>vbqNc@@O=Ot2T-@2rDSFtU^_sMMx(J1z{3DsjWR7}KGXO8&4yy_cZ6Zc zX@0*D0tG>E5)rKfaIj7pB3j~k-gBmMl}wNI+xRGo&LE;kp(oEPNs@d-2vN$DGgbwX zQeK!O$ewso0$isl=se=Gv~3LJ9p}QXJ(Yl1nM@w-^t$)cwM<<~Ys+c}mok=RN>u16Wk8Rv#&fM41T0%m+qMbU%ReH0#_Y zgt(Z9+J>SjF+rZ8?vJW@N%RJJdK5+?;{uJ0j0{OBA7SP*HA}7ZJnvE>>KF;8$Ufxh zk-0>Z$kU?`5*ZaJ2m)_#aBw4llQm1-;CbHFM3k7wrOFiY^vF!2Y2@i)T_R%wHJi0wnOBLcNrt@+*N_xEa)VP^h9pUKn1m_#Y% z>0wPGZGnOyIIPp@Yz1(vMj0leYkc4Tu5KP(6y)ilEs>%;J*-HiB~TPaM-kCh07q(+ ziJAFwA;c{@SxiAhS0TMTJ*tl5+%KhElJ-Dmu6FNSRpjY${NUhVt|L&&0yUe>W0-j} zfCU=sj{{il`~E788b%R9TmoQ?Mu`KNc}fd()*SothzG}m>1rBNd{vXW@F$OFt!pfC(CVCFRdbXo~Uk|a4% z2=N<@8o6UyqS+)5FoVfdDdh?S{mcI%qEmzrf77U86;pqBB6^;gPtd86r95c|3#U8- zg<<%q?&O4yJb#Jfcws-w1DHD#6h5u*OQ#>sJUtv{z8gTSQKKEl@l8aeQ=_B{bE*@7bsXRKA0(p7 z0jM^x4>*pqDT<<_Oa|*GdY3Q*lPDmf1M;3b)4>4%A;g_T^f`?>^O7W4KQ=bD-|eiL z!RH?!$GJ0I{(l=UgtuhXdYj&{4fK}uOOkgG4qNS+>jA<^r;Nc4UG8_cZJn|v%Y zTOW4IgN)_rQFw{o^eDsgylVj5p;7aMYPGtSnU(iFEIK*K)1%N5y-6S_1sYDbDAyxp`pjw!j&x)exc4jWrZumM_lBY)@C7QHV zgNVl4?e_TqeyduXnJ)>$@MhKHi-f6$G62tN9A7wzrtI|@9v*Hwj_VMZjbmst{rkavB-RgK3+^T-SXyj^on+{6lpSX1*>60^Kd{tYEr_Iq$i%teH`_ z;oP}%{{!Gu03)gk5z$RS5L~W$ybZEyfS*kg&A46F^SqamBsmR0Lvbx4q8|o9aIWI< zxsvtt#B7l$RljqsR{H}Hoen^Cv&#w*-4z7EiHgVPLSCacvq7TN4f8^XX91i8V2|RO z2NBV_FbvF4Q6w48|6YOGsz9OwQhiaw-KBQ?q+JHGEf$jny)P;FqD8^`g+AP5du&5#Amyb|h{ z=<947I7 ze+x6Ke9qHuL)E;TZP~qh_tN?E=Z~qbuY&d#cfRl6481X+BRieWhA<3$-7Kb1kjPjM zv!X;+>*OP47m%6#-@ z8i}mS0~p?^o`~A5R%;o6EgEIcjiTsBMQQ`theWx}ZA~IkA>;w{Hcyz8(a}-2QmJeP zaGXY&>pjo=im6N{=EX?e`yxD9H$8o#`z|k6IuJt_c zMpKzg&}cM-t+{_@u5RDHy|Q4zg8ycw^u5arvV(MSb_n3+Vi~I48|&% z0;4;W=~A!Pw-eE+0A5inK>${UVYu92tP+D{{?1e(#9x{DqX0t15)ObnrIbqz#wsyH zE^lr2eg6f=aZbnH4aQX%VCDxJjmAQgxk~iO?Y;4?>ptT+&V>L}cknpKah!+3Fx0uT zD=|!N0s(;Qx(^f4Wl-P9K>&CxiXxpmyAp%sDi8n&A?^X7^BVBsBuO>|L7;kPS7MOd z1p)xi^Hvhk*ELEk=(d3zVk}gN5>^BP03pOT0eoAd#5+5k&f`H4c&0Lyq{r&>CT1on zMiYXN-Bc9xQKSo;h#5h5DjwQJXT zMD(=nEG6kE#2KhZqtOtK<2(hu_s^H)NTCD*fU&W$L)-24RscuX&QdZxg}8)GWN2vU zxQQKKcrlxS0u!%@1KfuhJp!cHRk_;*28K{R4 z;@8+ar@q^EmXdu_Xn_F0^SmdSc{$WKawy4=!V3fdzVELiqAQ@@22zp{{{!w-(+1_- RA&CG0002ovPDHLkV1hb2VV(d0 diff --git a/dist/icons/overlay/osk_button_backspace_dark.png b/dist/icons/overlay/osk_button_backspace_dark.png index 19ac8847e42b79bc89d6219359f942926b248980..542038bef3b1444d403506f69fc640786b6725f6 100644 GIT binary patch delta 1253 zcmV+o+skG;>4K%zk0lK+Z#&+FMBAW;+Y_b z6pto~3zT)VdnC_ksE@@Xi8PNUiU(Bf#>W(o9RWn-(SJmmM-#;X%KGs68t`Z$&7+CJ zfvg{27Cisij?sBEk>=4v;Xsw4d|UCjZKCsNBF&?T!hzbx@FT(VkFAdg;pL*|54Yt1 zUw+sP>e-wt2Ow75r=s{zTNwcn?*aKP3V3EXP&O04R-yWVIpcVO#xA-sTfP{en zuIs-zbf3BrK(Ze(0C3xg>US&sfkeNgr^XHv)h}BQAkm-ssgXw#9?v=gXb;>;+$C3_Z6M$CSKAKIDej^@z>)l1?8a{hkwS0#0M7AS|BgIYf=Hn^4uNbAF~jql zn;#;D-g*YyBK*T!{G*#0I>kM_0KF9QHGeD6DemzXz}6QVL7!BIAxhkQ{5j79R9VMd zfBG7vDnH(wX;qTQfVl#>?( P00000NkvXXu0mjf)KO;VU>uUpy=TW+3)YCd zWks|lc6Mg(eRh3; zW+?6eLFnn}Ib?8f@O}VS#hv9~r{x3^LbManS^yWtoh1dZ;tt@4LZNUN5p6&!Phx19 z8Au3mI1#M}aCY2TQvOd%2xQy#kpMO^^BHkxN%sm$C5;Dordq)blYW*{NNd;re@ zI4`0&0hp;#W-l{esFd1jD%Zh=l#>1Y z|3ktG;s+tb*`DXE2XMGXnSXkox3s^%|7BCTQVvY)U*n~e7ZA}#=*jaofSy#I=!sPz zA;jeX9tY5&QRYq0^Uif$w`?d@iVtEFNJ{w`B3c8WEocp9-VI<;e}BI+7A(aVu?Qrk zydJ;<(EAMbr%WcZ$aURU4aG_cK;#11wtX{zyEVt|A08e)r(7<-ZYWktKq3{$w(Xml z`3IV1N{vQiaivmu(@3ZkEu#7wPeO=0nE7iuMVNVetya5W*REavHWe&Ik0@p!5<>ip zh_vJZuob|vxaBDn3bW!GTL{s#J26l%)Al6QAG4uloAyRg|=F)_9%dr02UL`(@Bvi6oIR4Ua?bt66Hb>$*1p z@J!?i$~f|jWr&$CQA%wHW9$j!8Ot_iUT$(}_q&;DbO!3~?moivygprdnE5u>b-#>c z%L9Zs6HP%LAOSOxF@f^={G3Lku@%5VojS~Xn^NjqhGGR~3i32DF3}X_X%eeMMg$T< z9P4@B769EEWtf@2-eU4JF(#2ud78u~k+wj&T<#Y#5{pDy0?nU4|CCH7vmHQ}Mwww|zSeP^yL7Ucf|;*}e|eg;6H%{i+e`gED1>O= zw{KrBg2~h53?hp32vlH!q?9?&^R@ywQDgieX1+`*^?*hVqhzz$s{uTrQ-YbF@G}!N ze}#%-4``#_$H7*`F#F#jC_SWUSsSUGZ&RoztN~+oKmS&&t|iiLQkS8 zFK3#|R+CVFPM8ee0tq2L;d$P30A_29`@Usa%au~=HEM)LR1&qiJb+JuY}-DIh&BPx z*-(6sh?bPgFG(tHNqx|Se_<{ zm1x|})ZX6SVP?J(Bd=Ks1QD$m92~q43A)T&mzd;fl1ParY}HUo4b|)QO91>)qyE)W z$~!flPYebd%3jiVK5-IF+UrxTR`(1I4J`%GuQA4rQp)dYJRdtITUxW(>|#W8WGD&_ z*eo_MFff?OWX=Qd7mabgCxrN_#`CdZx&c1wb7x^QrEbHWJ9qvaz!Cs&1s#Zper(&e z?uKoogmrpiI!QF;c2(DPUnQdR0K6M;EMn%LODR_cd_GdbK6*1^P2NS=Mp@ zL2ty$0C>>0?Xv%B?@%n z!f~A60=NRf+dyVA^JXb!F5vT_QLEK{1wDCcM6}XRKToMtsx$Lt%>1Orxa~yLE2aFn zMh)KtKCr_Q{PO6%&TBPloauSqx~{IStf5TWOhKa1PESlxqFCht%w9C^ zjovS~QKQC^j*gCpip64L-e*6FM4=zOIe6N5*C0pL0%t_5>%Pd$S778ah9Df~dESQ8PCIRZ$y_OVL?#dbD5W+K(KQHuk;5#@vYyH3^YaYG zO3@%vfdIg9oQIhCMvW3jd!E-Pg!rhjP$_|lTp$2YO5IIFw`i0&5kQ}m@)%Q@Qi2hS zKmg!4&aD7`6!du_It9Sy?(Xg*%%w{4OWgZSB&EC;z!zf9krI&DW}s0xm&;uT;PIGq zqy!}H83@409*<>NPcrkT;?9!dkGN+b03!|M?d|Os19&0sEGhm-#0=D=P$V zM3)0-#GNH&e-d;4YBJt==bh@DIdgUcSb>Buy9XQp1zs>)iA2UVssI2007*qoM6N<$ Ef|*-odH?_b diff --git a/dist/languages/.tx/config b/dist/languages/.tx/config index 0d9b512..30e76b9 100644 --- a/dist/languages/.tx/config +++ b/dist/languages/.tx/config @@ -1,7 +1,7 @@ [main] host = https://www.transifex.com -[yuzu.emulator] +[o:yuzu-emulator:p:yuzu:r:emulator] file_filter = .ts source_file = en.ts source_lang = en diff --git a/dist/languages/README.md b/dist/languages/README.md index 61981ab..c5ea1ad 100644 --- a/dist/languages/README.md +++ b/dist/languages/README.md @@ -1 +1,3 @@ -This directory stores translation patches (TS files) for yuzu Qt frontend. This directory is linked with [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu), so you can update the translation by executing `tx pull -a`. If you want to contribute to the translation, please go the transifex link and submit your translation there. This directory on the main repo will be synchronized with transifex periodically. Do not directly open PRs on github to modify the translation. +This directory stores translation patches (TS files) for yuzu Qt frontend. This directory is linked with [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu), so you can update the translation by executing `tx pull -t -a`. If you want to contribute to the translation, please go the transifex link and submit your translation there. This directory on the main repo will be synchronized with transifex periodically. + +Do not directly open PRs on github to modify the translation. diff --git a/dist/languages/ca.ts b/dist/languages/ca.ts index 59f864f..8f263b2 100644 --- a/dist/languages/ca.ts +++ b/dist/languages/ca.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -204,7 +204,7 @@ This would ban both their forum username and their IP address. - %1 (%2/%3 members) - connected + %1 - %2 (%3/%4 members) - connected @@ -218,6 +218,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Informeu sobre la compatibilitat del joc @@ -227,92 +232,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Si escolliu presentar un cas de prova a la </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">llista de compatibilitat de Yuzu</span></a><span style=" font-size:10pt;">, la informació següent es recollirà i es mostrarà al web:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> Informació del maquinari (CPU / GPU / Sistema operatiu)</li> <li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Quina versió de yuzu està utilitzant?</li> <li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">El compte de yuzu connectat</li></ul></body></html> - - Perfect - Perfecte + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>El joc funciona perfectament sense problemes d'àudio o gràfics. </p></body></html> + + Yes The game starts to output video or audio + - - Great - Genial + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p> El joc funciona amb problemes menors d'errors gràfics o d'àudio i es pot jugar de principi a fi. Pot requerir algunes solucions.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Correcte + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>El joc funciona amb problemes gràfics o d'àudio majors, però el joc es pot jugar de principi a fi amb solucions temporals.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Malament + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>El joc funciona, però amb greus errades gràfiques o d'àudio. No es pot avançar en àrees específiques a causa d'errades, fins i tot amb solucions temporals.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Intro/Menú + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>El joc no es pot jugar a causa de les greus errades gràfiques o d'àudio. No es pot avançar més enllà de la pantalla d'inici.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - No engega + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>El joc es bloqueja quan s'intenta iniciar.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Independentment de la velocitat o el rendiment, com funciona aquest joc de principi a fi en aquesta versió de yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Gràcies per el vostre enviament. - + Submitting Enviant - + Communication error Error de comunicació - + An error occurred while sending the Testcase S'ha produït un error mentre s'enviava el Cas de Prova - + Next Següent @@ -410,7 +450,7 @@ This would ban both their forum username and their IP address. Restaurar els valors predeterminats - + Auto Auto @@ -763,200 +803,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub Activar GDB Stub - + Port: Port: - + Logging Registre - + Global Log Filter Filtre de registre global - + Show Log in Console Mostra el registre a la consola - + Open Log Location Obrir ubicació de l'arxiu del registre - + When checked, the max size of the log increases from 100 MB to 1 GB Quan està marcat, la mida màxima del registre augmenta de 100 MB a 1 GB - + Enable Extended Logging** Habilitar registre ampliat** - + Homebrew Homebrew - + Arguments String Cadena d'arguments - + Graphics Gràfics - + When checked, the graphics API enters a slower debugging mode Quan està marcat, l'API de gràfics entrarà en un mode de depuració més lent - + Enable Graphics Debugging Activar depuració de gràfics - + When checked, it enables Nsight Aftermath crash dumps Quan està marcat, habilitarà els volcats dels errors d'Nsight Aftermath - + Enable Nsight Aftermath Activar Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Quan està marcat, bolcarà tots els shaders d'assemblador originals del cache de shaders del disc o del joc mentre els troba - + Dump Game Shaders Bolcar shaders del joc - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Quan està marcat, desactiva el compilador de macro Just In Time. Activar això fa que els jocs funcionin més lentament - + Disable Macro JIT Desactivar macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Quan està marcat, yuzu registrarà estadístiques sobre la cache de canonada compilada - + Enable Shader Feedback Activar informació de shaders - + When checked, it executes shaders without loop logic changes Quan està marcat, s'executaran els shaders sense canvis de lògica de bucle - + Disable Loop safety checks Desactivar comprovacions de seguretat de bucles - + Debugging Depuració - - Enable FS Access Log - Activar registre d'accés al FS - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Activa els serveis d'informes detallats** - + + Enable FS Access Log + Activar registre d'accés al FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avançat - + Kiosk (Quest) Mode Mode quiosc (Quest) - + Enable CPU Debugging Activar depuració de la CPU - + Enable Debug Asserts Activar alertes de depuració - + Enable Auto-Stub** Activar Auto-Stub** - + Enable All Controller Types Activar tots els tipus de controladors - + Disable Web Applet Desactivar el Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Això es restablirà automàticament quan es tanqui yuzu. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1326,193 +1401,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings Paràmetres gràfics - + Use disk pipeline cache Utilitzar cache de shaders de canonada - + Use asynchronous GPU emulation Utilitzar emulació asíncrona de GPU - + Accelerate ASTC texture decoding Accelerar la descodificació de textures ASTC - + NVDEC emulation: Emulació NVDEC: - + No Video Output Sense sortida de vídeo - + CPU Video Decoding Descodificació de vídeo a la CPU - + GPU Video Decoding (Default) Descodificació de vídeo a la GPU (Valor Predeterminat) - + Fullscreen Mode: Mode pantalla completa: - + Borderless Windowed Finestra sense vores - + Exclusive Fullscreen Pantalla completa exclusiva - + Aspect Ratio: Relació d'aspecte: - + Default (16:9) Valor predeterminat (16:9) - + Force 4:3 Forçar 4:3 - + Force 21:9 Forçar 21:9 - + + Force 16:10 + + + + Stretch to Window Estirar a la finestra - + Resolution: Resolució: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: Filtre d'adaptació de finestra: - + Nearest Neighbor Veí més proper - + Bilinear Bilineal - + Bicubic Bicúbic - + Gaussian Gaussià - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (només Vulkan) - + Anti-Aliasing Method: Mètode d'anti-aliasing - + None Cap - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Utilitza un color de fons global - + Set background color: Configura un color de fons: - + Background Color: Color de fons: @@ -1521,6 +1621,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM (Assembly Shaders, només NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1575,37 +1681,47 @@ This would ban both their forum username and their IP address. Utilitzar temps ràpid a la GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtrat anisotròpic: - + Automatic Automàtic - + Default Valor predeterminat - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2097,7 +2213,7 @@ This would ban both their forum username and their IP address. - + Left Stick Palanca esquerra @@ -2191,14 +2307,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2217,7 +2333,7 @@ This would ban both their forum username and their IP address. - + Plus Més @@ -2230,15 +2346,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2295,231 +2411,236 @@ This would ban both their forum username and their IP address. - + Right Stick Palanca dreta - - - - + + + + Clear Esborrar - - - - - + + + + + [not set] [no establert] - - - Toggle button - Botó commutador - - - - + + Invert button Botó d'inversió - - + + + Toggle button + Botó commutador + + + + Invert axis Invertir eixos - - - + + + Set threshold Configurar llindar - - + + Choose a value between 0% and 100% Esculli un valor entre 0% i 100% - + + Toggle axis + + + + Set gyro threshold Configurar llindar giroscopi - + Map Analog Stick Configuració de palanca analògica - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Després de prémer D'acord, primer moveu el joystick horitzontalment i després verticalment. Per invertir els eixos, primer moveu el joystick verticalment i després horitzontalment. - + Center axis Centrar eixos - - + + Deadzone: %1% Zona morta: %1% - - + + Modifier Range: %1% Rang del modificador: %1% - - + + Pro Controller Controlador Pro - + Dual Joycons Joycons duals - + Left Joycon Joycon esquerra - + Right Joycon Joycon dret - + Handheld Portàtil - + GameCube Controller Controlador de GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Controlador NES - + SNES Controller Controlador SNES - + N64 Controller Controlador N64 - + Sega Genesis Sega Genesis - + Start / Pause Inici / Pausa - + Z Z - + Control Stick Palanca de control - + C-Stick C-Stick - + Shake! Sacseja! - + [waiting] [esperant] - + New Profile Nou perfil - + Enter a profile name: Introdueixi un nom de perfil: - - + + Create Input Profile Crear perfil d'entrada - + The given profile name is not valid! El nom de perfil introduït no és vàlid! - + Failed to create the input profile "%1" Error al crear el perfil d'entrada "%1" - + Delete Input Profile Eliminar perfil d'entrada - + Failed to delete the input profile "%1" Error al eliminar el perfil d'entrada "%1" - + Load Input Profile Carregar perfil d'entrada - + Failed to load the input profile "%1" Error al carregar el perfil d'entrada "%1" - + Save Input Profile Guardar perfil d'entrada - + Failed to save the input profile "%1" Error al guardar el perfil d'entrada "%1" @@ -2774,42 +2895,42 @@ Per invertir els eixos, primer moveu el joystick verticalment i després horitzo Desenvolupador - + Add-Ons Complements - + General General - + System Sistema - + CPU CPU - + Graphics Gràfics - + Adv. Graphics Gràfics avanç. - + Audio Àudio - + Properties Propietats @@ -2865,37 +2986,37 @@ Per invertir els eixos, primer moveu el joystick verticalment i després horitzo Usuari actual - + Username Nom d'usuari - + Set Image Establir imatge - + Add Afegir - + Rename Renombrar - + Remove Eliminar - + Profile management is available only when game is not running. La gestió de perfils només està disponible quan el joc no s'està executant. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2903,96 +3024,105 @@ Per invertir els eixos, primer moveu el joystick verticalment i després horitzo %2 - + Enter Username Introdueixi el nom d'usuari - + Users Usuaris - + Enter a username for the new user: Introdueixi un nom d'usuari per al nou usuari: - + Enter a new username: Introdueixi un nou nom d'usuari: - - Confirm Delete - Confirmar eliminació - - - - You are about to delete user with name "%1". Are you sure? - Està a punt d'eliminar un usuari amb el nom "%1". Està segur? - - - + Select User Image Seleccioni una imatge d'usuari - + JPEG Images (*.jpg *.jpeg) Imatges JPEG (*.jpg *.jpeg) - + Error deleting image Error al eliminar la imatge - + Error occurred attempting to overwrite previous image at: %1. Error al intentar sobreescriure la imatge anterior a: %1. - + Error deleting file Error al eliminar el fitxer - + Unable to delete existing file: %1. No es pot eliminar el fitxer existent: %1. - + Error creating user image directory Error al crear el directori d'imatges de l'usuari - + Unable to create directory %1 for storing user images. No es pot crear el directori %1 per emmagatzemar imatges d’usuari. - + Error copying user image Error al copiar la imatge de l'usuari - + Unable to copy image from %1 to %2 No es pot copiar la imatge de %1 a %2 - + Error resizing user image Error al redimensionar la imatge d'usuari - + Unable to resize image No es pot redimensionar la imatge + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Confirmar eliminació + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3521,47 +3651,47 @@ Per invertir els eixos, primer moveu el joystick verticalment i després horitzo <html><head/><body><p>Llegeix l'entrada dels controladors des dels scripts en el mateix format que els scripts TAS-nx.<br/>Per a una explicació més detallada, si us plau, consulti la <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">pàgina d'ajuda</span></a> a la pàgina web de yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Per a comprovar quines tecles d'accés ràpid controlen la reproducció/gravació, si us plau, revisi la configuració de les tecles d'accés ràpid (Configuració -> General -> Tecles d'accés ràpid) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. AVÍS: Aquesta és una característica experimental.<br/>No es reproduiran els scripts perfectament amb l'actual i imperfecte mètode de sincronització de cuadres. - + Settings Paràmetres - + Enable TAS features Activar funcionalitats TAS - + Loop script Repetir script en bucle - + Pause execution during loads Pausar execució durant les càrregues - + Script Directory Directori d'scripts - + Path Ruta - + ... ... @@ -3813,56 +3943,71 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les + Show Compatibility List + + + + Show Add-Ons Column Mostrar columna de complements - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: Tamany de les icones dels jocs - + Folder Icon Size: Tamany de les icones de les carpetes - + Row 1 Text: Text de la fila 1: - + Row 2 Text: Text de la fila 2: - + Screenshots Captures de pantalla - + Ask Where To Save Screenshots (Windows Only) Preguntar on guardar les captures de pantalla (només Windows) - + Screenshots Path: Ruta de les captures de pantalla: - + ... ... - + Select Screenshots Path... Seleccioni el directori de les Captures de Pantalla... - + <System> <System> @@ -3971,7 +4116,7 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les - + Verify Verificar @@ -4058,7 +4203,7 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les - + Unspecified Sense especificar @@ -4073,17 +4218,36 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les El token no ha sigut verificat. El canvi al seu token no s'ha guardat. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Comprovant... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verificació fallida + + + Verification failed Verificació fallida - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verificació fallida. Comprovi que hagi ingressat el seu token correctament, i que la seva connexió a internet estigui funcionant. @@ -4152,12 +4316,12 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les DirectConnectWindow - + Connecting - + Connect @@ -4165,488 +4329,491 @@ Arrossegui els punts per a canviar la posició, o faci doble clic a les cel·les GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Es recullen dades anònimes</a> per ajudar a millorar yuzu. <br/><br/>Desitja compartir les seves dades d'ús amb nosaltres? - + Telemetry Telemetria - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Carregant Web applet... - - + + Disable Web Applet Desactivar el Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Desactivar l'Applet Web pot provocar comportaments indefinits i només hauria d'utilitzar-se amb Super Mario 3D All-Stars. Estàs segur de que vols desactivar l'Applet Web? (Això pot ser reactivat als paràmetres Debug.) - + The amount of shaders currently being built La quantitat de shaders que s'estan compilant actualment - + The current selected resolution scaling multiplier. El multiplicador d'escala de resolució seleccionat actualment. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocitat d'emulació actual. Valors superiors o inferiors a 100% indiquen que l'emulació s'està executant més ràpidament o més lentament que a la Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Quants fotogrames per segon està mostrant el joc actualment. Això variarà d'un joc a un altre i d'una escena a una altra. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Temps que costa emular un fotograma de la Switch, sense tenir en compte la limitació de fotogrames o la sincronització vertical. Per a una emulació òptima, aquest valor hauria de ser com a màxim de 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Esborrar arxius recents - + &Continue &Continuar - + &Pause &Pausar - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu està executant un joc - + Warning Outdated Game Format Advertència format del joc desfasat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Està utilitzant el format de directori de ROM deconstruït per a aquest joc, que és un format desactualitzat que ha sigut reemplaçat per altres, com NCA, NAX, XCI o NSP. Els directoris de ROM deconstruïts careixen d'icones, metadades i suport d'actualitzacions.<br><br>Per a obtenir una explicació dels diversos formats de Switch que suporta yuzu,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>faci una ullada a la nostra wiki</a>. Aquest missatge no es tornarà a mostrar. - - + + Error while loading ROM! Error carregant la ROM! - + The ROM format is not supported. El format de la ROM no està suportat. - + An error occurred initializing the video core. S'ha produït un error inicialitzant el nucli de vídeo. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu ha trobat un error mentre executava el nucli de vídeo. Això sol ser causat per controladors de la GPU obsolets, inclosos els integrats. Si us plau, consulti el registre per a més detalls. Per obtenir més informació sobre com accedir al registre, consulti la següent pàgina: <a href='https://yuzu-emu.org/help/reference/log-files/'>Com carregar el fitxer de registre</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Error al carregar la ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Si us plau, segueixi <a href='https://yuzu-emu.org/help/quickstart/'>la guia d'inici de yuzu</a> per a bolcar de nou els seus fitxers.<br>Pot consultar la wiki de yuzu wiki</a> o el Discord de yuzu</a> per obtenir ajuda. - + An unknown error occurred. Please see the log for more details. S'ha produït un error desconegut. Si us plau, consulti el registre per a més detalls. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Dades de partides guardades - + Mod Data Dades de mods - + Error Opening %1 Folder Error obrint la carpeta %1 - - + + Folder does not exist! La carpeta no existeix! - + Error Opening Transferable Shader Cache Error obrint la cache transferible de shaders - + Failed to create the shader cache directory for this title. No s'ha pogut crear el directori de la cache dels shaders per aquest títol. - - Contents - Continguts + + Error Removing Contents + - - Update - Actualització + + Error Removing Update + - - DLC - DLC + + Error Removing DLC + - + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + Remove Entry Eliminar entrada - - Remove Installed Game %1? - Eliminar el joc instal·lat %1? - - - - - - - - + + + + + + Successfully Removed S'ha eliminat correctament - + Successfully removed the installed base game. S'ha eliminat correctament el joc base instal·lat. - - - - Error Removing %1 - Error eliminant %1 - - - + The base game is not installed in the NAND and cannot be removed. El joc base no està instal·lat a la NAND i no pot ser eliminat. - + Successfully removed the installed update. S'ha eliminat correctament l'actualització instal·lada. - + There is no update installed for this title. No hi ha cap actualització instal·lada per aquest títol. - + There are no DLC installed for this title. No hi ha cap DLC instal·lat per aquest títol. - + Successfully removed %1 installed DLC. S'ha eliminat correctament %1 DLC instal·lat/s. - + Delete OpenGL Transferable Shader Cache? Desitja eliminar la cache transferible de shaders d'OpenGL? - + Delete Vulkan Transferable Shader Cache? Desitja eliminar la cache transferible de shaders de Vulkan? - + Delete All Transferable Shader Caches? Desitja eliminar totes les caches transferibles de shaders? - + Remove Custom Game Configuration? Desitja eliminar la configuració personalitzada del joc? - + Remove File Eliminar arxiu - - + + Error Removing Transferable Shader Cache Error eliminant la cache transferible de shaders - - + + A shader cache for this title does not exist. No existeix una cache de shaders per aquest títol. - + Successfully removed the transferable shader cache. S'ha eliminat correctament la cache transferible de shaders. - + Failed to remove the transferable shader cache. No s'ha pogut eliminar la cache transferible de shaders. - - + + Error Removing Transferable Shader Caches Error al eliminar les caches de shaders transferibles - + Successfully removed the transferable shader caches. Caches de shaders transferibles eliminades correctament. - + Failed to remove the transferable shader cache directory. No s'ha pogut eliminar el directori de caches de shaders transferibles. - - + + Error Removing Custom Configuration Error eliminant la configuració personalitzada - + A custom configuration for this title does not exist. No existeix una configuració personalitzada per aquest joc. - + Successfully removed the custom game configuration. S'ha eliminat correctament la configuració personalitzada del joc. - + Failed to remove the custom game configuration. No s'ha pogut eliminar la configuració personalitzada del joc. - - + + RomFS Extraction Failed! La extracció de RomFS ha fallat! - + There was an error copying the RomFS files or the user cancelled the operation. S'ha produït un error copiant els arxius RomFS o l'usuari ha cancel·lat la operació. - + Full Completa - + Skeleton Esquelet - + Select RomFS Dump Mode Seleccioni el mode de bolcat de RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Si us plau, seleccioni la forma en que desitja bolcar la RomFS.<br>Completa copiarà tots els arxius al nou directori mentre que<br>esquelet només crearà l'estructura de directoris. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root No hi ha suficient espai lliure a %1 per extreure el RomFS. Si us plau, alliberi espai o esculli un altre directori de bolcat a Emulació > Configuració > Sistema > Sistema d'arxius > Carpeta arrel de bolcat - + Extracting RomFS... Extraient RomFS... - - + + Cancel Cancel·la - + RomFS Extraction Succeeded! Extracció de RomFS completada correctament! - + The operation completed successfully. L'operació s'ha completat correctament. - + Error Opening %1 Error obrint %1 - + Select Directory Seleccionar directori - + Properties Propietats - + The game properties could not be loaded. Les propietats del joc no s'han pogut carregar. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Executable de Switch (%1);;Tots els Arxius (*.*) - + Load File Carregar arxiu - + Open Extracted ROM Directory Obrir el directori de la ROM extreta - + Invalid Directory Selected Directori seleccionat invàlid - + The directory you have selected does not contain a 'main' file. El directori que ha seleccionat no conté un arxiu 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Arxiu de Switch Instal·lable (*.nca *.nsp *.xci);;Arxiu de Continguts Nintendo (*.nca);;Paquet d'enviament Nintendo (*.nsp);;Imatge de Cartutx NX (*.xci) - + Install Files Instal·lar arxius - + %n file(s) remaining %n arxiu(s) restants%n arxiu(s) restants - + Installing file "%1"... Instal·lant arxiu "%1"... - - + + Install Results Resultats instal·lació - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Per evitar possibles conflictes, no recomanem als usuaris que instal·lin jocs base a la NAND. Si us plau, utilitzi aquesta funció només per a instal·lar actualitzacions i DLCs. - + %n file(s) were newly installed %n nou(s) arxiu(s) s'ha(n) instal·lat @@ -4654,7 +4821,7 @@ Si us plau, utilitzi aquesta funció només per a instal·lar actualitzacions i - + %n file(s) were overwritten %n arxiu(s) s'han sobreescrit @@ -4662,7 +4829,7 @@ Si us plau, utilitzi aquesta funció només per a instal·lar actualitzacions i - + %n file(s) failed to install %n arxiu(s) no s'han instal·lat @@ -4670,411 +4837,410 @@ Si us plau, utilitzi aquesta funció només per a instal·lar actualitzacions i - + System Application Aplicació del sistema - + System Archive Arxiu del sistema - + System Application Update Actualització de l'aplicació del sistema - + Firmware Package (Type A) Paquet de firmware (Tipus A) - + Firmware Package (Type B) Paquet de firmware (Tipus B) - + Game Joc - + Game Update Actualització de joc - + Game DLC DLC del joc - + Delta Title Títol delta - + Select NCA Install Type... Seleccioni el tipus d'instal·lació NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Seleccioni el tipus de títol que desitja instal·lar aquest NCA com a: (En la majoria dels casos, el valor predeterminat 'Joc' està bé.) - + Failed to Install Ha fallat la instal·lació - + The title type you selected for the NCA is invalid. El tipus de títol seleccionat per el NCA és invàlid. - + File not found Arxiu no trobat - + File "%1" not found Arxiu "%1" no trobat - + OK D'acord - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Falta el compte de yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Per tal d'enviar un cas de prova de compatibilitat de joc, ha de vincular el seu compte de yuzu.<br><br/>Per a vincular el seu compte de yuzu, vagi a Emulació & gt; Configuració & gt; Web. - + Error opening URL Error obrint URL - + Unable to open the URL "%1". No es pot obrir la URL "%1". - + TAS Recording Gravació TAS - + Overwrite file of player 1? Sobreescriure l'arxiu del jugador 1? - + Invalid config detected Configuració invàlida detectada - + Handheld controller can't be used on docked mode. Pro controller will be selected. El controlador del mode portàtil no es pot fer servir en el mode acoblat. Es seleccionarà el controlador Pro en el seu lloc. - - - Error - Error - - - - - The current game is not looking for amiibos - El joc actual no està buscant amiibos - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed L'amiibo actual ha sigut eliminat - + + Error + Error + + + + + The current game is not looking for amiibos + El joc actual no està buscant amiibos + + + Amiibo File (%1);; All Files (*.*) Arxiu Amiibo (%1);; Tots els Arxius (*.*) - + Load Amiibo Carregar Amiibo - - Error opening Amiibo data file - Error obrint l'arxiu de dades d'Amiibo - - - - Unable to open Amiibo file "%1" for reading. - No s'ha pogut obrir l'arxiu de dades d'Amiibo "%1" per a lectura. - - - - Error reading Amiibo data file - Error llegint l'arxiu de dades d'Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - No s'han pogut llegir completament les dades d'Amiibo. S'esperava llegir %1 bytes, però només s'han pogut llegir %2 bytes. - - - + Error loading Amiibo data Error al carregar les dades d'Amiibo - - Unable to load Amiibo data. - No s'han pogut carregar les dades d'Amiibo. + + The selected file is not a valid amiibo + - + + The selected file is already on use + + + + + An unknown error occurred + + + + Capture Screenshot Captura de pantalla - + PNG Image (*.png) Imatge PNG (*.png) - + TAS state: Running %1/%2 Estat TAS: executant %1/%2 - + TAS state: Recording %1 Estat TAS: gravant %1 - + TAS state: Idle %1/%2 Estat TAS: inactiu %1/%2 - + TAS State: Invalid Estat TAS: invàlid - + &Stop Running &Parar l'execució - + &Start &Iniciar - + Stop R&ecording Parar g&ravació - + R&ecord G&ravar - + Building: %n shader(s) Construint: %n shader(s)Construint: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Escala: %1x - + Speed: %1% / %2% Velocitat: %1% / %2% - + Speed: %1% Velocitat: %1% - + Game: %1 FPS (Unlocked) Joc: %1 FPS (desbloquejat) - + Game: %1 FPS Joc: %1 FPS - + Frame: %1 ms Fotograma: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU ALTA - + GPU EXTREME GPU EXTREMA - + GPU ERROR ERROR GPU - + DOCKED - + HANDHELD - + NEAREST MÉS PROPER - - + + BILINEAR BILINEAL - + BICUBIC BICÚBIC - + GAUSSIAN GAUSSIÀ - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA SENSE AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. El joc que està intentant carregar requereix d'arxius addicionals de la seva Switch abans de poder jugar. <br/><br/>Per a obtenir més informació sobre com bolcar aquests arxius, vagi a la següent pàgina de la wiki: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Bolcar arxius del sistema i les fonts compartides des d'una Consola Switch</a>. <br/><br/>Desitja tornar a la llista de jocs? Continuar amb l'emulació pot provocar el tancament inesperat, dades de partides guardades corruptes o altres errors. - + yuzu was unable to locate a Switch system archive. %1 yuzu no ha pogut localitzar l'arxiu de sistema de la Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu no ha pogut localitzar un arxiu de sistema de la Switch: %1. %2 - + System Archive Not Found Arxiu del sistema no trobat - + System Archive Missing Falta arxiu del sistema - + yuzu was unable to locate the Switch shared fonts. %1 yuzu no ha pogut trobar les fonts compartides de la Switch. %1 - + Shared Fonts Not Found Fonts compartides no trobades - + Shared Font Missing Falten les fonts compartides - + Fatal Error Error fatal - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu ha trobat un error fatal, consulti el registre per a obtenir més detalls. Per a més informació sobre com accedir al registre, consulti la següent pàgina: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Com carregar l'arxiu de registre?</a>.<br/><br/> Desitja tornar al llistat de jocs? Continuar amb l'emulació pot provocar el tancament inesperat, dades de partides guardades corruptes o altres errors. - + Fatal Error encountered Trobat error fatal - + Confirm Key Rederivation Confirmi la clau de rederivació - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5091,37 +5257,37 @@ i opcionalment faci còpies de seguretat. Això eliminarà els arxius de les claus generats automàticament i tornarà a executar el mòdul de derivació de claus. - + Missing fuses Falten fusibles - + - Missing BOOT0 - Falta BOOT0 - + - Missing BCPKG2-1-Normal-Main - Falta BCPKG2-1-Normal-Main - + - Missing PRODINFO - Falta PRODINFO - + Derivation Components Missing Falten components de derivació - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Falten les claus d'encriptació. <br>Si us plau, segueixi <a href='https://yuzu-emu.org/help/quickstart/'>la guia ràpida de yuzu</a> per a obtenir totes les seves claus, firmware i jocs.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5130,39 +5296,39 @@ Això pot prendre fins a un minut depenent del rendiment del seu sistema. - + Deriving Keys Derivant claus - + Select RomFS Dump Target Seleccioni el destinatari per a bolcar el RomFS - + Please select which RomFS you would like to dump. Si us plau, seleccioni quin RomFS desitja bolcar. - + Are you sure you want to close yuzu? Està segur de que vol tancar yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Està segur de que vol aturar l'emulació? Qualsevol progrés no guardat es perdrà. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5174,38 +5340,38 @@ Desitja tancar-lo de totes maneres? GRenderWindow - + OpenGL not available! OpenGL no disponible! - + yuzu has not been compiled with OpenGL support. yuzu no ha estat compilat amb suport per OpenGL. - - + + Error while initializing OpenGL! Error al inicialitzar OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. La seva GPU no suporta OpenGL, o no té instal·lat els últims controladors gràfics. - + Error while initializing OpenGL 4.6! Error inicialitzant OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 La seva GPU no suporta OpenGL 4.6, o no té instal·lats els últims controladors gràfics.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 És possible que la seva GPU no suporti una o més extensions necessàries d'OpenGL. Si us plau, asseguris de tenir els últims controladors de la tarjeta gràfica.<br><br>GL Renderer:<br>%1<br><br>Extensions no suportades:<br>%2 @@ -5213,153 +5379,153 @@ Desitja tancar-lo de totes maneres? GameList - + Favorite Preferit - + Start Game Iniciar el joc - + Start Game without Custom Configuration Iniciar el joc sense la configuració personalitzada - + Open Save Data Location Obrir la ubicació dels arxius de partides guardades - + Open Mod Data Location Obrir la ubicació dels mods - + Open Transferable Pipeline Cache Obrir cache transferible de shaders de canonada - + Remove Eliminar - + Remove Installed Update Eliminar actualització instal·lada - + Remove All Installed DLC Eliminar tots els DLC instal·lats - + Remove Custom Configuration Eliminar configuració personalitzada - + Remove OpenGL Pipeline Cache Eliminar cache de canonada d'OpenGL - + Remove Vulkan Pipeline Cache Eliminar cache de canonada de Vulkan - + Remove All Pipeline Caches Eliminar totes les caches de canonada - + Remove All Installed Contents Eliminar tots els continguts instal·lats - + Dump RomFS Bolcar RomFS - + Dump RomFS to SDMC Bolcar RomFS a SDMC - + Copy Title ID to Clipboard Copiar la ID del títol al porta-retalls - + Navigate to GameDB entry Navegar a l'entrada de GameDB - + Properties Propietats - + Scan Subfolders Escanejar subdirectoris - + Remove Game Directory Eliminar directori de jocs - + ▲ Move Up ▲ Moure amunt - + ▼ Move Down ▼ Move avall - + Open Directory Location Obre ubicació del directori - + Clear Esborrar - + Name Nom - + Compatibility Compatibilitat - + Add-ons Complements - + File type Tipus d'arxiu - + Size Mida @@ -5368,81 +5534,61 @@ Desitja tancar-lo de totes maneres? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Perfecte - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - El joc funciona a la perfecció sense errors d'àudio o gràfics, totes les funcions provades funcionen segons el previst -sense cap solució temporal necessària. - - - - Great - Genial - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - El joc funciona amb errors gràfics o d'àudio menors i es pot jugar de principi a fi. Pot requerir de -solucions temporals. - - Okay - Correcte - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - El joc funciona amb importants errors gràfics o d'àudio, però el joc es pot jugar de principi a fi amb -solucions temporals. + Game can be played without issues. + - Bad - Malament + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - El joc funciona, però amb importants errors gràfics o d'àudio. És impossible avançar en zones específiques -inclús amb solucions temporals. + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu Intro / Menú - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - No és possible jugar a aquest joc degut a importants errors gràfics o d'àudio. És impossible avançar més enllà de la pantalla -d'inici. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot No engega - + The game crashes when attempting to startup. El joc es bloqueja al intentar iniciar. - + Not Tested No provat - + The game has not yet been tested. Aquest joc encara no ha estat provat. @@ -5450,7 +5596,7 @@ d'inici. GameListPlaceholder - + Double-click to add a new folder to the game list Faci doble clic per afegir un nou directori a la llista de jocs @@ -5463,12 +5609,12 @@ d'inici. %1 de %n resultat(s)%1 de %n resultat(s) - + Filter: Filtre: - + Enter pattern to filter Introdueixi patró per a filtrar @@ -5544,12 +5690,12 @@ d'inici. HostRoomWindow - + Error Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5558,11 +5704,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5584,112 +5731,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Captura de pantalla - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Pantalla Completa - + Load File Carregar arxiu - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5804,42 +5950,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Jugadors - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5862,232 +6008,237 @@ Debug Message: &Arxius recents - + &Emulation &Emulació - + &View &Veure - + &Reset Window Size &Reiniciar tamany de finestra - + &Debugging &Depuració - + Reset Window Size to &720p Reiniciar el tamany de la finestra a &720p - + Reset Window Size to 720p Reiniciar el tamany de la finestra a 720p - + Reset Window Size to &900p Reiniciar el tamany de la finestra a &900p - + Reset Window Size to 900p Reiniciar el tamany de la finestra a 900p - + Reset Window Size to &1080p Reiniciar el tamany de la finestra a &1080p - + Reset Window Size to 1080p Reiniciar el tamany de la finestra a 1080p - + + &Multiplayer + + + + &Tools &Eines - + &TAS &TAS - + &Help &Ajuda - + &Install Files to NAND... &instal·lar arxius a la NAND... - + L&oad File... C&arregar arxiu... - + Load &Folder... Carregar &carpeta... - + E&xit S&ortir - + &Pause &Pausar - + &Stop &Aturar - + &Reinitialize keys... &Reinicialitzar claus... - + &About yuzu &Sobre yuzu - + Single &Window Mode Mode una sola &finestra - + Con&figure... Con&figurar... - + Display D&ock Widget Headers Mostrar complements de capçalera del D&ock - + Show &Filter Bar Mostrar la barra de &filtre - + Show &Status Bar Mostrar la barra d'&estat - + Show Status Bar Mostrar barra d'estat - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room + &Browse Public Game Lobby - - Direct Connect to Room + + &Create Room - - Show Current Room + + &Leave Room + &Direct Connect to Room + + + + + &Show Current Room + + + + F&ullscreen P&antalla completa - + &Restart &Reiniciar - + Load/Remove &Amiibo... Carregar/Eliminar &Amiibo... - + &Report Compatibility &Informar de compatibilitat - + Open &Mods Page Obrir la pàgina de &mods - + Open &Quickstart Guide Obre la guia d'&inici ràpid - + &FAQ &Preguntes freqüents - + Open &yuzu Folder Obrir la carpeta de &yuzu - + &Capture Screenshot &Captura de pantalla - + &Configure TAS... &Configurar TAS... - + Configure C&urrent Game... Configurar joc a&ctual... - + &Start &Iniciar - + &Reset &Reiniciar - + R&ecord E&nregistrar @@ -6152,46 +6303,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Connectat - - - - Not Connected - + + Connected + Connectat + + + + New Messages Received + + + + Error Error - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6292,22 +6438,39 @@ They may have left the room. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6315,7 +6478,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Error @@ -6364,42 +6527,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 is playing %2 - + Not playing a game - + Installed SD Titles Títols instal·lats a la SD - + Installed NAND Titles Títols instal·lats a la NAND - + System Titles Títols del sistema - + Add New Game Directory Afegir un nou directori de jocs - + Favorites Preferits @@ -6429,7 +6592,7 @@ p, li { white-space: pre-wrap; } - + [not set] [no establert] @@ -6444,10 +6607,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Eix %1%2 @@ -6461,9 +6624,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [desconegut] @@ -6628,15 +6791,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [invàlid] - - + + %1%2Hat %3 %1%2Rotació %3 @@ -6644,35 +6807,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Eix %3 - + %1%2Axis %3,%4,%5 %1%2Eixos %3,%4,%5 - + %1%2Motion %3 %1%2Moviment %3 - - + + %1%2Button %3 %1%2Botó %3 - + [unused] [sense ús] @@ -6713,11 +6876,124 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Nom + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6821,6 +7097,7 @@ p, li { white-space: pre-wrap; } + Handheld Portàtil @@ -6860,11 +7137,6 @@ p, li { white-space: pre-wrap; } Docked Acoblada - - - Undocked - Portàtil - Vibration diff --git a/dist/languages/cs.ts b/dist/languages/cs.ts index cc83592..3e879cd 100644 --- a/dist/languages/cs.ts +++ b/dist/languages/cs.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -204,7 +204,7 @@ This would ban both their forum username and their IP address. - %1 (%2/%3 members) - connected + %1 - %2 (%3/%4 members) - connected @@ -218,6 +218,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Nahlásit kompatibilitu hry. @@ -227,92 +232,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Rozmysli si jestli chceš poslat data do: </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Listu Kompatibility s yuzu</span></a><span style=" font-size:10pt;">, následující informace budou uloženy a zobrazeny na stránce:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">informace o hardwaru (CPU / GPU / Operační Systém)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Verze yuzu</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Přidružený účet</li></ul></body></html> - - Perfect - Perfektní + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Hra funguje bez chyby bez grafických nebo zvukových artefaktů.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Skvělé + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Hra funguje s jemnými grafickými nebo vizuálními chybami, ale je hratelná od startu do konce, avšak může potřebovat problém obejít.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Okej + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Hra funguje se zasadními grafickými nebo zvukovými chybami, ale je hratelná od začátku do konce s překážkami. + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Špatný + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Hra funguje, ale s velkými grafickými nebo zvukovými chybami. Nejde se dostat přes specifická místa, ani s obkličkami.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Intro/Menu + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Hra nejde kompletně hrát z důvodu grafických nebo zvukových chyb. Nejde se dostat přes Start Screen.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Ne-bootuje + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Hra crashuje při startu.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Nezávislé na rychlosti nebo výkona, jak dobře se tato hra hraje na této verzi yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Děkujeme za odezvu! - + Submitting Potvrzuji - + Communication error Chyba komunikace - + An error occurred while sending the Testcase Chyba při odesílání testovacího případu - + Next Další @@ -410,7 +450,7 @@ This would ban both their forum username and their IP address. Vrátit výchozí nastavení - + Auto Automatické @@ -755,200 +795,235 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj ConfigureDebug - + Debugger Debugger - + Enable GDB Stub Povolit GDB Stub - + Port: Port: - + Logging Logování - + Global Log Filter Centrální log filtr - + Show Log in Console Zobrazit log v konzoli - + Open Log Location Otevřít lokaci s logama - + When checked, the max size of the log increases from 100 MB to 1 GB Po povolení se zvýší maximální velikost logu z 100 MB na 1 GB. - + Enable Extended Logging** Povolit rozšířené logování - + Homebrew Homebrew - + Arguments String Argumenty - + Graphics Grafika - + When checked, the graphics API enters a slower debugging mode Po povolení se grafické API přepne do pomalejšího režimu ladění. - + Enable Graphics Debugging Zapnout ladění grafiky - + When checked, it enables Nsight Aftermath crash dumps Zaškrtnutí povolí crash dumpy Nsight Aftermath - + Enable Nsight Aftermath Povolit Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Když je zaškrtnuto, dumpne všechny originální shadery assembleru z diskové zálohy shaderů či hry jak jsou nalezeny. - + Dump Game Shaders Dumpnout shadery hry - + When checked, it will dump all the macro programs of the GPU Když je zaškrtnuto, dumpne všechny makro programy GPU - + Dump Maxwell Macros Dumpnout Maxwell Makra - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Po povolení se zakáže makro Just-in-Time překladač. Poté hry poběží pomaleji. - + Disable Macro JIT Zakázat Makro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Když je zaškrtnuto, yuzu bude logovat statistiky o kompilované mezipaměti pipelinu - + Enable Shader Feedback Povolit Shader Feedback - + When checked, it executes shaders without loop logic changes Když je zaškrtnuto, shadery budou exekutovány bez změn logických smyček. - + Disable Loop safety checks - + Debugging Ladění - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Pokročilé - + Kiosk (Quest) Mode Předváděcí (Quest/Kiosk) režim - + Enable CPU Debugging - + Enable Debug Asserts Povolit Debug Asserts - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet Zakázat Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1318,193 +1393,218 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj API: - + Graphics Settings Nastavení grafiky - + Use disk pipeline cache - + Use asynchronous GPU emulation Použít asynchronní emulaci GPU - + Accelerate ASTC texture decoding - + NVDEC emulation: - + No Video Output - + CPU Video Decoding - + GPU Video Decoding (Default) - + Fullscreen Mode: Režim celé obrazovky: - + Borderless Windowed Okno bez okrajů - + Exclusive Fullscreen Exkluzivní - + Aspect Ratio: Poměr stran: - + Default (16:9) Výchozí (16:9) - + Force 4:3 Vynutit 4:3 - + Force 21:9 Vynutit 21:9 - + + Force 16:10 + + + + Stretch to Window Roztáhnout podle okna - + Resolution: - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) - + 2X (1440p/2160p) - + 3X (2160p/3240p) - + 4X (2880p/4320p) - + 5X (3600p/5400p) - + 6X (4320p/6480p) - + Window Adapting Filter: - + Nearest Neighbor - + Bilinear - + Bicubic - + Gaussian - + ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) - + Anti-Aliasing Method: - + None Žádné - + FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Použít globální barvu pozadí - + Set background color: Nastavit barvu pozadí: - + Background Color: Barva Pozadí: @@ -1513,6 +1613,12 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj GLASM (Assembly Shaders, NVIDIA Only) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1567,37 +1673,47 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anizotropní filtrování: - + Automatic - + Default Výchozí - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2089,7 +2205,7 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + Left Stick Levá Páčka @@ -2183,14 +2299,14 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + L L - + ZL ZL @@ -2209,7 +2325,7 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + Plus Plus @@ -2222,15 +2338,15 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + R R - + ZR ZR @@ -2287,231 +2403,236 @@ Tato možnost zlepšuje rychlost díky závislosti na sémantice cmpxchg pro zaj - + Right Stick Pravá páčka - - - - + + + + Clear Vyčistit - - - - - + + + + + [not set] [nenastaveno] - - - Toggle button - Přepnout tlačítko - - - - + + Invert button - - + + + Toggle button + Přepnout tlačítko + + + + Invert axis Převrátit osy - - - + + + Set threshold - - + + Choose a value between 0% and 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Namapovat analogovou páčku - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Po stisknutí OK nejprve posuňte joystick horizontálně, poté vertikálně. Pro převrácení os nejprve posuňte joystick vertikálně, poté horizontálně. - + Center axis - - + + Deadzone: %1% Deadzone: %1% - - + + Modifier Range: %1% Rozsah modifikátoru: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Dual Joycons - + Left Joycon Levý Joycon - + Right Joycon Pravý Joycon - + Handheld V rukou - + GameCube Controller Ovladač GameCube - + Poke Ball Plus - + NES Controller - + SNES Controller - + N64 Controller - + Sega Genesis - + Start / Pause Start / Pause - + Z Z - + Control Stick Control Stick - + C-Stick C-Stick - + Shake! Shake! - + [waiting] [čekání] - + New Profile Nový profil - + Enter a profile name: Zadejte název profilu: - - + + Create Input Profile Vytvořit profil vstupu - + The given profile name is not valid! Zadaný název profilu není platný! - + Failed to create the input profile "%1" Nepodařilo se vytvořit profil vstupu "%1" - + Delete Input Profile Odstranit profil vstupu - + Failed to delete the input profile "%1" Nepodařilo se odstranit profil vstupu "%1" - + Load Input Profile Načíst profil vstupu - + Failed to load the input profile "%1" Nepodařilo se načíst profil vstupu "%1" - + Save Input Profile Uložit profil vstupu - + Failed to save the input profile "%1" Nepodařilo se uložit profil vstupu "%1" @@ -2766,42 +2887,42 @@ Pro převrácení os nejprve posuňte joystick vertikálně, poté horizontáln Vývojář - + Add-Ons Doplňky - + General Obecné - + System Systém - + CPU CPU - + Graphics Grafika - + Adv. Graphics Pokroč. grafika - + Audio Zvuk - + Properties Vlastnosti @@ -2857,37 +2978,37 @@ Pro převrácení os nejprve posuňte joystick vertikálně, poté horizontáln Aktuální uživatel - + Username Přezdívka - + Set Image Nastavit obrázek - + Add Přidat - + Rename Přejmenovat - + Remove Odebrat - + Profile management is available only when game is not running. Spravování profilů je k dispozici, pouze když neběží žádná hra. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2895,96 +3016,105 @@ Pro převrácení os nejprve posuňte joystick vertikálně, poté horizontáln %2 - + Enter Username Zadejte přezdívku - + Users Uživatelé - + Enter a username for the new user: Zadejte přezdívku pro nového uživatele: - + Enter a new username: Zadejte novou přezdívku: - - Confirm Delete - Potvrdit smazání - - - - You are about to delete user with name "%1". Are you sure? - Přejete si odstranit uživatele se jménem "%1"? - - - + Select User Image Vyberte obrázek uživatele - + JPEG Images (*.jpg *.jpeg) Obrázek JPEG (*.jpg *.jpeg) - + Error deleting image Chyba při odstraňování obrázku - + Error occurred attempting to overwrite previous image at: %1. Chyba při přepisování předchozího obrázku na: %1 - + Error deleting file Chyba při odstraňování souboru - + Unable to delete existing file: %1. Nelze odstranit existující soubor: %1. - + Error creating user image directory Chyba při vytváření složky s obrázkem uživatele - + Unable to create directory %1 for storing user images. Nelze vytvořit složku %1 pro ukládání obrázků uživatele. - + Error copying user image Chyba při kopírování obrázku uživatele - + Unable to copy image from %1 to %2 Nelze zkopírovat obrázek z %1 do %2 - + Error resizing user image - + Unable to resize image + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Potvrdit smazání + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3513,47 +3643,47 @@ Pro převrácení os nejprve posuňte joystick vertikálně, poté horizontáln - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings Nastavení - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Cesta - + ... ... @@ -3805,56 +3935,71 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z + Show Compatibility List + + + + Show Add-Ons Column Ukázat sloupec Doplňky - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: - + Folder Icon Size: - + Row 1 Text: Text řádku 1: - + Row 2 Text: Text řádku 2: - + Screenshots Snímek obrazovky - + Ask Where To Save Screenshots (Windows Only) Zeptat se, kam uložit snímek obrazovky (pouze Windows) - + Screenshots Path: Cesta snímků obrazovky: - + ... ... - + Select Screenshots Path... Vyberte cestu ke snímkům obrazovky... - + <System> <System> @@ -3963,7 +4108,7 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z - + Verify Ověřit @@ -4050,7 +4195,7 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z - + Unspecified Nespecifikovaný @@ -4065,17 +4210,36 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z Token nebyl ověřen. Změna k vašemu tokenu nebyla uložena. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Ověřuji... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Ověřování selhalo + + + Verification failed Ověřování selhalo - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Ověřování selhalo. Zkontrolujte, zda jste zadali token správně a že vaše připojení k internetu funguje v pořádku. @@ -4144,12 +4308,12 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z DirectConnectWindow - + Connecting - + Connect @@ -4157,909 +4321,911 @@ Táhněte body pro změnu pozice nebo dvojitě klikněte na buňky tabulky pro z GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymní data jsou sbírána</a> pro vylepšení yuzu. <br/><br/>Chcete s námi sdílet anonymní data? - + Telemetry Telemetry - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Načítání Web Appletu... - - + + Disable Web Applet Zakázat Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Počet aktuálně sestavovaných shaderů - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Aktuální emulační rychlost. Hodnoty vyšší než 100% indikují, že emulace běží rychleji nebo pomaleji než na Switchi. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Kolik snímků za sekundu aktuálně hra zobrazuje. Tohle závisí na hře od hry a scény od scény. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Čas potřebný na emulaci framu scény, nepočítá se limit nebo v-sync. Pro plnou rychlost by se tohle mělo pohybovat okolo 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Vymazat poslední soubory - + &Continue &Pokračovat - + &Pause &Pauza - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Varování Zastaralý Formát Hry - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Používáte rozbalený formát hry, který je zastaralý a byl nahrazen jinými jako NCA, NAX, XCI, nebo NSP. Rozbalená ROM nemá ikony, metadata, a podporu updatů.<br><br>Pro vysvětlení všech možných podporovaných typů, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>zkoukni naší wiki</a>. Tato zpráva se nebude znova zobrazovat. - - + + Error while loading ROM! Chyba při načítání ROM! - + The ROM format is not supported. Tento formát ROM není podporován. - + An error occurred initializing the video core. Nastala chyba při inicializaci jádra videa. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Chyba při načítání ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Pro extrakci souborů postupujte podle <a href='https://yuzu-emu.org/help/quickstart/'>rychlého průvodce yuzu</a>. Nápovědu naleznete na <br>wiki</a> nebo na Discordu</a>. - + An unknown error occurred. Please see the log for more details. Nastala chyba. Koukni do logu. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Uložit data - + Mod Data Módovat Data - + Error Opening %1 Folder Chyba otevírání složky %1 - - + + Folder does not exist! Složka neexistuje! - + Error Opening Transferable Shader Cache Chyba při otevírání přenositelné mezipaměti shaderů - + Failed to create the shader cache directory for this title. - - Contents - Obsah + + Error Removing Contents + - - Update - Aktualizace + + Error Removing Update + - - DLC - DLC + + Error Removing DLC + - + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + Remove Entry Odebrat položku - - Remove Installed Game %1? - Odebrat Nainstalovanou Hru %1? - - - - - - - - + + + + + + Successfully Removed Úspěšně odebráno - + Successfully removed the installed base game. Úspěšně odebrán nainstalovaný základ hry. - - - - Error Removing %1 - Chyba při odstraňování %1 - - - + The base game is not installed in the NAND and cannot be removed. Základ hry není nainstalovaný na NAND a nemůže být odstraněn. - + Successfully removed the installed update. Úspěšně odebrána nainstalovaná aktualizace. - + There is no update installed for this title. Není nainstalovaná žádná aktualizace pro tento titul. - + There are no DLC installed for this title. Není nainstalované žádné DLC pro tento titul. - + Successfully removed %1 installed DLC. Úspěšně odstraněno %1 nainstalovaných DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? Odstranit vlastní konfiguraci hry? - + Remove File Odstranit soubor - - + + Error Removing Transferable Shader Cache Chyba při odstraňování přenositelné mezipaměti shaderů - - + + A shader cache for this title does not exist. Mezipaměť shaderů pro tento titul neexistuje. - + Successfully removed the transferable shader cache. Přenositelná mezipaměť shaderů úspěšně odstraněna - + Failed to remove the transferable shader cache. Nepodařilo se odstranit přenositelnou mezipaměť shaderů - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration Chyba při odstraňování vlastní konfigurace hry - + A custom configuration for this title does not exist. Vlastní konfigurace hry pro tento titul neexistuje. - + Successfully removed the custom game configuration. Úspěšně odstraněna vlastní konfigurace hry. - + Failed to remove the custom game configuration. Nepodařilo se odstranit vlastní konfiguraci hry. - - + + RomFS Extraction Failed! Extrakce RomFS se nepovedla! - + There was an error copying the RomFS files or the user cancelled the operation. Nastala chyba při kopírování RomFS souborů, nebo uživatel operaci zrušil. - + Full Plný - + Skeleton Kostra - + Select RomFS Dump Mode Vyber RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Vyber jak by si chtěl RomFS vypsat.<br>Plné zkopíruje úplně všechno, ale<br>kostra zkopíruje jen strukturu složky. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Extrahuji RomFS... - - + + Cancel Zrušit - + RomFS Extraction Succeeded! Extrakce RomFS se povedla! - + The operation completed successfully. Operace byla dokončena úspěšně. - + Error Opening %1 Chyba při otevírání %1 - + Select Directory Vybraná Složka - + Properties Vlastnosti - + The game properties could not be loaded. Herní vlastnosti nemohly být načteny. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch Executable (%1);;Všechny soubory (*.*) - + Load File Načíst soubor - + Open Extracted ROM Directory Otevřít složku s extrahovanou ROM - + Invalid Directory Selected Vybraná složka je neplatná - + The directory you have selected does not contain a 'main' file. Složka kterou jste vybrali neobsahuje soubor "main" - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Instalovatelný soubor pro Switch (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Instalovat Soubory - + %n file(s) remaining - + Installing file "%1"... Instalování souboru "%1"... - - + + Install Results Výsledek instalace - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Abychom předešli možným konfliktům, nedoporučujeme uživatelům instalovat základní hry na paměť NAND. Tuto funkci prosím používejte pouze k instalaci aktualizací a DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systémová Aplikace - + System Archive Systémový archív - + System Application Update Systémový Update Aplikace - + Firmware Package (Type A) Firmware-ový baliček (Typu A) - + Firmware Package (Type B) Firmware-ový baliček (Typu B) - + Game Hra - + Game Update Update Hry - + Game DLC Herní DLC - + Delta Title Delta Title - + Select NCA Install Type... Vyberte typ instalace NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Vyberte typ title-u, který chcete nainstalovat tenhle NCA jako: (Většinou základní "game" stačí.) - + Failed to Install Chyba v instalaci - + The title type you selected for the NCA is invalid. Tento typ pro tento NCA není platný. - + File not found Soubor nenalezen - + File "%1" not found Soubor "%1" nenalezen - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Chybí účet yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Pro přidání recenze kompatibility je třeba mít účet yuzu<br><br/>Pro nalinkování yuzu účtu jdi do Emulace &gt; Konfigurace &gt; Web. - + Error opening URL Chyba při otevírání URL - + Unable to open the URL "%1". Nelze otevřít URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected Zjištěno neplatné nastavení - + Handheld controller can't be used on docked mode. Pro controller will be selected. Ruční ovladač nelze používat v dokovacím režimu. Bude vybrán ovladač Pro Controller. - - - Error - - - - - - The current game is not looking for amiibos - - - - - + + Amiibo - - + + The current amiibo has been removed - + + Error + + + + + + The current game is not looking for amiibos + + + + Amiibo File (%1);; All Files (*.*) Soubor Amiibo (%1);; Všechny Soubory (*.*) - + Load Amiibo Načíst Amiibo - - Error opening Amiibo data file - Chyba při načítání souboru Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Amiibo "%1" nešlo otevřít v řežimu pro čtení. - - - - Error reading Amiibo data file - Chyba načítání Amiiba - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Načtení celého Amiiba nebylo možné. Očekáváno bylo %1 bytů, ale pouze %2 bytů se načetlo. - - - + Error loading Amiibo data Chyba načítání Amiiba - - Unable to load Amiibo data. - Načtení Amiiba nebylo možné - - - - Capture Screenshot - Pořídit Snímek Obrazovky - - - - PNG Image (*.png) - PNG Image (*.png) - - - - TAS state: Running %1/%2 + + The selected file is not a valid amiibo - - TAS state: Recording %1 + + The selected file is already on use - - TAS state: Idle %1/%2 + + An unknown error occurred + Capture Screenshot + Pořídit Snímek Obrazovky + + + + PNG Image (*.png) + PNG Image (*.png) + + + + TAS state: Running %1/%2 + + + + + TAS state: Recording %1 + + + + + TAS state: Idle %1/%2 + + + + TAS State: Invalid - + &Stop Running - + &Start &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Rychlost: %1% / %2% - + Speed: %1% Rychlost: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Hra: %1 FPS - + Frame: %1 ms Frame: %1 ms - + GPU NORMAL GPU NORMÁLNÍ - + GPU HIGH GPU VYSOKÝ - + GPU EXTREME GPU EXTRÉMNÍ - + GPU ERROR GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Hra, kterou se snažíte načíst potřebuje další data z vašeho Switche, než bude moci být načtena.<br/><br/>Pro více informací o získání těchto souboru se koukněte na wiki: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Získávání Systémových Archivů a Sdílených Fontu z konzole Switch</a>.<br/><br/>Přejete si odejít do listu her? Pokračování v emulaci by mohlo mít negativní účinky jako crashe, rozbité savy , nebo další bugy. - + yuzu was unable to locate a Switch system archive. %1 Aplikace yuzu nenašla systémový archiv Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 Aplikace yuzu nenašla systémový archiv Switch: %1. %2 - + System Archive Not Found Systémový Archív Nenalezen - + System Archive Missing Chybí systémový archiv - + yuzu was unable to locate the Switch shared fonts. %1 Aplikace yuzu nenašla sdílená písma Switch. %1 - + Shared Fonts Not Found Sdílené Fonty Nenalezeny - + Shared Font Missing Chybí sdílené písmo - + Fatal Error Fatální Chyba - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu narazilo na fatální chybu, prosím kouknšte do logu pro více informací. Pro více informací jak se dostat do logu se koukněte na následující stránku: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'> Jak Uploadnout Log</a>.<br/><br/>Přejete si odejít do listu her? Pokračování v emulaci může mít za následek crashe, rozbité savy, nebo další bugy. - + Fatal Error encountered Vyskytla se kritická chyba - + Confirm Key Rederivation Potvďte Rederivaci Klíčů - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5076,37 +5242,37 @@ a udělejte si zálohu. Toto vymaže věechny vaše automaticky generované klíče a znova spustí modul derivace klíčů. - + Missing fuses Chybí Fuses - + - Missing BOOT0 - Chybí BOOT0 - + - Missing BCPKG2-1-Normal-Main - Chybí BCPKG2-1-Normal-Main - + - Missing PRODINFO - Chybí PRODINFO - + Derivation Components Missing Chybé odvozené komponenty - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5115,39 +5281,39 @@ Tohle může zabrat až minutu podle výkonu systému. - + Deriving Keys Derivuji Klíče - + Select RomFS Dump Target Vyberte Cíl vypsaní RomFS - + Please select which RomFS you would like to dump. Vyberte, kterou RomFS chcete vypsat. - + Are you sure you want to close yuzu? Jste si jist, že chcete zavřít yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Jste si jist, že chcete ukončit emulaci? Jakýkolic neuložený postup bude ztracen. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5159,38 +5325,38 @@ Opravdu si přejete ukončit tuto aplikaci? GRenderWindow - + OpenGL not available! OpenGL není k dispozici! - + yuzu has not been compiled with OpenGL support. yuzu nebylo sestaveno s OpenGL podporou. - - + + Error while initializing OpenGL! Chyba při inicializaci OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Vaše grafická karta pravděpodobně nepodporuje OpenGL nebo nejsou nainstalovány nejnovější ovladače. - + Error while initializing OpenGL 4.6! Chyba při inicializaci OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Vaše grafická karta pravděpodobně nepodporuje OpenGL 4.6 nebo nejsou nainstalovány nejnovější ovladače.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Vaše grafická karta pravděpodobně nepodporuje jedno nebo více rozšíření OpenGL. Ujistěte se prosím, že jsou nainstalovány nejnovější ovladače.<br><br>GL Renderer:<br>%1<br><br>Nepodporované rozšíření:<br>%2 @@ -5198,153 +5364,153 @@ Opravdu si přejete ukončit tuto aplikaci? GameList - + Favorite Oblíbené - + Start Game Spustit hru - + Start Game without Custom Configuration Spustit hru bez vlastní konfigurace - + Open Save Data Location Otevřít Lokaci Savů - + Open Mod Data Location Otevřít Lokaci Modifikací - + Open Transferable Pipeline Cache - + Remove Odstranit - + Remove Installed Update Odstranit nainstalovanou aktualizaci - + Remove All Installed DLC Odstranit všechny nainstalované DLC - + Remove Custom Configuration Odstranit vlastní konfiguraci hry - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents Odstranit všechen nainstalovaný obsah - + Dump RomFS Vypsat RomFS - + Dump RomFS to SDMC - + Copy Title ID to Clipboard Zkopírovat ID Titulu do schránky - + Navigate to GameDB entry Navigovat do GameDB - + Properties Vlastnosti - + Scan Subfolders Prohledat podsložky - + Remove Game Directory Odstranit složku se hrou - + ▲ Move Up ▲ Posunout nahoru - + ▼ Move Down ▼ Posunout dolů - + Open Directory Location Otevřít umístění složky - + Clear Vymazat - + Name Název - + Compatibility Kompatibilita - + Add-ons Modifkace - + File type Typ-Souboru - + Size Velikost @@ -5353,77 +5519,61 @@ Opravdu si přejete ukončit tuto aplikaci? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Perfektní - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - Hra funguje bez problému, bez zvukových nebo grafických glitchů, všechno testované funguje jak má bez žádných obkliček - - - - Great - Skvělé - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - Hra funguje s jemnými grafickými nebo vizuálními chybami, ale je hratelná od startu do konce, avšak může potřebovat problém obejít. - - Okay - Okej - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - Hra funguje se zasadními grafickými nebo zvukovými chybami, ale je hratelná od začátku do konce s překážkami. + Game can be played without issues. + - Bad - Špatný + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Hra funguje se zasadními grafickými nebo zvukovými chybami. Není možné se dostat přes specifická místa, kvůli glitchům -i s obkličkami. + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu Intro/Menu - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Hra nejde kompletně hrát z důvodu grafických nebo zvukových chyb. Nejde se dostat přes Start Screen. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Nebootuje - + The game crashes when attempting to startup. Hra crashuje při startu. - + Not Tested Netestováno - + The game has not yet been tested. Hra ještě nebyla testována @@ -5431,7 +5581,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Dvojitým kliknutím přidáte novou složku do seznamu her @@ -5444,12 +5594,12 @@ Screen. - + Filter: Filtr: - + Enter pattern to filter Zadejte filtr @@ -5525,12 +5675,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5539,11 +5689,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5565,112 +5716,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Pořídit Snímek Obrazovky - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Celá Obrazovka - + Load File Načíst soubor - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5784,42 +5934,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Hráči - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5842,232 +5992,237 @@ Debug Message: &Nedávné soubory - + &Emulation &Emulace - + &View &Pohled - + &Reset Window Size &Resetovat Velikost Okna - + &Debugging &Ladění - + Reset Window Size to &720p Nastavit velikost okna na &720p - + Reset Window Size to 720p Nastavit velikost okna na 720p - + Reset Window Size to &900p Resetovat Velikost Okna na &900p - + Reset Window Size to 900p Resetovat Velikost Okna na 900p - + Reset Window Size to &1080p Nastavit velikost okna na &1080p - + Reset Window Size to 1080p Nastavit velikost okna na 1080p - + + &Multiplayer + + + + &Tools &Nástroje - + &TAS - + &Help &Pomoc - + &Install Files to NAND... &Instalovat soubory na NAND... - + L&oad File... Načís&t soubor... - + Load &Folder... Načíst sl&ožku... - + E&xit E&xit - + &Pause &Pauza - + &Stop &Stop - + &Reinitialize keys... &Znovu inicializovat klíče... - + &About yuzu O &aplikaci yuzu - + Single &Window Mode &Režim jednoho okna - + Con&figure... &Nastavení - + Display D&ock Widget Headers Zobrazit záhlaví widgetů d&oku - + Show &Filter Bar Zobrazit &filtrovací panel - + Show &Status Bar Zobrazit &stavový řádek - + Show Status Bar Zobrazit Staus Bar - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room + &Browse Public Game Lobby - - Direct Connect to Room + + &Create Room - - Show Current Room + + &Leave Room + &Direct Connect to Room + + + + + &Show Current Room + + + + F&ullscreen &Celá obrazovka - + &Restart &Restartovat - + Load/Remove &Amiibo... - + &Report Compatibility &Nahlásit kompatibilitu - + Open &Mods Page Otevřít stránku s &modifikacemi - + Open &Quickstart Guide Otevřít &rychlého průvodce - + &FAQ Často &kladené otázky - + Open &yuzu Folder Otevřít složku s &yuzu - + &Capture Screenshot Za&chytit snímek obrazovky - + &Configure TAS... - + Configure C&urrent Game... Nastavení současné hry - + &Start &Start - + &Reset - + R&ecord @@ -6132,46 +6287,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Připojeno - - - - Not Connected - + + Connected + Připojeno + + + + New Messages Received + + + + Error - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6272,22 +6422,39 @@ They may have left the room. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6295,7 +6462,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6344,42 +6511,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 is playing %2 - + Not playing a game - + Installed SD Titles Nainstalované SD tituly - + Installed NAND Titles Nainstalované NAND tituly - + System Titles Systémové tituly - + Add New Game Directory Přidat novou složku s hrami - + Favorites Oblíbené @@ -6409,7 +6576,7 @@ p, li { white-space: pre-wrap; } - + [not set] [Nenastaveno] @@ -6424,10 +6591,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Osa %1%2 @@ -6441,9 +6608,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [Neznámá] @@ -6608,15 +6775,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6624,35 +6791,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [nepoužito] @@ -6693,11 +6860,124 @@ p, li { white-space: pre-wrap; } - + %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Název + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6801,6 +7081,7 @@ p, li { white-space: pre-wrap; } + Handheld Handheld @@ -6840,11 +7121,6 @@ p, li { white-space: pre-wrap; } Docked Zadokovaná - - - Undocked - Nezadokovaná - Vibration diff --git a/dist/languages/da.ts b/dist/languages/da.ts index b6dea2f..b802e3e 100644 --- a/dist/languages/da.ts +++ b/dist/languages/da.ts @@ -14,7 +14,7 @@ <html><head/><body><p>%1 (%2)</p></body></html> - + <html><head/><body><p>%1 (%2)</p></body></html> @@ -25,17 +25,23 @@ p, li { white-space: pre-wrap; } <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">This software should not be used to play games you have not legally obtained.</span></p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Yuzu er en eksperimentel åben emulator til Nintendo Switch, under GPLv3.0+ licensen.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Denne programvare bør ikke bruges til, at spille spil, du ikke har anskaffet på lovlig vis.</span></p></body></html> <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/">Netsted<span style=" text-decoration: underline; color:#039be5;"></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Kildekode</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Bidragsydere</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Licens</span></a></p></body></html> <html><head/><body><p><span style=" font-size:7pt;">&quot;Nintendo Switch&quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.</span></p></body></html> - <html><head/><body><p><span style=" font-size:7pt;">&quot;Nintendo Switch&quot; er et varemærke tilhørende Nintendo. yuzu er ikke tilknyttet Nintendo på nogen måde.</span></p></body></html> + <html><head/><body><p><span style=" font-size:7pt;">&quot;Nintendo Switch&quot; er et varemærke tilhørende Nintendo. Yuzu er ikke tilknyttet Nintendo på nogen måde.</span></p></body></html> @@ -76,95 +82,97 @@ p, li { white-space: pre-wrap; } Room Window - + Rumvindue Send Chat Message - + Send Chat-Besked Send Message - + Send Besked - + Members - + Medlemmer - + %1 has joined - + %1 har tilsluttet sig - + %1 has left - + %1 er gået - + %1 has been kicked - + %1 har fået sparket - + %1 has been banned - + %1 er blevet bandlyst - + %1 has been unbanned - + %1 er ikke længere bandlyst - + View Profile - - - - - - Block Player - - - - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - - - - - Kick - - - - - Ban - + Vis Profil + - Kick Player - + Block Player + Blokér Spiller + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + Når du blokerer en spiller, vil du ikke længere modtage chat-beskeder fra vedkommende.<br><br>Er du sikker på, at du vil blokere %1? + + + + Kick + Giv Sparket + + + + Ban + Lys i Band + + + + Kick Player + Giv Spiller Sparket + + + Are you sure you would like to <b>kick</b> %1? - + Er du sikker på, at du vil give %1 <b>sparket?</b> - + Ban Player - + Lys Spiller i Band - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - + Er du sikker på, at du vil <b>sparke og bandlyse</b> %1? + +Dette vil bandlyse både vedkommendes forum-brugernavn og IP-adresse. @@ -172,22 +180,22 @@ This would ban both their forum username and their IP address. Room Window - + Rumvindue Room Description - + Rumbeskrivelse Moderation... - + Moderation... Leave Room - + Forlad Rum @@ -200,12 +208,12 @@ This would ban both their forum username and their IP address. Disconnected - + Frakoblet - %1 (%2/%3 members) - connected - + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 medlemmer) - forbundet @@ -218,6 +226,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Rapportér Spilkompatibilitet @@ -227,92 +240,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Skulle du vælge, at indsende en test-sag til </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">yuzu-Kompatibilitetslisten</span></a><span style=" font-size:10pt;">, vil de følgende oplysninger blive indsamlede og vist på siden:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> Information om Maskinel (CPU / GPU / Operativsystem)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hvilket version af yuzu du kører med</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Den forbundne yuzu-konto</li></ul></body></html> - - Perfect - Perfekt + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Spil fungerer fejlfrit, helt uden lyd- eller grafikfejl.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Fedt + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Spil fungerer, med mindre lyd- eller grafikfejl, og kan spilles fra start til slut. Kan kræve nogen omgåelse.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - OK + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Spil fungerer med større grafik- eller lydfejl, men spil kan spilles fra start til slut, med omgåelse. </p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Dårlig + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Spil-funktioner, men med større grafik- eller lydfejl. Ude af stand til, at komme videre i visse områder, på grund af fejl, selv med omgåelser.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Intro/Menu + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Spil er fuldstændig umuligt at spille, på grund af større grafik- eller lydfejl. Ude af stand til, at komme forbi Startskærmen.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Starter Ikke Op + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Spillet bryder sammen, ved forsøg på at starte.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Hvor godt kører dette spil, uafhængigt af hastighed eller ydeevne, fra start til slut, på denne version af yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Tak for din indsendelse! - + Submitting Sender - + Communication error Kommunikationsfejl - + An error occurred while sending the Testcase Der skete en fejl under indsendelse af Test-sagen - + Next Næste @@ -328,17 +376,17 @@ This would ban both their forum username and their IP address. Output Engine: - Outputmotor: + Udgangsmotor: Output Device - + Udgangsenhed Input Device - Inputenhed + Indgangsenhed @@ -372,37 +420,37 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + Konfigurér Infrarødt Kamera Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + Vælg hvor det emulerede kameras billede skal komme fra. Det kan være et virtuelt kamera eller et virkeligt kamera. Camera Image Source: - + Kamera-Billedkilde: Input device: - + Indgangsenhed: Preview - + Forhåndsvisning Resolution: 320*240 - + Opløsning: 320*240 Click to preview - + Klik for forhåndsvisning @@ -410,7 +458,7 @@ This would ban both their forum username and their IP address. Gendan Standarder - + Auto Automatisk @@ -455,7 +503,7 @@ This would ban both their forum username and their IP address. Paranoid (disables most optimizations) - + Paranoid (deaktiverer de fleste optimeringer) @@ -547,12 +595,14 @@ This would ban both their forum username and their IP address. <div>This option improves speed by relying only on the semantics of cmpxchg to ensure safety of exclusive access instructions. Please note this may result in deadlocks and other race conditions.</div> - + + <div>Denne valgmulighed forbedrer hastigheden, ved kun at benytte sig cmpxchg's semantik, for at sikre eksklusiv-adgangsinstruktioners sikkerhed. Bemærk venligst at dette kan resultere i fastfrysning og andre sjældne forhold.</div> + Ignore global monitor - + Ignorér global overvågning @@ -716,7 +766,7 @@ This would ban both their forum username and their IP address. Enable Host MMU Emulation (general memory instructions) - + Aktivér Værts MMU-Emulering (generelle hukommelsesinstruktioner) @@ -725,12 +775,16 @@ This would ban both their forum username and their IP address. <div style="white-space: nowrap">Enabling it causes guest exclusive memory reads/writes to be done directly into memory and make use of Host's MMU.</div> <div style="white-space: nowrap">Disabling this forces all exclusive memory accesses to use Software MMU Emulation.</div> - + + <div style="white-space: nowrap">Denne optimering øger hastigheden på eksklusive hukommelsestilgange, af gæsteprogrammet.</div> + <div style="white-space: nowrap">Aktivering af den forårsage, at gæstens eksklusive hukommelsestilgange foretages direkte i hukommelsen og benytter sig af Værtens MMU.</div> + <div style="white-space: nowrap">Deaktivering af dette tvinger alle eksklusive hukommelsestilgange til at benytte sig af programvarens MMU-emulering.</div> + Enable Host MMU Emulation (exclusive memory instructions) - + Aktivér Værts MMU-Emulering (eksklusive hukommelsesinstruktioner) @@ -738,12 +792,15 @@ This would ban both their forum username and their IP address. <div style="white-space: nowrap">This optimization speeds up exclusive memory accesses by the guest program.</div> <div style="white-space: nowrap">Enabling it reduces the overhead of fastmem failure of exclusive memory accesses.</div> - + + <div style="white-space: nowrap">Denne optimering øger hastigheden på eksklusive hukommelsestilgange af gæsteprogrammet.</div> + <div style="white-space: nowrap">Aktivering af den reducerer overarbejdet ved eksklusive hukommelsestilganges fastmem-fejl.</div> + Enable recompilation of exclusive memory instructions - + Aktivér rekompilering af eksklusive hukommelsesinstruktioner @@ -754,200 +811,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Fejlretning - + Enable GDB Stub - Aktiver GDB Stub + Aktivér GDB Stub - + Port: Port: - + Logging Logføring - + Global Log Filter Globalt Logfilter - + Show Log in Console Vis Log i Konsol - + Open Log Location Åbn Logplacering - + When checked, the max size of the log increases from 100 MB to 1 GB Når valgt, øges loggens maksimale størrelse fra 100 MB til 1 GB - + Enable Extended Logging** Aktivér Udvidet Logning** - + Homebrew Hjemmebrændt - + Arguments String Argumentsstreng - + Graphics Grafik - + When checked, the graphics API enters a slower debugging mode Når valgt, påbegynder grafik-APIen en langsommere fejlfindingstilstand - + Enable Graphics Debugging Aktivér Grafik-Fejlfinding - + When checked, it enables Nsight Aftermath crash dumps Når valgt, aktiverer det Nsight Aftermath nedbruds-nedfældelser - + Enable Nsight Aftermath Aktivér Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Når valgt, vil den dumpe alle de originale samler-shadere fra diskens shader-lager eller game, som fundet - + Dump Game Shaders - + Dump Spil-Shadere - + When checked, it will dump all the macro programs of the GPU - + Når valgt, vil den dumpe alle GPUens makroprogrammer - + Dump Maxwell Macros - + Dump Maxwell-Makroer - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - Når valgt, deaktiverer det makro-Just-In-Time-kompilatoren. Aktivering heraf får spil til, at køre langsommere + Når valgt, deaktiverer den makro-Just-In-Time-kompileringen. Aktivering heraf får spil til at køre langsommere - + Disable Macro JIT Deaktivér Makro-JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Når valgt, vil yuzu logføre statistikker om det kompilerede rørlinje-mellemlager - + Enable Shader Feedback Aktivér Shader-Tilbagemelding - + When checked, it executes shaders without loop logic changes Når valgt, eksekverer den shadere, uden loop-logik-forandringer - + Disable Loop safety checks Deaktivér Loop-sikkerhedskontrol - + Debugging Fejlfinding - - Enable FS Access Log - Aktivér FS-Tilgangslog - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Aktivér Vitterlig Rapporteringstjeneste - + + Enable FS Access Log + Aktivér FS-Tilgangslog + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + Aktivér dette, for at udgyde den senest genererede lyd-kommandoliste til konsollen. Påvirker kun spil, som gør brug af lyd-renderingen. + + + + Dump Audio Commands To Console** + Dump Lydkommandoer Til Konsol** + + + + Create Minidump After Crash + Opret Minidump Efter Nedbrud + + + Advanced Avanceret - + Kiosk (Quest) Mode Kiosk (Rejse)-Tilstand - + Enable CPU Debugging Aktivér CPU-Fejlfinding - + Enable Debug Asserts Aktivér Fejlfindingshævdelser - + Enable Auto-Stub** Aktivér Automatisk Stub** - + Enable All Controller Types - + Aktivér Alle Kontrolenhedstyper - + Disable Web Applet Deaktivér Net-Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + Gør Yuzu i stand til at kontrollere for et funktionelt Vulkan-miljø, når programmet starter op. Deaktivering af dette forårsager problemer med at eksterne programmer ser Yuzu. + + + + Perform Startup Vulkan Check + Udfør Vulkan-Kontrol Under Opstart + + + **This will be reset automatically when yuzu closes. **Dette vil automatisk blive nulstillet, når yuzu lukkes. + + + Restart Required + Genstart Kræves + + + + yuzu is required to restart in order to apply this setting. + Yuzu kræver en genstart, for at anvende denne indstilling. + + + + Web applet not compiled + Net-applet ikke kompileret + + + + MiniDump creation not compiled + MiniDump oprettelse ikke kompileret + ConfigureDebugController @@ -1241,7 +1333,7 @@ This would ban both their forum username and their IP address. Extended memory layout (6GB DRAM) - + Udvidet hukommelsesopsætning (6GB DRAM) @@ -1261,7 +1353,7 @@ This would ban both their forum username and their IP address. Mute audio when in background - + Gør lydløs, når i baggrunden @@ -1317,193 +1409,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings Grafikindstillinger - + Use disk pipeline cache Brug disk-rørlinje-mellemlager - + Use asynchronous GPU emulation Brug asynkron GPU-emulering - + Accelerate ASTC texture decoding Accelerér ASTC-tekstur afkodning - + NVDEC emulation: NVDEC-emulering: - + No Video Output Ingen Video-Output - + CPU Video Decoding CPU-Video Afkodning - + GPU Video Decoding (Default) GPU-Video Afkodning (Standard) - + Fullscreen Mode: Fuldskærmstilstand: - + Borderless Windowed Uindrammet Vindue - + Exclusive Fullscreen Eksklusiv Fuld Skærm - + Aspect Ratio: Skærmformat: - + Default (16:9) Standard (16:9) - + Force 4:3 Tving 4:3 - + Force 21:9 Tving 21:9 - + + Force 16:10 + + + + Stretch to Window Stræk til Vindue - + Resolution: - + Opløsning: - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0,5X (360p/540p) [EKSPERIMENTEL] - + 0.75X (540p/810p) [EXPERIMENTAL] - + 0,75X (540p/810p) [EKSPERIMENTEL] - + 1X (720p/1080p) - + 1X (720p/1080p) - + 2X (1440p/2160p) - + 2X (1440p/2160p) - + 3X (2160p/3240p) - + 3X (2160p/3240p) - + 4X (2880p/4320p) - + 4X (2880p/4320p) - + 5X (3600p/5400p) - + 5X (3600p/5400p) - + 6X (4320p/6480p) - + 6X (4320p/6480p) - + Window Adapting Filter: - + Vinduestilpassende Filter: - + Nearest Neighbor - + Nærmeste Nabo - + Bilinear - + Bilineær - + Bicubic - + Bikubisk - + Gaussian - + Gausisk - + ScaleForce - + ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) - + AMD FidelityFX™️ Superopløsning (Kun Vulkan) - + Anti-Aliasing Method: - + Anti-Aliaseringsmetode: - + None Ingen - + FXAA + FXAA + + + + Use global FSR Sharpness - - + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Brug global baggrundsfarve - + Set background color: Angiv baggrundsfarve: - + Background Color: Baggrundsfarve: @@ -1512,6 +1629,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM (Assembly-Shadere, kun NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1543,7 +1666,7 @@ This would ban both their forum username and their IP address. Use VSync - + Brug VSync @@ -1566,37 +1689,47 @@ This would ban both their forum username and their IP address. Brug Hurtig GPU-Tid (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotropisk Filtrering: - + Automatic - + Default Standard - + 2x - + 4x - + 8x - + 16x @@ -2088,7 +2221,7 @@ This would ban both their forum username and their IP address. - + Left Stick Venstre Styrepind @@ -2182,14 +2315,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2208,7 +2341,7 @@ This would ban both their forum username and their IP address. - + Plus Plus @@ -2221,15 +2354,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2286,231 +2419,236 @@ This would ban both their forum username and their IP address. - + Right Stick Højre Styrepind - - - - + + + + Clear Ryd - - - - - + + + + + [not set] [ikke indstillet] - - - Toggle button - Funktionsskifteknap - - - - + + Invert button - - + + + Toggle button + Funktionsskifteknap + + + + Invert axis Omvend akser - - - + + + Set threshold Angiv tærskel - - + + Choose a value between 0% and 100% Vælg en værdi imellem 0% og 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Tilsted Analog Pind - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Bevæg, efter tryk på OK, først din styrepind vandret og så lodret. Bevæg, for at omvende akserne, først din styrepind lodret og så vandret. - + Center axis - - + + Deadzone: %1% - Dødzone: 1% + Dødzone: %1% - - + + Modifier Range: %1% Forandringsrækkevidde: %1% - - + + Pro Controller Pro-Styringsenhed - + Dual Joycons Dobbelt-Joycon - + Left Joycon Venstre Joycon - + Right Joycon Højre Joycon - + Handheld Håndholdt - + GameCube Controller GameCube-Styringsenhed - + Poke Ball Plus - + NES Controller - + SNES Controller - + N64 Controller - + Sega Genesis - + Start / Pause Start / Pause - + Z Z - + Control Stick Styrepind - + C-Stick C-Pind - + Shake! Ryst! - + [waiting] [venter] - + New Profile Ny Profil - + Enter a profile name: Indtast et profilnavn: - - + + Create Input Profile Opret Input-Profil - + The given profile name is not valid! Det angivne profilnavn er ikke gyldigt! - + Failed to create the input profile "%1" Oprettelse af input-profil "%1" mislykkedes - + Delete Input Profile Slet Input-Profil - + Failed to delete the input profile "%1" Sletning af input-profil "%1" mislykkedes - + Load Input Profile Indlæs Input-Profil - + Failed to load the input profile "%1" Indlæsning af input-profil "%1" mislykkedes - + Save Input Profile Gem Input-Profil - + Failed to save the input profile "%1" Lagring af input-profil "%1" mislykkedes @@ -2765,42 +2903,42 @@ Bevæg, for at omvende akserne, først din styrepind lodret og så vandret.Udvikler - + Add-Ons Tilføjelser - + General Generelt - + System System - + CPU CPU - + Graphics Grafik - + Adv. Graphics - + Audio Lyd - + Properties Egenskaber @@ -2856,37 +2994,37 @@ Bevæg, for at omvende akserne, først din styrepind lodret og så vandret.Nuværende Bruger - + Username Brugernavn - + Set Image Angiv Billede - + Add Tilføj - + Rename Omdøb - + Remove Fjern - + Profile management is available only when game is not running. Profilhåndtering er kun tilgængelig, når spil ikke kører. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2894,96 +3032,105 @@ Bevæg, for at omvende akserne, først din styrepind lodret og så vandret. - + Enter Username Indtast Brugernavn - + Users Brugere - + Enter a username for the new user: Indtast et brugernavn for den nye bruger: - + Enter a new username: Indtast et nyt brugernavn: - - Confirm Delete - Bekræft Slet - - - - You are about to delete user with name "%1". Are you sure? - Du er ved at slette brugeren, med navnet "%1". Er du sikker? - - - + Select User Image Vælg Brugerbillede - + JPEG Images (*.jpg *.jpeg) JPEG-Billeder (*.jpg *.jpeg) - + Error deleting image Fejl ved sletning af billede - + Error occurred attempting to overwrite previous image at: %1. Der skete en fejl, ved forsøg på at overskrive forrige billede på: %1. - + Error deleting file Fejl ved sletning af fil - + Unable to delete existing file: %1. Kan ikke slette eksisterende fil: %1. - + Error creating user image directory Fejl ved oprettelse af brugerbillede-mappe - + Unable to create directory %1 for storing user images. Ude af stand til, at oprette mappe %1, til lagring af brugerbilleder. - + Error copying user image Fejl ved kopiering af brugerbillede - + Unable to copy image from %1 to %2 Ude af stand til, at kopiere billede fra %1 til %2 - + Error resizing user image - + Unable to resize image + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Bekræft Slet + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3042,7 +3189,7 @@ Bevæg, for at omvende akserne, først din styrepind lodret og så vandret. Deadzone: %1% - Dødzone: 1% + Dødzone: %1% @@ -3512,47 +3659,47 @@ Bevæg, for at omvende akserne, først din styrepind lodret og så vandret.<html><head/><body><p>Læser styringsenheds input fra skrift, i samme format som TAS-nx skrifter.<br/>Konsultér venligst <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">hjælp-siden</span></a>, for en mere detaljeret forklaring, på yuzu-hjemmesiden.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Referér venligst til Genvajstast-indstillingerne (Konfigurér -> Generelt -> Genvejstaster), for at kontrollere hvilke genvejstaster, der kontrollerer afspilning/optagelse. - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ADVARSEL: Dette er en eksperimentiel funktion.<br/>Det vil ikke afspille skrifter perfekt til billedet, med den aktuelle, uperfekte synkroniseringsmetode. - + Settings Indstillinger - + Enable TAS features Aktivér TAS-funktioner - + Loop script Loop skrift - + Pause execution during loads Sæt eksekvering på pause under indlæsninger - + Script Directory Skriftmappe - + Path Sti - + ... ... @@ -3804,56 +3951,71 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r + Show Compatibility List + + + + Show Add-Ons Column Vis Tilføjelser-Kolonne - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: Spil-Ikonstørrelse: - + Folder Icon Size: Mappe-Ikonstørrelse: - + Row 1 Text: Række 1-Tekst: - + Row 2 Text: Række 2-Tekst: - + Screenshots Skærmbilleder - + Ask Where To Save Screenshots (Windows Only) Spørg Hvor Skærmbilleder Skal Gemmes (Kun Windows) - + Screenshots Path: Skærmbilledsti: - + ... ... - + Select Screenshots Path... Vælg Skærmbilledsti... - + <System> <System> @@ -3962,7 +4124,7 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r - + Verify Bekræft @@ -4049,7 +4211,7 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r - + Unspecified Uspecificeret @@ -4064,17 +4226,36 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r Token blev ikke bekræftet. Ændringen af dit token er ikke blevet gemt. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Bekræfter... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Bekræftelse mislykkedes + + + Verification failed Bekræftelse mislykkedes - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Bekræftelse mislykkedes. Kontrollér at du har indtastet dit token korrekt, og at din internetforbindelse virker. @@ -4143,12 +4324,12 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r DirectConnectWindow - + Connecting - + Connect @@ -4156,907 +4337,909 @@ Træk punkter, for at skifte position, eller dobbeltklik i tabelceller, for at r GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonym data indsamles</a>, for at hjælp med, at forbedre yuzu. <br/><br/>Kunne du tænke dig, at dele dine brugsdata med os? - + Telemetry Telemetri - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Indlæser Net-Applet... - - + + Disable Web Applet Deaktivér Net-Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Aktuel emuleringshastighed. Værdier højere eller lavere end 100% indikerer, at emulering kører hurtigere eller langsommere end en Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + VULKAN - + OPENGL - + &Clear Recent Files - + &Continue - + &Pause - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Advarsel, Forældet Spilformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. - - + + Error while loading ROM! Fejl under indlæsning af ROM! - + The ROM format is not supported. ROM-formatet understøttes ikke. - + An error occurred initializing the video core. Der skete en fejl under initialisering af video-kerne. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Save Data - + Mod Data - + Error Opening %1 Folder Fejl ved Åbning af %1 Mappe - - + + Folder does not exist! Mappe eksisterer ikke! - + Error Opening Transferable Shader Cache - + Failed to create the shader cache directory for this title. - - Contents - - - - - Update - - - - - DLC - - - - - Remove Entry - - - - - Remove Installed Game %1? - - - - - - - - - Successfully Removed + Error Removing Contents - - Successfully removed the installed base game. - - - - - - - Error Removing %1 - - - - - The base game is not installed in the NAND and cannot be removed. - - - - - Successfully removed the installed update. + + Error Removing Update + Error Removing DLC + + + + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + + Remove Entry + + + + + + + + + + Successfully Removed + + + + + Successfully removed the installed base game. + + + + + The base game is not installed in the NAND and cannot be removed. + + + + + Successfully removed the installed update. + + + + There is no update installed for this title. - + There are no DLC installed for this title. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File - - + + Error Removing Transferable Shader Cache - - + + A shader cache for this title does not exist. - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! RomFS-Udpakning Mislykkedes! - + There was an error copying the RomFS files or the user cancelled the operation. Der skete en fejl ved kopiering af RomFS-filerne, eller brugeren afbrød opgaven. - + Full Fuld - + Skeleton Skelet - + Select RomFS Dump Mode Vælg RomFS-Nedfældelsestilstand - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Udpakker RomFS... - - + + Cancel Afbryd - + RomFS Extraction Succeeded! RomFS-Udpakning Lykkedes! - + The operation completed successfully. Fuldførelse af opgaven lykkedes. - + Error Opening %1 Fejl ved Åbning af %1 - + Select Directory Vælg Mappe - + Properties Egenskaber - + The game properties could not be loaded. Spil-egenskaberne kunne ikke indlæses. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch-Eksekverbar (%1);;Alle filer (*.*) - + Load File Indlæs Fil - + Open Extracted ROM Directory Åbn Udpakket ROM-Mappe - + Invalid Directory Selected Ugyldig Mappe Valgt - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... Installér fil "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systemapplikation - + System Archive Systemarkiv - + System Application Update Systemapplikationsopdatering - + Firmware Package (Type A) Firmwarepakke (Type A) - + Firmware Package (Type B) Firmwarepakke (Type B) - + Game Spil - + Game Update Spilopdatering - + Game DLC Spiludvidelse - + Delta Title Delta-Titel - + Select NCA Install Type... Vælg NCA-Installationstype... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install Installation mislykkedes - + The title type you selected for the NCA is invalid. - + File not found Fil ikke fundet - + File "%1" not found Fil "%1" ikke fundet - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Manglende yuzu-Konto - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - - Error - - - - - - The current game is not looking for amiibos - - - - - + + Amiibo - - + + The current amiibo has been removed - + + Error + + + + + + The current game is not looking for amiibos + + + + Amiibo File (%1);; All Files (*.*) Amiibo-Fil (%1);; Alle Filer (*.*) - + Load Amiibo Indlæs Amiibo - - Error opening Amiibo data file - Fejl ved åbning af Amiibo-datafil - - - - Unable to open Amiibo file "%1" for reading. - Ude af stand til, at åbne Amiibo-fil "%1" til indlæsning. - - - - Error reading Amiibo data file - Fejl ved indlæsning af Amiibo-datafil - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - - - - + Error loading Amiibo data Fejl ved indlæsning af Amiibo-data - - Unable to load Amiibo data. - Ude af stand til, at indlæse Amiibo-data. - - - - Capture Screenshot - Optag Skærmbillede - - - - PNG Image (*.png) - PNG-Billede (*.png) - - - - TAS state: Running %1/%2 + + The selected file is not a valid amiibo - - TAS state: Recording %1 + + The selected file is already on use - - TAS state: Idle %1/%2 + + An unknown error occurred + Capture Screenshot + Optag Skærmbillede + + + + PNG Image (*.png) + PNG-Billede (*.png) + + + + TAS state: Running %1/%2 + + + + + TAS state: Recording %1 + + + + + TAS state: Idle %1/%2 + + + + TAS State: Invalid - + &Stop Running - + &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Hastighed: %1% / %2% - + Speed: %1% Hastighed: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Spil: %1 FPS - + Frame: %1 ms Billede: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + yuzu was unable to locate a Switch system archive. %1 yuzu var ude af stand til, at lokalisere et Switch-systemarkiv. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu var ude af stand til, at lokalisere et Switch-systemarkiv. %1. %2 - + System Archive Not Found Systemarkiv Ikke Fundet - + System Archive Missing Systemarkiv Mangler - + yuzu was unable to locate the Switch shared fonts. %1 yuzu var ude af stand til, at finde delte Switch-skrifttyper. %1 - + Shared Fonts Not Found Delte Skrifttyper Ikke Fundet - + Shared Font Missing Delte Skrifttyper Mangler - + Fatal Error Fatal Fejl - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + Fatal Error encountered Stødte på Fatal Fejl - + Confirm Key Rederivation - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5067,76 +5250,76 @@ This will delete your autogenerated key files and re-run the key derivation modu - + Missing fuses - + - Missing BOOT0 - + - Missing BCPKG2-1-Normal-Main - + - Missing PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. - + Deriving Keys - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close yuzu? Er du sikker på, at du vil lukke yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Er du sikker på, at du vil stoppe emulereingen? Enhver ulagret data, vil gå tabt. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5146,38 +5329,38 @@ Would you like to bypass this and exit anyway? GRenderWindow - + OpenGL not available! - + yuzu has not been compiled with OpenGL support. - - + + Error while initializing OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. - + Error while initializing OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 @@ -5185,153 +5368,153 @@ Would you like to bypass this and exit anyway? GameList - + Favorite - + Start Game - + Start Game without Custom Configuration - + Open Save Data Location Åbn Gemt Data-Placering - + Open Mod Data Location Åbn Mod-Data-Placering - + Open Transferable Pipeline Cache - + Remove Fjern - + Remove Installed Update - + Remove All Installed DLC - + Remove Custom Configuration - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents - + Dump RomFS - + Dump RomFS to SDMC - + Copy Title ID to Clipboard Kopiér Titel-ID til Udklipsholder - + Navigate to GameDB entry - + Properties Egenskaber - + Scan Subfolders - + Remove Game Directory - + ▲ Move Up - + ▼ Move Down - + Open Directory Location - + Clear Ryd - + Name Navn - + Compatibility Kompatibilitet - + Add-ons Tilføjelser - + File type Filtype - + Size Størrelse @@ -5340,76 +5523,61 @@ Would you like to bypass this and exit anyway? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Perfekt - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - - - - - Great - Fedt - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - - - Okay - OK - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. + Game can be played without issues. - Bad - Dårlig - - - - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. + Playable - + + Game functions with minor graphical or audio glitches and is playable from start to finish. + + + + Intro/Menu Intro/Menu - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. + + Game loads, but is unable to progress past the Start Screen. - + Won't Boot Starter Ikke Op - + The game crashes when attempting to startup. - + Not Tested Ikke Afprøvet - + The game has not yet been tested. Spillet er endnu ikke blevet afprøvet. @@ -5417,7 +5585,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list @@ -5430,12 +5598,12 @@ Screen. - + Filter: Filter: - + Enter pattern to filter @@ -5485,7 +5653,7 @@ Screen. Room Description - + Rumbeskrivelse @@ -5511,12 +5679,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5525,11 +5693,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5551,112 +5720,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Optag Skærmbillede - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Fuldskærm - + Load File Indlæs Fil - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5676,7 +5844,7 @@ Debug Message: Install - + Installér @@ -5770,42 +5938,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Spillere - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5828,232 +5996,237 @@ Debug Message: - + &Emulation &Emulering - + &View - + &Reset Window Size - + &Debugging - + Reset Window Size to &720p - + Reset Window Size to 720p - + Reset Window Size to &900p - + Reset Window Size to 900p - + Reset Window Size to &1080p - + Reset Window Size to 1080p - + + &Multiplayer + + + + &Tools - + &TAS - + &Help &Hjælp - + &Install Files to NAND... - + L&oad File... - + Load &Folder... - + E&xit - + &Pause - + &Stop - + &Reinitialize keys... - + &About yuzu - + Single &Window Mode - + Con&figure... - + Display D&ock Widget Headers - + Show &Filter Bar - + Show &Status Bar - + Show Status Bar Vis Statuslinje - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room + &Browse Public Game Lobby - - Direct Connect to Room + + &Create Room - - Show Current Room + + &Leave Room - F&ullscreen + &Direct Connect to Room - &Restart + &Show Current Room - Load/Remove &Amiibo... + F&ullscreen - &Report Compatibility + &Restart + Load/Remove &Amiibo... + + + + + &Report Compatibility + + + + Open &Mods Page - + Open &Quickstart Guide - + &FAQ - + Open &yuzu Folder - + &Capture Screenshot - + &Configure TAS... - + Configure C&urrent Game... - + &Start - + &Reset - + R&ecord @@ -6118,46 +6291,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Tilsluttet - - - - Not Connected - + + Connected + Tilsluttet + + + + New Messages Received + + + + Error - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6258,22 +6426,39 @@ They may have left the room. - - Leave Room + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. - + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + + Leave Room + Forlad Rum + + + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6281,7 +6466,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6326,42 +6511,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 is playing %2 - + Not playing a game - + Installed SD Titles Installerede SD-Titler - + Installed NAND Titles Installerede NAND-Titler - + System Titles Systemtitler - + Add New Game Directory Tilføj Ny Spilmappe - + Favorites @@ -6391,7 +6576,7 @@ p, li { white-space: pre-wrap; } - + [not set] [ikke indstillet] @@ -6406,10 +6591,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Akse %1%2 @@ -6423,9 +6608,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [ukendt] @@ -6590,15 +6775,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6606,35 +6791,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [ubrugt] @@ -6675,11 +6860,124 @@ p, li { white-space: pre-wrap; } - + %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Navn + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6783,6 +7081,7 @@ p, li { white-space: pre-wrap; } + Handheld Håndholdt @@ -6822,11 +7121,6 @@ p, li { white-space: pre-wrap; } Docked Dokket - - - Undocked - Udokket - Vibration diff --git a/dist/languages/de.ts b/dist/languages/de.ts index bb35ff2..78da9af 100644 --- a/dist/languages/de.ts +++ b/dist/languages/de.ts @@ -87,86 +87,86 @@ p, li { white-space: pre-wrap; } Send Chat Message - + Chatnachricht senden Send Message - + Nachricht senden - + Members - + Mitglieder - + %1 has joined %1 ist beigetreten - + %1 has left %1 ist gegangen - + %1 has been kicked %1 wurde gekickt - + %1 has been banned %1 wurde gebannt - + %1 has been unbanned %1 wurde entbannt - + View Profile Profil ansehen - - + + Block Player Spieler blockieren - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick Kicken - + Ban Bannen - + Kick Player Spieler kicken - + Are you sure you would like to <b>kick</b> %1? - + Ban Player Spieler bannen - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -206,11 +206,11 @@ This would ban both their forum username and their IP address. Disconnected - + Verbindung getrennt - %1 (%2/%3 members) - connected + %1 - %2 (%3/%4 members) - connected @@ -224,6 +224,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Kompatibilität des Spiels melden @@ -233,92 +238,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Solltest du einen Bericht zur </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">yuzu-Kompatibilitätsliste</span></a><span style=" font-size:10pt;"> beitragen wollen, werden die folgenden Informationen gesammelt und auf der yuzu-Webseite dargestellt:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware-Informationen (CPU / GPU / Betriebssystem)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Welche yuzu-Version du benutzt</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Den verbundenen yuzu-Account</li></ul></body></html> - - Perfect - Perfekt + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Das Spiel funktioniert einwandfrei und ohne Audio- oder Grafikfehler.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Gut + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Das Spiel funktioniert mit kleineren Grafik- oder Audiofehlern und ist von Anfang bis Ende spielbar. Eventuell sind einige Workarounds erforderlich.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Okay + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Das Spiel funktioniert mit größeren Grafik- oder Audiofehlern, lässt sich aber mit Workarounds bis zum Ende durchspielen. </p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Schlecht + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Spiel funktioniert zwar, aber nur mit starken Grafik- oder Audiofehlern. Manche Bereiche des Spiels können selbst mit Workarounds nicht abgeschlossen werden.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Intro/Menü + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Das Spiel ist wegen schwerwiegendsten Grafik- oder Audiofehlern unspielbar. Das Spiel lässt sich lediglich starten.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Startet nicht + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Das Spiel stürzt beim Versuch zu starten ab.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Unabhängig von Geschwindigkeit oder Performance, wie gut läuft das Spiel von Start bis Ende in dieser Version von yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Vielen Dank für deinen Beitrag! - + Submitting Absenden - + Communication error Kommunikationsfehler - + An error occurred while sending the Testcase Beim Senden des Berichtes ist ein Fehler aufgetreten. - + Next Weiter @@ -378,22 +418,22 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + Infrarotkamera konfigurieren Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + Wähle aus, woher das Bild der emulierten Kamera kommen soll. Es kann eine virtuelle oder echte Kamera sein. Camera Image Source: - + Kamera Bildquelle: Input device: - + Eingabegerät: @@ -416,7 +456,7 @@ This would ban both their forum username and their IP address. Standardwerte wiederherstellen - + Auto Auto @@ -461,7 +501,7 @@ This would ban both their forum username and their IP address. Paranoid (disables most optimizations) - + Paranoid (deaktiviert die meisten Optimierungen) @@ -751,198 +791,233 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger Debugger - + Enable GDB Stub GDB-Stub aktivieren - + Port: Port: - + Logging Logging - + Global Log Filter Globaler Log-Filter - + Show Log in Console Log in der Konsole zeigen - + Open Log Location Log-Verzeichnis öffnen - + When checked, the max size of the log increases from 100 MB to 1 GB Wenn diese Option aktiviert ist, erhöht sich die maximale Größe des Logs von 100 MB auf 1 GB - + Enable Extended Logging** - + Erweitertes Logging aktivieren** - + Homebrew Homebrew - + Arguments String String-Argumente - + Graphics Grafik - + When checked, the graphics API enters a slower debugging mode Wenn diese Option aktiviert ist, wechselt die Grafik-API in einen langsameren Debug-Modus - + Enable Graphics Debugging Grafik-Debugging aktivieren - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath Nsight Aftermath aktivieren - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + Maxwell-Macros dumpen - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Diese Option deaktiviert den Macro-JIT-Compiler. Dies wird die Geschwindigkeit verringern. - + Disable Macro JIT Macro-JIT deaktivieren - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + Shader-Feedback aktivieren - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Debugging - - Enable FS Access Log - FS-Zugriffslog aktivieren - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + FS-Zugriffslog aktivieren + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Erweitert - + Kiosk (Quest) Mode Kiosk(Quest)-Modus - + Enable CPU Debugging CPU Debugging aktivieren - + Enable Debug Asserts aktiviere Debug-Meldungen - + Enable Auto-Stub** Auto-Stub** aktivieren - + Enable All Controller Types - + Disable Web Applet Deaktiviere die Web Applikation - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. + **Dies wird automatisch beim Schließen von yuzu zurückgesetzt. + + + + Restart Required + Neustart erforderlich + + + + yuzu is required to restart in order to apply this setting. + yuzu muss neugestartet werden, damit diese Einstellungen übernommen werden können. + + + + Web applet not compiled + + + + + MiniDump creation not compiled @@ -1258,7 +1333,7 @@ This would ban both their forum username and their IP address. Mute audio when in background - + Audio im Hintergrund stummschalten @@ -1301,7 +1376,7 @@ This would ban both their forum username and their IP address. Shader Backend: - + Shader Backend: @@ -1314,193 +1389,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings Grafik-Einstellungen - + Use disk pipeline cache - + Disk-Pipeline-Cache verwenden - + Use asynchronous GPU emulation Asynchrone GPU-Emulation verwenden - + Accelerate ASTC texture decoding - + NVDEC emulation: NVDEC Emulation: - + No Video Output Keine Videoausgabe - + CPU Video Decoding CPU Video Dekodierung - + GPU Video Decoding (Default) GPU Video Dekodierung (Standard) - + Fullscreen Mode: Vollbild Modus: - + Borderless Windowed Rahmenloses Fenster - + Exclusive Fullscreen Exklusiver Vollbildmodus - + Aspect Ratio: Seitenverhältnis: - + Default (16:9) Standard (16:9) - + Force 4:3 4:3 erzwingen - + Force 21:9 21:9 erzwingen - + + Force 16:10 + Erzwinge 16:10 + + + Stretch to Window Auf Fenster anpassen - + Resolution: Auflösung: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EXPERIMENTELL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EXPERIMENTELL] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: - + Nearest Neighbor - + Bilinear Bilinear - + Bicubic Bikubisch - + Gaussian - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (nur Vulkan) - + Anti-Aliasing Method: Kantenglättungs-Methode: - + None Keiner - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Globale Hintergrundfarbe verwenden - + Set background color: Hintergrundfarbe: - + Background Color: Hintergrundfarbe: @@ -1509,6 +1609,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1540,7 +1646,7 @@ This would ban both their forum username and their IP address. Use VSync - + VSync verwenden @@ -1563,37 +1669,47 @@ This would ban both their forum username and their IP address. - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotrope Filterung: - + Automatic Automatisch - + Default Standard - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -1638,7 +1754,7 @@ This would ban both their forum username and their IP address. Controller Hotkey - + Controller-Hotkey @@ -1988,12 +2104,12 @@ This would ban both their forum username and their IP address. Ring Controller - + Ring-Controller Infrared Camera - + Infrarotkamera @@ -2023,7 +2139,7 @@ This would ban both their forum username and their IP address. Controller navigation - + Controller-Navigation @@ -2085,7 +2201,7 @@ This would ban both their forum username and their IP address. - + Left Stick Linker Analogstick @@ -2179,14 +2295,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2205,7 +2321,7 @@ This would ban both their forum username and their IP address. - + Plus Plus @@ -2218,15 +2334,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2283,231 +2399,236 @@ This would ban both their forum username and their IP address. - + Right Stick Rechter Analogstick - - - - + + + + Clear Löschen - - - - - + + + + + [not set] [nicht belegt] - - - Toggle button - Taste umschalten - - - - + + Invert button Knopf invertieren - - + + + Toggle button + Taste umschalten + + + + Invert axis Achsen umkehren - - - + + + Set threshold - - + + Choose a value between 0% and 100% Wert zwischen 0% und 100% wählen - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Analog-Stick festlegen - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Nach dem Drücken von OK den Joystick zuerst horizontal, dann vertikal bewegen. Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizontal. - + Center axis - + Achse zentrieren - - + + Deadzone: %1% Deadzone: %1% - - + + Modifier Range: %1% Modifikator-Radius: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Zwei Joycons - + Left Joycon Linker Joycon - + Right Joycon Rechter Joycon - + Handheld Handheld - + GameCube Controller GameCube-Controller - + Poke Ball Plus Poke-Ball Plus - + NES Controller NES Controller - + SNES Controller SNES Controller - + N64 Controller N64 Controller - + Sega Genesis Sega Genesis - + Start / Pause Start / Pause - + Z Z - + Control Stick Analog Stick - + C-Stick C-Stick - + Shake! Schütteln! - + [waiting] [wartet] - + New Profile Neues Profil - + Enter a profile name: Profilnamen eingeben: - - + + Create Input Profile Eingabeprofil erstellen - + The given profile name is not valid! Angegebener Profilname ist nicht gültig! - + Failed to create the input profile "%1" Erstellen des Eingabeprofils "%1" ist fehlgeschlagen - + Delete Input Profile Eingabeprofil löschen - + Failed to delete the input profile "%1" Löschen des Eingabeprofils "%1" ist fehlgeschlagen - + Load Input Profile Eingabeprofil laden - + Failed to load the input profile "%1" Laden des Eingabeprofils "%1" ist fehlgeschlagen - + Save Input Profile Eingabeprofil speichern - + Failed to save the input profile "%1" Speichern des Eingabeprofils "%1" ist fehlgeschlagen @@ -2762,42 +2883,42 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Entwickler - + Add-Ons Add-Ons - + General Allgemeines - + System System - + CPU CPU - + Graphics Grafik - + Adv. Graphics Erw. Grafik - + Audio Audio - + Properties Einstellungen @@ -2853,37 +2974,37 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Aktueller Nutzer - + Username Nutzername - + Set Image Bild wählen - + Add Hinzufügen - + Rename Umbenennen - + Remove Entfernen - + Profile management is available only when game is not running. Die Nutzerverwaltung ist nur verfügbar, wenn kein Spiel aktiv ist. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2891,102 +3012,111 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta %2 - + Enter Username Nutzername eingeben - + Users Nutzer - + Enter a username for the new user: Gib einen Benutzernamen für den neuen Benutzer ein: - + Enter a new username: Gib einen neuen Nutzernamen ein: - - Confirm Delete - Löschen bestätigen - - - - You are about to delete user with name "%1". Are you sure? - Du bist dabei, den Nutzer "%1" zu löschen. Bist du dir sicher? - - - + Select User Image Profilbild wählen - + JPEG Images (*.jpg *.jpeg) JPEG Bilddateien (*.jpg *.jpeg) - + Error deleting image Fehler beim Löschen des Bildes - + Error occurred attempting to overwrite previous image at: %1. Fehler beim Überschreiben des vorherigen Bildes bei: %1 - + Error deleting file Fehler beim Löschen der Datei - + Unable to delete existing file: %1. Konnte die bestehende Datei "%1" nicht löschen. - + Error creating user image directory Fehler beim Erstellen des Ordners für die Profilbilder - + Unable to create directory %1 for storing user images. Konnte Ordner "%1" nicht erstellen, um Profilbilder zu speichern. - + Error copying user image Fehler beim Kopieren des Profilbildes - + Unable to copy image from %1 to %2 Das Bild konnte nicht von "%1" nach "%2" kopiert werden - + Error resizing user image - + Unable to resize image + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Löschen bestätigen + + + + Name: %1 +UUID: %2 + + + ConfigureRingController Configure Ring Controller - + Ring-Controller konfigurieren @@ -3002,13 +3132,13 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta Pull - + Ziehen Push - + Drücken @@ -3509,47 +3639,47 @@ Um die Achsen umzukehren, bewege den Joystick zuerst vertikal und dann horizonta - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings Einstellungen - + Enable TAS features - + TAS-Funktionen aktivieren - + Loop script - + Script loopen - + Pause execution during loads - + Script Directory Skript-Verzeichnis - + Path Pfad - + ... ... @@ -3724,7 +3854,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Full Size (256x256) - + Volle Größe (256x256) @@ -3801,56 +3931,71 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf + Show Compatibility List + Kompatibilitätsliste anzeigen + + + Show Add-Ons Column Add-On Spalte anzeigen - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: - + Spiel-Icon Größe: - + Folder Icon Size: - + Ordner-Icon Größe: - + Row 1 Text: Zeile 1 Text: - + Row 2 Text: Zeile 2 Text: - + Screenshots Screenshots - + Ask Where To Save Screenshots (Windows Only) Frage nach, wo Screenshots gespeichert werden sollen (Nur Windows) - + Screenshots Path: Screenshotpfad - + ... ... - + Select Screenshots Path... Screenshotpfad auswählen... - + <System> <System> @@ -3865,7 +4010,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Press any controller button to vibrate the controller. - + Drücke einen Knopf um den Controller vibrieren zu lassen. @@ -3959,7 +4104,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf - + Verify Überprüfen @@ -4046,7 +4191,7 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf - + Unspecified Nicht spezifiziert @@ -4061,17 +4206,36 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf Token wurde nicht verfiziert. Die Änderungen an deinem Token wurden nicht gespeichert. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Verifizieren... - + + Verified + Tooltip + Verifiziert + + + + Verification failed + Tooltip + Verifizierung fehlgeschlagen + + + Verification failed Verifizierung fehlgeschlagen - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verifizierung fehlgeschlagen. Prüfe ob dein Nutzername und Token richtig eingegeben wurden und ob deine Internetverbindung korrekt funktioniert. @@ -4140,12 +4304,12 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf DirectConnectWindow - + Connecting - + Verbinde - + Connect Verbinden @@ -4153,487 +4317,490 @@ Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonyme Daten werden gesammelt,</a> um yuzu zu verbessern.<br/><br/>Möchstest du deine Nutzungsdaten mit uns teilen? - + Telemetry Telemetrie - + Broken Vulkan Installation Detected - + Defekte Vulkan-Installation erkannt - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Lade Web-Applet... - - + + Disable Web Applet Deaktiviere die Web Applikation - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Wie viele Shader im Moment kompiliert werden - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Derzeitige Emulations-Geschwindigkeit. Werte höher oder niedriger als 100% zeigen, dass die Emulation scheller oder langsamer läuft als auf einer Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Wie viele Bilder pro Sekunde angezeigt werden variiert von Spiel zu Spiel und von Szene zu Szene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Zeit, die gebraucht wurde, um einen Switch-Frame zu emulieren, ohne Framelimit oder V-Sync. Für eine Emulation bei voller Geschwindigkeit sollte dieser Wert bei höchstens 16.67ms liegen. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Zuletzt geladene Dateien leeren - + &Continue &Fortsetzen - + &Pause &Pause - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu betreibt ein Speil - + Warning Outdated Game Format Warnung veraltetes Spielformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Du nutzt eine entpackte ROM-Ordnerstruktur für dieses Spiel, welches ein veraltetes Format ist und von anderen Formaten wie NCA, NAX, XCI oder NSP überholt wurde. Entpackte ROM-Ordner unterstützen keine Icons, Metadaten oder Updates.<br><br><a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>Unser Wiki</a> enthält eine Erklärung der verschiedenen Formate, die yuzu unterstützt. Diese Nachricht wird nicht noch einmal angezeigt. - - + + Error while loading ROM! ROM konnte nicht geladen werden! - + The ROM format is not supported. ROM-Format wird nicht unterstützt. - + An error occurred initializing the video core. Beim Initialisieren des Video-Kerns ist ein Fehler aufgetreten. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. ROM konnte nicht geladen werden! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Bitte folge der <a href='https://yuzu-emu.org/help/quickstart/'>yuzu-Schnellstart-Anleitung</a> um deine Dateien zu extrahieren.<br>Hilfe findest du im yuzu-Wiki</a> oder dem yuzu-Discord</a>. - + An unknown error occurred. Please see the log for more details. Ein unbekannter Fehler ist aufgetreten. Bitte prüfe die Log-Dateien auf mögliche Fehlermeldungen. - + (64-bit) (64-Bit) - + (32-bit) (32-Bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Speicherdaten - + Mod Data Mod-Daten - + Error Opening %1 Folder Konnte Verzeichnis %1 nicht öffnen - - + + Folder does not exist! Verzeichnis existiert nicht! - + Error Opening Transferable Shader Cache Fehler beim Öffnen des transferierbaren Shader-Caches - + Failed to create the shader cache directory for this title. - - Contents - Inhalte + + Error Removing Contents + - - Update - Update + + Error Removing Update + - - DLC - DLC + + Error Removing DLC + - + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + Remove Entry Eintrag entfernen - - Remove Installed Game %1? - Installiertes Spiel %1 entfernen? - - - - - - - - + + + + + + Successfully Removed Erfolgreich entfernt - + Successfully removed the installed base game. Das Spiel wurde entfernt. - - - - Error Removing %1 - Fehler beim Entfernen von %1 - - - + The base game is not installed in the NAND and cannot be removed. Das Spiel ist nicht im NAND installiert und kann somit nicht entfernt werden. - + Successfully removed the installed update. Das Update wurde entfernt. - + There is no update installed for this title. Es ist kein Update für diesen Titel installiert. - + There are no DLC installed for this title. Es sind keine DLC für diesen Titel installiert. - + Successfully removed %1 installed DLC. %1 DLC entfernt. - + Delete OpenGL Transferable Shader Cache? Transferierbaren OpenGL Shader Cache löschen? - + Delete Vulkan Transferable Shader Cache? Transferierbaren Vulkan Shader Cache löschen? - + Delete All Transferable Shader Caches? Alle transferierbaren Shader Caches löschen? - + Remove Custom Game Configuration? Spiel-Einstellungen entfernen? - + Remove File Datei entfernen - - + + Error Removing Transferable Shader Cache Fehler beim Entfernen - - + + A shader cache for this title does not exist. Es existiert kein Shader-Cache für diesen Titel. - + Successfully removed the transferable shader cache. Der transferierbare Shader-Cache wurde entfernt. - + Failed to remove the transferable shader cache. Konnte den transferierbaren Shader-Cache nicht entfernen. - - + + Error Removing Transferable Shader Caches Fehler beim Entfernen der transferierbaren Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration Fehler beim Entfernen - + A custom configuration for this title does not exist. Es existieren keine Spiel-Einstellungen für dieses Spiel. - + Successfully removed the custom game configuration. Die Spiel-Einstellungen wurden entfernt. - + Failed to remove the custom game configuration. Die Spiel-Einstellungen konnten nicht entfernt werden. - - + + RomFS Extraction Failed! RomFS-Extraktion fehlgeschlagen! - + There was an error copying the RomFS files or the user cancelled the operation. Das RomFS konnte wegen eines Fehlers oder Abbruchs nicht kopiert werden. - + Full Komplett - + Skeleton Nur Ordnerstruktur - + Select RomFS Dump Mode RomFS Extraktions-Modus auswählen - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Bitte wähle, wie das RomFS gespeichert werden soll.<br>"Full" wird alle Dateien des Spiels extrahieren, während <br>"Skeleton" nur die Ordnerstruktur erstellt. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... RomFS wird extrahiert... - - + + Cancel Abbrechen - + RomFS Extraction Succeeded! RomFS wurde extrahiert! - + The operation completed successfully. Der Vorgang wurde erfolgreich abgeschlossen. - + Error Opening %1 Fehler beim Öffnen von %1 - + Select Directory Verzeichnis auswählen - + Properties Einstellungen - + The game properties could not be loaded. Spiel-Einstellungen konnten nicht geladen werden. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch-Programme (%1);;Alle Dateien (*.*) - + Load File Datei laden - + Open Extracted ROM Directory Öffne das extrahierte ROM-Verzeichnis - + Invalid Directory Selected Ungültiges Verzeichnis ausgewählt - + The directory you have selected does not contain a 'main' file. Das Verzeichnis, das du ausgewählt hast, enthält keine 'main'-Datei. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Installierbares Switch-Programm (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Dateien installieren - + %n file(s) remaining %n Datei verbleibend%n Dateien verbleibend - + Installing file "%1"... Datei "%1" wird installiert... - - + + Install Results NAND-Installation - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Um Konflikte zu vermeiden, raten wir Nutzern davon ab, Spiele im NAND zu installieren. Bitte nutze diese Funktion nur zum Installieren von Updates und DLC. - + %n file(s) were newly installed %n file was newly installed @@ -4641,423 +4808,422 @@ Bitte nutze diese Funktion nur zum Installieren von Updates und DLC. - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systemanwendung - + System Archive Systemarchiv - + System Application Update Systemanwendungsupdate - + Firmware Package (Type A) Firmware-Paket (Typ A) - + Firmware Package (Type B) Firmware-Paket (Typ B) - + Game Spiel - + Game Update Spiel-Update - + Game DLC Spiel-DLC - + Delta Title Delta-Titel - + Select NCA Install Type... Wähle den NCA-Installationstyp aus... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Bitte wähle, als was diese NCA installiert werden soll: (In den meisten Fällen sollte die Standardeinstellung 'Spiel' ausreichen.) - + Failed to Install Installation fehlgeschlagen - + The title type you selected for the NCA is invalid. Der Titel-Typ, den du für diese NCA ausgewählt hast, ist ungültig. - + File not found Datei nicht gefunden - + File "%1" not found Datei "%1" nicht gefunden - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Fehlender yuzu-Account - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Um einen Kompatibilitätsbericht abzuschicken, musst du einen yuzu-Account mit yuzu verbinden.<br><br/>Um einen yuzu-Account zu verbinden, prüfe die Einstellungen unter Emulation &gt; Konfiguration &gt; Web. - + Error opening URL Fehler beim Öffnen der URL - + Unable to open the URL "%1". URL "%1" kann nicht geöffnet werden. - + TAS Recording TAS Aufnahme - + Overwrite file of player 1? Datei von Spieler 1 überschreiben? - + Invalid config detected Ungültige Konfiguration erkannt - + Handheld controller can't be used on docked mode. Pro controller will be selected. Handheld-Controller können nicht im Dock verwendet werden. Der Pro-Controller wird verwendet. - - - Error - Fehler - - - - - The current game is not looking for amiibos - - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed - + Das aktuelle Amiibo wurde entfernt - + + Error + Fehler + + + + + The current game is not looking for amiibos + Das aktuelle Spiel sucht nicht nach Amiibos + + + Amiibo File (%1);; All Files (*.*) Amiibo-Datei (%1);; Alle Dateien (*.*) - + Load Amiibo Amiibo laden - - Error opening Amiibo data file - Fehler beim Öffnen der Amiibo Datei - - - - Unable to open Amiibo file "%1" for reading. - Die Amiibo Datei "%1" konnte nicht zum Lesen geöffnet werden. - - - - Error reading Amiibo data file - Fehler beim Lesen der Amiibo-Daten - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Amiibo-Daten können nicht vollständig gelesen werden. Es wurde erwartet, dass %1 Bytes gelesen werden, es konnten aber nur %2 Bytes gelesen werden. - - - + Error loading Amiibo data Fehler beim Laden der Amiibo-Daten - - Unable to load Amiibo data. - Amiibo-Daten konnten nicht geladen werden. + + The selected file is not a valid amiibo + Die ausgewählte Datei ist keine gültige Amiibo - + + The selected file is already on use + Die ausgewählte Datei wird bereits verwendet + + + + An unknown error occurred + Ein unbekannter Fehler ist aufgetreten + + + Capture Screenshot Screenshot aufnehmen - + PNG Image (*.png) PNG Bild (*.png) - + TAS state: Running %1/%2 TAS Zustand: Läuft %1/%2 - + TAS state: Recording %1 TAS Zustand: Aufnahme %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid TAS Zustand: Ungültig - + &Stop Running - + &Start &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Skalierung: %1x - + Speed: %1% / %2% Geschwindigkeit: %1% / %2% - + Speed: %1% Geschwindigkeit: %1% - + Game: %1 FPS (Unlocked) Spiel: %1 FPS (Unbegrenzt) - + Game: %1 FPS Spiel: %1 FPS - + Frame: %1 ms Frame: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU HOCH - + GPU EXTREME GPU EXTREM - + GPU ERROR GPU FEHLER - + DOCKED - + DOCKED - + HANDHELD - + HANDHELD - + NEAREST NÄCHSTER - - + + BILINEAR BILINEAR - + BICUBIC BIKUBISCH - + GAUSSIAN - + GAUSSIAN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Das Spiel, dass du versuchst zu spielen, benötigt bestimmte Dateien von deiner Switch-Konsole.<br/><br/>Um Informationen darüber zu erhalten, wie du diese Dateien von deiner Switch extrahieren kannst, prüfe bitte die folgenden Wiki-Seiten: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>System-Archive und Shared Fonts von einer Switch-Konsole extrahieren</a>.<br/><br/>Willst du zur Spiele-Liste zurückkehren und die Emulation beenden? Das Fortsetzen der Emulation könnte zu Spielfehlern, Abstürzen, beschädigten Speicherdaten und anderen Fehlern führen. - + yuzu was unable to locate a Switch system archive. %1 yuzu konnte ein Switch Systemarchiv nicht finden. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu konnte ein Switch Systemarchiv nicht finden: %1. %2 - + System Archive Not Found Systemarchiv nicht gefunden - + System Archive Missing Systemarchiv fehlt - + yuzu was unable to locate the Switch shared fonts. %1 yuzu konnte die Switch Shared Fonts nicht finden. %1 - + Shared Fonts Not Found Shared Fonts nicht gefunden - + Shared Font Missing Shared Font fehlt - + Fatal Error Schwerwiegender Fehler - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Ein schwerwiegender Fehler ist aufgetreten, bitte prüfe die Log-Dateien auf mögliche Fehlermeldungen. Weitere Informationen: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Wie kann ich eine Log-Datei hochladen</a>.<br/><br/>Willst du zur Spiele-Liste zurückkehren und die Emulation beenden? Das Fortsetzen der Emulation könnte zu Spielfehlern, Abstürzen, beschädigten Speicherdaten und anderen Fehlern führen. - + Fatal Error encountered Fataler Fehler aufgetreten - + Confirm Key Rederivation Schlüsselableitung bestätigen - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5070,37 +5236,37 @@ This will delete your autogenerated key files and re-run the key derivation modu Dieser Prozess wird die generierten Schlüsseldateien löschen und die Schlüsselableitung neu starten. - + Missing fuses Fuses fehlen - + - Missing BOOT0 - BOOT0 fehlt - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main fehlt - + - Missing PRODINFO - PRODINFO fehlt - + Derivation Components Missing Derivationskomponenten fehlen - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5108,39 +5274,39 @@ on your system's performance. Dies könnte, je nach Leistung deines Systems, bis zu einer Minute dauern. - + Deriving Keys Schlüsselableitung - + Select RomFS Dump Target RomFS wählen - + Please select which RomFS you would like to dump. Wähle, welches RomFS du speichern möchtest. - + Are you sure you want to close yuzu? Bist du sicher, dass du yuzu beenden willst? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Bist du sicher, dass du die Emulation stoppen willst? Jeder nicht gespeicherte Fortschritt geht verloren. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5152,38 +5318,38 @@ Möchtest du dies umgehen und sie trotzdem beenden? GRenderWindow - + OpenGL not available! OpenGL nicht verfügbar! - + yuzu has not been compiled with OpenGL support. yuzu wurde nicht mit OpenGL-Unterstützung kompiliert. - - + + Error while initializing OpenGL! Fehler beim Initialisieren von OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Deine Grafikkarte unterstützt kein OpenGL oder du hast nicht den neusten Treiber installiert. - + Error while initializing OpenGL 4.6! Fehler beim Initialisieren von OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Deine Grafikkarte unterstützt OpenGL 4.6 nicht, oder du benutzt nicht die neuste Treiberversion.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Deine Grafikkarte unterstützt anscheinend nicht eine oder mehrere von yuzu benötigten OpenGL-Erweiterungen. Bitte stelle sicher, dass du den neusten Grafiktreiber installiert hast.<br><br>GL Renderer:<br>%1<br><br>Nicht unterstützte Erweiterungen:<br>%2 @@ -5191,153 +5357,153 @@ Möchtest du dies umgehen und sie trotzdem beenden? GameList - + Favorite Favorit - + Start Game Spiel starten - + Start Game without Custom Configuration Spiel ohne benutzerdefinierte Spiel-Einstellungen starten - + Open Save Data Location Spielstand-Verzeichnis öffnen - + Open Mod Data Location Mod-Verzeichnis öffnen - + Open Transferable Pipeline Cache - + Remove Entfernen - + Remove Installed Update Installiertes Update entfernen - + Remove All Installed DLC Alle installierten DLCs entfernen - + Remove Custom Configuration Spiel-Einstellungen entfernen - - - Remove OpenGL Pipeline Cache - - - Remove Vulkan Pipeline Cache - + Remove OpenGL Pipeline Cache + OpenGL-Pipeline-Cache entfernen - - Remove All Pipeline Caches - + + Remove Vulkan Pipeline Cache + Vulkan-Pipeline-Cache entfernen + Remove All Pipeline Caches + Alle Pipeline-Caches entfernen + + + Remove All Installed Contents Alle installierten Inhalte entfernen - + Dump RomFS RomFS speichern - + Dump RomFS to SDMC - + Copy Title ID to Clipboard Title-ID in die Zwischenablage kopieren - + Navigate to GameDB entry GameDB-Eintrag öffnen - + Properties Eigenschaften - + Scan Subfolders Unterordner scannen - + Remove Game Directory Spieleverzeichnis entfernen - + ▲ Move Up ▲ Nach Oben - + ▼ Move Down ▼ Nach Unten - + Open Directory Location Verzeichnis öffnen - + Clear Löschen - + Name Name - + Compatibility Kompatibilität - + Add-ons Add-ons - + File type Dateityp - + Size Größe @@ -5346,78 +5512,61 @@ Möchtest du dies umgehen und sie trotzdem beenden? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Perfekt - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - Das Spiel funktioniert fehlerfrei, ohne Audio- oder Grafikfehler, und sämtliche getestete Funktionalität funktioniert wie vorhergesehen ohne Workarounds. - - - - Great - Gut - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - Das Spiel funktioniert mit kleineren Audio- oder Grafikfehlern und lässt sich bis zum Ende durchspielen. -Eventuell sind einige Workarounds notwendig. - - Okay - Okay - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - Das Spiel funktioniert mit größern Audio- oder Grafikfehlern, -lässt sich aber mit Workarounds bis zum Ende durchspielen. + Game can be played without issues. + - Bad - Schlecht + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Spiel funktioniert zwar, aber nur mit starken Audio- oder Grafikfehlern. Manche Bereiche des Spiels können selbst mit Workarounds nicht abgeschlossen werden. + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu Intro/Menü - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Das Spiel ist wegen schwerwiegenden Audio- oder Grafikfehlern unspielbar. Das Spiel lässt sich lediglich starten. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Startet nicht - + The game crashes when attempting to startup. Das Spiel stürzt beim Versuch zu starten ab. - + Not Tested Nicht getestet - + The game has not yet been tested. Spiel wurde noch nicht getestet. @@ -5425,7 +5574,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Doppelklicke, um einen neuen Ordner zur Spieleliste hinzuzufügen. @@ -5438,12 +5587,12 @@ Screen. %1 von %n Ergebnis%1 von %n Ergebnisse - + Filter: Filter: - + Enter pattern to filter Wörter zum Filtern eingeben @@ -5463,7 +5612,7 @@ Screen. Preferred Game - + Bevorzugtes Spiel @@ -5478,7 +5627,7 @@ Screen. (Leave blank for open game) - + (Leer lassen für offenes Spiel) @@ -5498,7 +5647,7 @@ Screen. Load Previous Ban List - + Vorherige Bann-Liste laden @@ -5519,12 +5668,12 @@ Screen. HostRoomWindow - + Error Fehler - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5533,11 +5682,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5559,112 +5709,111 @@ Debug Message: - Main Window Hauptfenster - + Audio Volume Down - + Lautstärke verringern + + + + Audio Volume Up + Lautstärke erhöhen - Audio Volume Up - - - - Capture Screenshot Screenshot aufnehmen - + Change Adapting Filter - + Adaptiven Filter ändern - + Change Docked Mode - + Change GPU Accuracy - + GPU-Genauigkeit ändern + + + + Continue/Pause Emulation + Emulation fortsetzen/pausieren - Continue/Pause Emulation - - - - Exit Fullscreen Vollbild verlassen - + Exit yuzu yuzu verlassen - + Fullscreen Vollbild - + Load File Datei laden - + Load/Remove Amiibo - + Amiibo laden/entfernen + + + + Restart Emulation + Emulation neustarten - Restart Emulation - + Stop Emulation + Emulation stoppen - Stop Emulation - + TAS Record + TAS aufnehmen - TAS Record - + TAS Reset + TAS neustarten - TAS Reset - + TAS Start/Stop + TAS starten/stoppen - TAS Start/Stop - - - - Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5744,7 +5893,7 @@ Debug Message: Public Room Browser - + Browser für öffentliche Räume @@ -5775,45 +5924,45 @@ Debug Message: Refresh Lobby - + Lobby aktualisieren - + Password Required to Join Passwort zum Joinen benötigt - + Password: Passwort: - - Room Name - Raumname - - - - Preferred Game - - - - - Host - Host - - - + Players Spieler - - Refreshing - + + Room Name + Raumname - + + Preferred Game + Bevorzugtes Spiel + + + + Host + Host + + + + Refreshing + Aktualisiere + + + Refresh List Liste aktualisieren @@ -5836,232 +5985,237 @@ Debug Message: &Zuletzt geladene Dateien - + &Emulation &Emulation - + &View &Anzeige - + &Reset Window Size &Fenstergöße zurücksetzen - + &Debugging &Debugging - + Reset Window Size to &720p Fenstergröße auf &720p zurücksetzen - + Reset Window Size to 720p Fenstergröße auf 720p zurücksetzen - + Reset Window Size to &900p Fenstergröße auf &900p zurücksetzen - + Reset Window Size to 900p Fenstergröße auf 900p zurücksetzen - + Reset Window Size to &1080p Fenstergröße auf &1080p zurücksetzen - + Reset Window Size to 1080p Fenstergröße auf 1080p zurücksetzen - + + &Multiplayer + &Mehrspieler + + + &Tools &Werkzeuge - + &TAS &TAS - + &Help &Hilfe - + &Install Files to NAND... &Dateien im NAND installieren... - + L&oad File... Datei &laden... - + Load &Folder... &Verzeichnis laden... - + E&xit S&chließen - + &Pause &Pause - + &Stop &Stop - + &Reinitialize keys... &Schlüssel neu initialisieren... - + &About yuzu &Über yuzu - + Single &Window Mode &Einzelfenster-Modus - + Con&figure... Kon&figurieren - + Display D&ock Widget Headers D&ock-Widget-Header anzeigen - + Show &Filter Bar &Filterleiste anzeigen - + Show &Status Bar &Statusleiste anzeigen - + Show Status Bar Statusleiste anzeigen - - - Browse Public Game Lobby - - - - - Create Room - Raum erstellen - - Leave Room - Raum verlassen + &Browse Public Game Lobby + &Öffentliche Spiele-Lobbys durchsuchen - - Direct Connect to Room - + + &Create Room + &Raum erstellen - - Show Current Room - + + &Leave Room + &Raum verlassen + &Direct Connect to Room + &Direkte Verbindung zum Raum + + + + &Show Current Room + &Aktuellen Raum anzeigen + + + F&ullscreen Vollbild (&u) - + &Restart Neusta&rt - + Load/Remove &Amiibo... &Amiibo laden/entfernen... - + &Report Compatibility &Kompatibilität melden - + Open &Mods Page &Mods-Seite öffnen - + Open &Quickstart Guide &Schnellstart-Anleitung öffnen - + &FAQ &FAQ - + Open &yuzu Folder &yuzu-Verzeichnis öffnen - + &Capture Screenshot &Bildschirmfoto aufnehmen - + &Configure TAS... - + &TAS &konfigurieren... - + Configure C&urrent Game... &Spiel-Einstellungen ändern... - + &Start &Start - + &Reset &Zurücksetzen - + R&ecord @@ -6090,7 +6244,7 @@ Debug Message: Refreshing - + Aktualisiere @@ -6100,7 +6254,7 @@ Debug Message: Subject - + Thema @@ -6110,7 +6264,7 @@ Debug Message: Forum Username - + Forum Benutzername @@ -6126,63 +6280,58 @@ Debug Message: MultiplayerState - - + Current connection status Aktueller Verbindungsstatus - - + Not Connected. Click here to find a room! - + Nicht verbunden! Hier klicken um Raum zu finden! - - - Connected - Verbunden - - - - Not Connected Nicht verbunden - + + Connected + Verbunden + + + + New Messages Received + Neue Nachrichten erhalten + + + Error Fehler - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - Neue Nachrichten erhalten - NetworkMessage Username is not valid. Must be 4 to 20 alphanumeric characters. - + Benutzername ist ungültig. Muss aus 4 bis 20 alphanumerischen Zeichen bestehen. Room name is not valid. Must be 4 to 20 alphanumeric characters. - + Raumname ist ungütig. Muss aus 4 bis 20 alphanumerischen Zeichen bestehen. Username is already in use or not valid. Please choose another. - + Benutzername wird bereits genutzt oder ist ungültig. Bitte wähle einen anderen. @@ -6212,12 +6361,12 @@ Debug Message: Unable to connect to the room because it is already full. - + Verbindung zum Raum fehlgeschlagen, da dieser bereits voll ist. Creating a room failed. Please retry. Restarting yuzu might be necessary. - + Raum konnte nicht erstellt werden. Bitte versuche es erneut. Ein Neustart von yuzu ist eventuell notwendig. @@ -6237,51 +6386,69 @@ Debug Message: An unknown error occurred. If this error continues to occur, please open an issue - + Ein unbekannter Fehler ist aufgetreten. Sollte der Fehler weiterhin auftreten, erstelle bitte eine Fehlermeldung. Connection to room lost. Try to reconnect. - + Verbindung zum Raum verloren. Versuche dich erneut zu verbinden. You have been kicked by the room host. - + Sie wurden vom Raum-Ersteller gekickt. IP address is already in use. Please choose another. - + IP-Addresse wird bereits genutzt. Bitte wählen Sie eine andere aus. You do not have enough permission to perform this action. - + Du besitzt nicht genug Rechte, um diese Aktion durchzuführen. The user you are trying to kick/ban could not be found. They may have left the room. + Den Benutzer, welchen du bannen/kicken wolltest, konnte nicht gefunden werden. +Eventuell hat dieser den Raum verlassen. + + + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. - + + Game already running + Spiel läuft bereits + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room Raum verlassen - + You are about to close the room. Any network connections will be closed. - + Disconnect Verbindung trennen - + You are about to leave the room. Any network connections will be closed. @@ -6289,7 +6456,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Fehler @@ -6338,42 +6505,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game %1 spielt kein Spiel - + %1 is playing %2 %1 spielt %2 - + Not playing a game Spielt kein Spiel - + Installed SD Titles Installierte SD-Titel - + Installed NAND Titles Installierte NAND-Titel - + System Titles Systemtitel - + Add New Game Directory Neues Spieleverzeichnis hinzufügen - + Favorites Favoriten @@ -6403,7 +6570,7 @@ p, li { white-space: pre-wrap; } - + [not set] [nicht gesetzt] @@ -6418,10 +6585,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Achse %1%2 @@ -6435,9 +6602,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [unbekannt] @@ -6593,7 +6760,7 @@ p, li { white-space: pre-wrap; } [undefined] - + [undefiniert] @@ -6602,15 +6769,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [ungültig] - - + + %1%2Hat %3 @@ -6618,35 +6785,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Achse %3 - + %1%2Axis %3,%4,%5 %1%2Achse %3,%4,%5 - + %1%2Motion %3 %1%2Bewegung %3 - - + + %1%2Button %3 %1%2Knopf %3 - + [unused] [unbenutzt] @@ -6679,7 +6846,7 @@ p, li { white-space: pre-wrap; } Task - + Aufgabe @@ -6687,11 +6854,124 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + Typ + + + + Name + Name + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6795,6 +7075,7 @@ p, li { white-space: pre-wrap; } + Handheld Handheld @@ -6834,11 +7115,6 @@ p, li { white-space: pre-wrap; } Docked Im Dock - - - Undocked - Handheld - Vibration diff --git a/dist/languages/el.ts b/dist/languages/el.ts index 6369a8f..e0d0a7c 100644 --- a/dist/languages/el.ts +++ b/dist/languages/el.ts @@ -30,7 +30,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Ιστοσελίδα</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Πηγαίος Κώδικας</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Συνεργάτες</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"></span>Άδεια</span></a></p></body></html> @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -187,7 +187,7 @@ This would ban both their forum username and their IP address. Leave Room - + Αποχωρήσει από το δωμάτιο @@ -204,8 +204,8 @@ This would ban both their forum username and their IP address. - %1 (%2/%3 members) - connected - + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 μέλη) - συνδεμένα @@ -218,6 +218,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Αναφορά συμβατότητας παιχνιδιού @@ -227,92 +232,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Αν επιλέξετε να υποβάλλετε μια υπόθεση για τεστ στη </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Λίστα Συμβατότητας του yuzu</span></a><span style=" font-size:10pt;">, Οι ακόλουθες πληροφορίες θα μαζευτούν και θα προβληθούν στο site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Πληροφορίες Υλικού (CPU / GPU / Λειτουργικό Σύστημα)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ποιά έκδοση του yuzu τρέχετε </li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Τον συνδεδεμένο λογαριασμό yuzu</li></ul></body></html> - - Perfect - Άψογα + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Το παιχνίδι λειτουργεί αλάνθαστα χωρίς ακουστικά ή γραφικά σφάλματα.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Καλά + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Το παιχνίδι λειτουργεί με σημαντικά γραφικά ή ακουστικά σφάλματα, και μπορεί να παιχτεί από την αρχή μέχρι το τέλος. Ίσως χρειάζεται μερικές μικροδιορθώσεις.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Εντάξει + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Το παιχνίδι λειτουργεί με σημαντικά γραφικά ή ακουστικά σφάλματα, αλλά το παιχνίδι μπορεί να παιχτεί από την αρχή μέχρι το τέλος με μικροδιορθώσεις.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Άσχημα + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Το παιχνίδι λειτουργεί, αλλά με σημαντικά γραφικά ή ακουστικά σφάλματα. Αδύνατο να προχωρήσει σε συγκεκριμένες περιοχές λόγω σφαλμάτων ακόμα και με μικροδιορθώσεις.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Εισαγωγή/Μενού + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Το παιχνίδι δεν μπορεί να παιχτεί λόγω σημαντικών γραφικών ή ακουστικών σφαλμάτων. Αδύνατον να προχωρήσει μετά την Αρχική Οθόνη</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Δεν ξεκινά + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Το παιχνίδι καταρρέει όταν προσπαθεί να ξεκινήσει.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Ανεξάρτητα από την ταχύτητα ή την απόδοση, πόσο καλά παίζει αυτό το παιχνίδι από την αρχή μέχρι το τέλος σε αυτή την έκδοση του yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Ευχαριστούμε για την συμμετοχή! - + Submitting Υποβάλλεται - + Communication error Σφάλμα επικοινωνίας - + An error occurred while sending the Testcase Προέκυψε ένα σφάλμα κατά την αποστολή της Υπόθεσης Τεστ - + Next Επόμενο @@ -410,7 +450,7 @@ This would ban both their forum username and their IP address. Επαναφορά Προεπιλογών - + Auto @@ -755,200 +795,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub - + Port: Θύρα: - + Logging Καταγραφή Συμβάντων - + Global Log Filter Παγκόσμιο φίλτρο αρχείου καταγραφής - + Show Log in Console - + Open Log Location Άνοιγμα θέσης του αρχείου καταγραφής - + When checked, the max size of the log increases from 100 MB to 1 GB Όταν επιλεγεί, το μέγιστο μέγεθος του αρχείου καταγραφής αυξάνεται από 100 MB σε 1 GB - + Enable Extended Logging** Ενεργοποίηση Εκτεταμένης Καταγραφής** - + Homebrew Σπιτικό - + Arguments String - + Graphics Γραφικά - + When checked, the graphics API enters a slower debugging mode Όταν είναι επιλεγμένο, το API γραφικών εισέρχεται σε μια πιο αργή λειτουργία εντοπισμού σφαλμάτων - + Enable Graphics Debugging Ενεργοποίηση του Εντοπισμού Σφαλμάτων Γραφικών - + When checked, it enables Nsight Aftermath crash dumps Όταν είναι επιλεγμένο, ενεργοποιεί την ένδειξη σφαλμάτων Nsight Aftermath - + Enable Nsight Aftermath Ενεργοποίηση του Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Όταν επιλεγεί, θα απορρίψει όλους τους αρχικούς assembler shaders από την κρυφή μνήμη ή το παιχνίδι σκίασης δίσκου όπως βρέθηκε - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Όταν είναι επιλεγμένο, απενεργοποιεί τη μακροεντολή Just In Time compiler. Η ενεργοποίηση αυτού κάνει τα παιχνίδια να τρέχουν πιο αργά - + Disable Macro JIT Απενεργοποίηση του Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback Ενεργοποίηση Shader Feedback - + When checked, it executes shaders without loop logic changes Όταν είναι επιλεγμένο, εκτελεί shaders χωρίς αλλαγές στη λογική του βρόχου - + Disable Loop safety checks Απενεργοποίηση των ελέγχων ασφαλείας βρόχου - + Debugging Εντοπισμός Σφαλμάτων - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + Δημιουργία Minidump μετά από κατάρρευση + + + Advanced Προχωρημένα - + Kiosk (Quest) Mode - + Enable CPU Debugging Ενεργοποίηση Εντοπισμού Σφαλμάτων CPU - + Enable Debug Asserts Ενεργοποίηση Βεβαιώσεων Εντοπισμού Σφαλμάτων - + Enable Auto-Stub** Ενεργοποίηση Auto-Stub** - + Enable All Controller Types - + Disable Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + Επιτρέπει στο yuzu να ελέγχει για ένα λειτουργικό περιβάλλον Vulkan κατά την εκκίνηση του προγράμματος. Απενεργοποιήστε το αν αυτό προκαλεί προβλήματα με τα εξωτερικά προγράμματα που βλέπουν το yuzu. + + + + Perform Startup Vulkan Check + Εκτέλεση ελέγχου Vulkan κατά την εκκίνηση + + + **This will be reset automatically when yuzu closes. **Αυτό θα μηδενιστεί αυτόματα όταν το yuzu κλείσει. + + + Restart Required + Απαιτείται επανεκκίνηση + + + + yuzu is required to restart in order to apply this setting. + το yuzu πρέπει να επανεκκινηθεί για να εφαρμοστεί αυτή η ρύθμιση. + + + + Web applet not compiled + Το web applet δεν έχει συσταθεί + + + + MiniDump creation not compiled + Δημιουργία MiniDump που δεν έχει συσταθεί + ConfigureDebugController @@ -1318,193 +1393,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings Ρυθμίσεις Γραφικών - + Use disk pipeline cache - + Use asynchronous GPU emulation Χρησιμοποίηση ασύγχρονης εξομοίωσης GPU - + Accelerate ASTC texture decoding Επιτάχυνση του αποκωδικοποίηση υφής ASTC - + NVDEC emulation: Εξομοίωση NVDEC: - + No Video Output Χωρίς Έξοδο Βίντεο - + CPU Video Decoding Αποκωδικοποίηση Βίντεο CPU - + GPU Video Decoding (Default) Αποκωδικοποίηση Βίντεο GPU (Προεπιλογή) - + Fullscreen Mode: Λειτουργία Πλήρους Οθόνης: - + Borderless Windowed Παραθυροποιημένο Χωρίς Όρια - + Exclusive Fullscreen Αποκλειστική Πλήρης Οθόνη - + Aspect Ratio: Αναλογία Απεικόνισης: - + Default (16:9) Προεπιλογή (16:9) - + Force 4:3 Επιβολή 4:3 - + Force 21:9 Επιβολή 21:9 - + + Force 16:10 + Επιβολή 16:10 + + + Stretch to Window Επέκταση στο Παράθυρο - + Resolution: Ανάλυση: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [ΠΕΙΡΑΜΑΤΙΚΟ] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [ΠΕΙΡΑΜΑΤΙΚΟ] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: Φίλτρο Προσαρμογής Παραθύρου: - + Nearest Neighbor Πλησιέστερος Γείτονας - + Bilinear Διγραμμικό - + Bicubic Δικυβικό - + Gaussian Gaussian - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (μόνο Vulkan) - + Anti-Aliasing Method: Μέθοδος Anti-Aliasing: - + None Κανένα - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Χρησιμοποιήστε καθολικό χρώμα φόντου - + Set background color: Ορισμός χρώματος φόντου: - + Background Color: Χρώμα Φόντου: @@ -1513,6 +1613,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM (Shaders Γλώσσας Μηχανής, μόνο NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1544,7 +1650,7 @@ This would ban both their forum username and their IP address. Use VSync - + Χρήση VSync @@ -1567,37 +1673,47 @@ This would ban both their forum username and their IP address. Χρήση Γοργού Ρυθμού GPU (Τέχνασμα) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + Ενεργοποιεί περιστασιακές εκκαθαρίσεις των ρυθμιστικών διαύλων. Αυτή η επιλογή θα αναγκάσει τους μη τροποποιημένους ρυθμιστικούς διαύλους να εκκαθαριστούν, πράγμα που μπορεί να κοστίσει σε απόδοση. + + + + Use pessimistic buffer flushes (Hack) + Χρήση περιστασιακών εκκαθαρίσεων ρυθμιστικού διαύλου (Hack) + + + Anisotropic Filtering: Ανισοτροπικό Φιλτράρισμα: - + Automatic Αυτόματα - + Default Προεπιλεγμένο - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2089,7 +2205,7 @@ This would ban both their forum username and their IP address. - + Left Stick Αριστερό Stick @@ -2183,14 +2299,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2209,7 +2325,7 @@ This would ban both their forum username and their IP address. - + Plus Συν @@ -2222,15 +2338,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2287,231 +2403,236 @@ This would ban both their forum username and their IP address. - + Right Stick Δεξιός Μοχλός - - - - + + + + Clear Καθαρισμός - - - - - + + + + + [not set] [άδειο] - - - Toggle button - Κουμπί εναλλαγής - - - - + + Invert button Κουμπί αντιστροφής - - + + + Toggle button + Κουμπί εναλλαγής + + + + Invert axis Αντιστροφή άξονα - - - + + + Set threshold Ορισμός ορίου - - + + Choose a value between 0% and 100% Επιλέξτε μια τιμή μεταξύ 0% και 100% - + + Toggle axis + Εναλλαγή αξόνων + + + Set gyro threshold Ρύθμιση κατωφλίου γυροσκοπίου - + Map Analog Stick Χαρτογράφηση Αναλογικού Stick - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Αφού πατήσετε OK, μετακινήστε πρώτα το joystick σας οριζόντια και μετά κατακόρυφα. Για να αντιστρέψετε τους άξονες, μετακινήστε πρώτα το joystick κατακόρυφα και μετά οριζόντια. - + Center axis Κεντρικός άξονας - - + + Deadzone: %1% Νεκρή Ζώνη: %1% - - + + Modifier Range: %1% Εύρος Τροποποιητή: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Διπλά Joycons - + Left Joycon Αριστερό Joycon - + Right Joycon Δεξί Joycon - + Handheld Handheld - + GameCube Controller Χειριστήριο GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Χειριστήριο NES - + SNES Controller Χειριστήριο SNES - + N64 Controller Χειριστήριο N64 - + Sega Genesis Sega Genesis - + Start / Pause - + Z Z - + Control Stick - + C-Stick C-Stick - + Shake! - + [waiting] [αναμονή] - + New Profile Νέο Προφίλ - + Enter a profile name: Εισαγάγετε ένα όνομα προφίλ: - - + + Create Input Profile Δημιουργία Προφίλ Χειρισμού - + The given profile name is not valid! Το όνομα του προφίλ δεν είναι έγκυρο! - + Failed to create the input profile "%1" Η δημιουργία του προφίλ χειρισμού "%1" απέτυχε - + Delete Input Profile Διαγραφή Προφίλ Χειρισμού - + Failed to delete the input profile "%1" Η διαγραφή του προφίλ χειρισμού "%1" απέτυχε - + Load Input Profile Φόρτωση Προφίλ Χειρισμού - + Failed to load the input profile "%1" Η φόρτωση του προφίλ χειρισμού "%1" απέτυχε - + Save Input Profile Αποθήκευση Προφίλ Χειρισμού - + Failed to save the input profile "%1" Η αποθήκευση του προφίλ χειρισμού "%1" απέτυχε @@ -2766,42 +2887,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Προγραμματιστής - + Add-Ons Πρόσθετα - + General Γενικά - + System Σύστημα - + CPU CPU - + Graphics Γραφικά - + Adv. Graphics Προχ. Γραφικά - + Audio Ήχος - + Properties Ιδιότητες @@ -2857,37 +2978,37 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Τρέχων Χρήστης - + Username Όνομα χρήστη - + Set Image Ορισμός Εικόνας - + Add Προσθήκη - + Rename Μετονομασία - + Remove Αφαίρεση - + Profile management is available only when game is not running. Η διαχείριση προφίλ είναι διαθέσιμη μόνο όταν το παιχνίδι δεν εκτελείται. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2895,96 +3016,105 @@ To invert the axes, first move your joystick vertically, and then horizontally.< %2 - + Enter Username Εισάγετε Όνομα Χρήστη - + Users Χρήστες - + Enter a username for the new user: Εισαγάγετε ένα όνομα χρήστη για τον νέο χρήστη: - + Enter a new username: Εισαγάγετε ένα νέο όνομα χρήστη: - - Confirm Delete - Επιβεβαίωση Διαγραφής - - - - You are about to delete user with name "%1". Are you sure? - Πρόκειται να διαγράψετε χρήστη με όνομα "%1". Είστε σίγουροι; - - - + Select User Image Επιλέξτε Εικόνα χρήστη - + JPEG Images (*.jpg *.jpeg) Εικόνες JPEG (*.jpg *.jpeg) - + Error deleting image Σφάλμα κατα τη διαγραφή εικόνας - + Error occurred attempting to overwrite previous image at: %1. Παρουσιάστηκε σφάλμα κατά την προσπάθεια αντικατάστασης της προηγούμενης εικόνας στο: %1. - + Error deleting file Σφάλμα κατα τη διαγραφή του αρχείου - + Unable to delete existing file: %1. Δεν είναι δυνατή η διαγραφή του υπάρχοντος αρχείου: %1. - + Error creating user image directory Σφάλμα δημιουργίας καταλόγου εικόνων χρήστη - + Unable to create directory %1 for storing user images. Δεν είναι δυνατή η δημιουργία του καταλόγου %1 για την αποθήκευση εικόνων χρήστη. - + Error copying user image Σφάλμα κατά την αντιγραφή της εικόνας χρήστη - + Unable to copy image from %1 to %2 Αδύνατη η αντιγραφή της εικόνας από το %1 στο %2 - + Error resizing user image Σφάλμα αλλαγής μεγέθους εικόνας χρήστη - + Unable to resize image Δεν είναι δυνατή η αλλαγή μεγέθους της εικόνας + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Επιβεβαίωση Διαγραφής + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3447,7 +3577,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< RNG Seed - + RNG Seed @@ -3513,47 +3643,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Αυτό είναι ένα πειραματικό χαρακτηριστικό.<br/>Δεν θα αναπαράγει τέλεια καρέ με την τρέχουσα, ατελή μέθοδο συγχρονισμού. - + Settings Ρυθμίσεις - + Enable TAS features Ενεργοποίηση λειτουργιών TAS - + Loop script Σενάριο επανάληψης - + Pause execution during loads Παύση εκτέλεσης κατά τη διάρκεια φόρτωσης - + Script Directory Κατάλογος Σεναρίων - + Path Μονοπάτι - + ... ... @@ -3804,56 +3934,71 @@ Drag points to change position, or double-click table cells to edit values. + Show Compatibility List + + + + Show Add-Ons Column - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: - + Folder Icon Size: - + Row 1 Text: - + Row 2 Text: - + Screenshots Στιγμιότυπα - + Ask Where To Save Screenshots (Windows Only) - + Screenshots Path: - + ... ... - + Select Screenshots Path... - + <System> <System> @@ -3962,7 +4107,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify Επαλήθευση @@ -3974,7 +4119,7 @@ Drag points to change position, or double-click table cells to edit values. Token: - + Διαπιστευτήριο: @@ -3984,7 +4129,7 @@ Drag points to change position, or double-click table cells to edit values. What is my token? - + Ποιο είναι το διακριτικό μου; @@ -4009,7 +4154,7 @@ Drag points to change position, or double-click table cells to edit values. Telemetry ID: - + Αναγνωριστικό τηλεμετρίας: @@ -4029,7 +4174,7 @@ Drag points to change position, or double-click table cells to edit values. <a href='https://yuzu-emu.org/help/feature/telemetry/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> - + <a href='https://yuzu-emu.org/help/feature/telemetry/'><span style="text-decoration: underline; color:#039be5;">Μάθετε περισσότερα</span></a> @@ -4049,7 +4194,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified @@ -4064,17 +4209,36 @@ Drag points to change position, or double-click table cells to edit values. - + + Unverified, please click Verify before saving configuration + Tooltip + Μη επαληθευμένο, κάντε κλικ στο κουμπί Επαλήθευση πριν αποθηκεύσετε τις ρυθμίσεις + + + + Verifying... - - Verification failed - + + Verified + Tooltip + Επαληθεύτηκε - + + Verification failed + Tooltip + Η επαλήθευση απέτυχε + + + + Verification failed + Η επαλήθευση απέτυχε + + + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. @@ -4143,12 +4307,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting - + Connect @@ -4156,907 +4320,914 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? - + Telemetry Τηλεμετρία - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Πόσα καρέ ανά δευτερόλεπτο εμφανίζει το παιχνίδι αυτή τη στιγμή. Αυτό διαφέρει από παιχνίδι σε παιχνίδι και από σκηνή σε σκηνή. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files - + &Continue &Συνέχεια - + &Pause &Παύση - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. - + Μη μεταφρασμένη συμβολοσειρά + +Χρησιμοποιείτε τη μορφή καταλόγου αποδομημένης ROM για αυτό το παιχνίδι, η οποία είναι μια ξεπερασμένη μορφή που έχει αντικατασταθεί από άλλες, όπως NCA, NAX, XCI ή NSP. Οι αποδομημένοι κατάλογοι ROM στερούνται εικονιδίων, μεταδεδομένων και υποστήριξης ενημερώσεων.<br><br> +Για μια εξήγηση των διαφόρων μορφών Switch που υποστηρίζει το yuzu,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'> δείτε το wiki μας </a>. Αυτό το μήνυμα δεν θα εμφανιστεί ξανά. - - + + Error while loading ROM! - + Σφάλμα κατά τη φόρτωση της ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - + Εμφανίστηκε ένα απροσδιόριστο σφάλμα. Ανατρέξτε στο αρχείο καταγραφής για περισσότερες λεπτομέρειες. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Αποθήκευση δεδομένων - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! Ο φάκελος δεν υπάρχει! - + Error Opening Transferable Shader Cache - + Failed to create the shader cache directory for this title. - - Contents - Περιεχόμενα - - - - Update - Ενημέρωση - - - - DLC - DLC - - - - Remove Entry - - - - - Remove Installed Game %1? - - - - - - - - - Successfully Removed + Error Removing Contents - - Successfully removed the installed base game. - - - - - - - Error Removing %1 - - - - - The base game is not installed in the NAND and cannot be removed. - - - - - Successfully removed the installed update. + + Error Removing Update + Error Removing DLC + + + + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + + Remove Entry + + + + + + + + + + Successfully Removed + + + + + Successfully removed the installed base game. + + + + + The base game is not installed in the NAND and cannot be removed. + + + + + Successfully removed the installed update. + + + + There is no update installed for this title. - + There are no DLC installed for this title. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File Αφαίρεση Αρχείου - - + + Error Removing Transferable Shader Cache - - + + A shader cache for this title does not exist. - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Επιλογή λειτουργίας απόρριψης RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + Μη αποθηκευμένη μετάφραση. +Παρακαλούμε επιλέξτε τον τρόπο με τον οποίο θα θέλατε να γίνει η απόρριψη της RomFS.<br> +Η επιλογή Πλήρης θα αντιγράψει όλα τα αρχεία στο νέο κατάλογο, ενώ η επιλογή <br> Σκελετός θα δημιουργήσει μόνο τη δομή του καταλόγου. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel Ακύρωση - + RomFS Extraction Succeeded! - + The operation completed successfully. Η επέμβαση ολοκληρώθηκε με επιτυχία. - + Error Opening %1 - + Select Directory Επιλογή καταλόγου - + Properties Ιδιότητες - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File Φόρτωση αρχείου - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... - - + + Install Results Αποτελέσματα εγκατάστασης - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Εφαρμογή συστήματος - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game Παιχνίδι - + Game Update Ενημέρωση παιχνιδιού - + Game DLC DLC παιχνιδιού - + Delta Title - + Select NCA Install Type... - + Επιλέξτε τον τύπο εγκατάστασης NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found Το αρχείο δεν βρέθηκε - + File "%1" not found Το αρχείο "%1" δεν βρέθηκε - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. - + Error opening URL Σφάλμα κατα το άνοιγμα του URL - + Unable to open the URL "%1". Αδυναμία ανοίγματος του URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - - Error - - - - - - The current game is not looking for amiibos - - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed - + + Error + Σφάλμα + + + + + The current game is not looking for amiibos + + + + Amiibo File (%1);; All Files (*.*) - + Load Amiibo Φόρτωση Amiibo - - Error opening Amiibo data file - - - - - Unable to open Amiibo file "%1" for reading. - - - - - Error reading Amiibo data file - - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - - - - + Error loading Amiibo data - + Σφάλμα φόρτωσης δεδομένων Amiibo - - Unable to load Amiibo data. - Αδυναμία φόρτωσης Amiibo δεδομένων. + + The selected file is not a valid amiibo + Το επιλεγμένο αρχείο δεν αποτελεί έγκυρο amiibo - - Capture Screenshot - + + The selected file is already on use + Το επιλεγμένο αρχείο χρησιμοποιείται ήδη - - PNG Image (*.png) - Εικόνα PBG (*.png) - - - - TAS state: Running %1/%2 - - - - - TAS state: Recording %1 - - - - - TAS state: Idle %1/%2 + + An unknown error occurred + Capture Screenshot + Λήψη στιγμιότυπου οθόνης + + + + PNG Image (*.png) + Εικόνα PBG (*.png) + + + + TAS state: Running %1/%2 + + + + + TAS state: Recording %1 + + + + + TAS state: Idle %1/%2 + + + + TAS State: Invalid - + &Stop Running - + &Start &Έναρξη - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Κλίμακα: %1x - + Speed: %1% / %2% Ταχύτητα: %1% / %2% - + Speed: %1% Ταχύτητα: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS - + Frame: %1 ms - + Καρέ: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR FSR - - + + NO AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + yuzu was unable to locate a Switch system archive. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 - + System Archive Not Found - + System Archive Missing - + yuzu was unable to locate the Switch shared fonts. %1 - + Shared Fonts Not Found - + Shared Font Missing - + Fatal Error Σοβαρό Σφάλμα - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + Fatal Error encountered Παρουσιάστηκε Σοβαρό Σφάλμα - + Confirm Key Rederivation - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5067,76 +5238,76 @@ This will delete your autogenerated key files and re-run the key derivation modu - + Missing fuses - + - Missing BOOT0 - Λείπει το BOOT0 - + - Missing BCPKG2-1-Normal-Main - Λείπει το BCPKG2-1-Normal-Main - + - Missing PRODINFO - Λείπει το PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. - + Deriving Keys - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close yuzu? Είστε σίγουροι ότι θέλετε να κλείσετε το yuzu; - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5146,38 +5317,38 @@ Would you like to bypass this and exit anyway? GRenderWindow - + OpenGL not available! Το OpenGL δεν είναι διαθέσιμο! - + yuzu has not been compiled with OpenGL support. - - + + Error while initializing OpenGL! Σφάλμα κατα την αρχικοποίηση του OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. - + Error while initializing OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 @@ -5185,153 +5356,153 @@ Would you like to bypass this and exit anyway? GameList - + Favorite Αγαπημένο - + Start Game Έναρξη παιχνιδιού - + Start Game without Custom Configuration - + Open Save Data Location Άνοιγμα Τοποθεσίας Αποθήκευσης Δεδομένων - + Open Mod Data Location Άνοιγμα Τοποθεσίας Δεδομένων Mod - + Open Transferable Pipeline Cache - + Remove Αφαίρεση - + Remove Installed Update Αφαίρεση Εγκατεστημένης Ενημέρωσης - + Remove All Installed DLC Αφαίρεση Όλων των Εγκατεστημένων DLC - + Remove Custom Configuration - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches Καταργήστε Όλη την Κρυφή μνήμη του Pipeline - + Remove All Installed Contents Καταργήστε Όλο το Εγκατεστημένο Περιεχόμενο - + Dump RomFS Απόθεση του RomFS - + Dump RomFS to SDMC Απόθεση του RomFS στο SDMC - + Copy Title ID to Clipboard Αντιγραφή του Title ID στο Πρόχειρο - + Navigate to GameDB entry Μεταβείτε στην καταχώρηση GameDB - + Properties Ιδιότητες - + Scan Subfolders Σκανάρισμα Υποφακέλων - + Remove Game Directory Αφαίρεση Φακέλου Παιχνιδιών - + ▲ Move Up ▲ Μετακίνηση Επάνω - + ▼ Move Down ▼ Μετακίνηση Κάτω - + Open Directory Location Ανοίξτε την Τοποθεσία Καταλόγου - + Clear Καθαρισμός - + Name Όνομα - + Compatibility Συμβατότητα - + Add-ons Πρόσθετα - + File type Τύπος αρχείου - + Size Μέγεθος @@ -5340,77 +5511,61 @@ Would you like to bypass this and exit anyway? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Τέλεια - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - - - - - Great - Καλά - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - - - Okay - Okay - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. + Game can be played without issues. - Bad - Άσχημα - - - - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. + Playable - + + Game functions with minor graphical or audio glitches and is playable from start to finish. + + + + Intro/Menu Εισαγωγή/Μενου - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Το παιχνίδι δεν μπορεί να παιχτεί εντελώς λόγω σημαντικών προβλημάτων γραφικών ή ήχου. -Δεν είναι δυνατή η πρόοδος μετά την Αρχική Οθόνη. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Δεν ξεκινά - + The game crashes when attempting to startup. Το παιχνίδι διακόπτεται κατά την προσπάθεια εκκίνησης. - + Not Tested Μη Τεσταρισμένο - + The game has not yet been tested. Το παιχνίδι δεν έχει ακόμα τεσταριστεί. @@ -5418,7 +5573,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Διπλο-κλικ για προσθήκη νεου φακέλου στη λίστα παιχνιδιών @@ -5431,12 +5586,12 @@ Screen. - + Filter: Φίλτρο: - + Enter pattern to filter Εισαγάγετε μοτίβο για φιλτράρισμα @@ -5512,12 +5667,12 @@ Screen. HostRoomWindow - + Error - + Σφάλμα - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5526,11 +5681,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5552,112 +5708,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot - + Λήψη στιγμιότυπου οθόνης - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Πλήρη Οθόνη - + Load File Φόρτωση αρχείου - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5771,42 +5926,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5821,7 +5976,7 @@ Debug Message: &File - + &Αρχείο @@ -5829,232 +5984,237 @@ Debug Message: - + &Emulation - + &View - + &Reset Window Size - + &Debugging - + Reset Window Size to &720p - + Reset Window Size to 720p - + Reset Window Size to &900p - + Reset Window Size to 900p - + Reset Window Size to &1080p - + Reset Window Size to 1080p - + + &Multiplayer + &Πολλαπλών Παικτών + + + &Tools - + &TAS &TAS - + &Help - + &Install Files to NAND... - + L&oad File... - + Load &Folder... - + E&xit - + &Pause &Παύση - + &Stop - + &Σταμάτημα - + &Reinitialize keys... - + &About yuzu - + Single &Window Mode - + Con&figure... - + Display D&ock Widget Headers - + Show &Filter Bar - + Show &Status Bar - + Show Status Bar - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room - + &Browse Public Game Lobby + &Περιήγηση σε δημόσιο λόμπι παιχνιδιού - - Direct Connect to Room - + + &Create Room + &Δημιουργία δωματίου - - Show Current Room - + + &Leave Room + &Αποχωρήσει από το δωμάτιο + &Direct Connect to Room + &Άμεση σύνδεση σε Δωμάτιο + + + + &Show Current Room + &Εμφάνιση τρέχοντος δωματίου + + + F&ullscreen - + &Restart - + Load/Remove &Amiibo... - + &Report Compatibility - + Open &Mods Page - + Open &Quickstart Guide - + &FAQ - + Open &yuzu Folder - + &Capture Screenshot - + &Configure TAS... - + Configure C&urrent Game... - + &Start &Έναρξη - + &Reset - + R&ecord @@ -6119,46 +6279,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Συνδεδεμένο - - - - Not Connected - - Error + + Connected + Συνδεδεμένο + + + + New Messages Received - + + Error + Σφάλμα + + + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6225,66 +6380,86 @@ Debug Message: Incorrect password. - + Λανθασμένος κωδικός πρόσβασης. An unknown error occurred. If this error continues to occur, please open an issue - + Εμφανίστηκε ένα άγνωστο σφάλμα. Αν αυτό το σφάλμα συνεχίζει να εμφανίζεται, ανοίξτε ένα αίτημα Connection to room lost. Try to reconnect. - + Η σύνδεση με το δωμάτιο χάθηκε. Προσπαθήστε να επανασυνδεθείτε. You have been kicked by the room host. - + Έχετε διωχθεί από τον οικοδεσπότη του δωματίου. IP address is already in use. Please choose another. - + Η διεύθυνση IP χρησιμοποιείται ήδη. Παρακαλώ επιλέξτε άλλη. You do not have enough permission to perform this action. - + Δεν έχετε επαρκή δικαιώματα για την εκτέλεση αυτής της ενέργειας. The user you are trying to kick/ban could not be found. They may have left the room. - + Ο χρήστης που προσπαθείτε να διώξετε/αποβάλλετε δεν βρέθηκε. +Μπορεί να έχει φύγει από το δωμάτιο. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + Δεν έχει επιλεγεί έγκυρη διασύνδεση δικτύου. +Παρακαλούμε μεταβείτε στη Ρυθμίσεις -> Σύστημα -> Δίκτυο και κάντε μια επιλογή. + + + + Game already running + Το παιχνίδι εκτελείται ήδη + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + Η συμμετοχή σε ένα δωμάτιο όταν το παιχνίδι είναι ήδη σε εξέλιξη δεν συνιστάται και μπορεί να προκαλέσει τη μη σωστή εκτέλεση της λειτουργίας του δωματίου. +Προχωρήστε ούτως ή άλλως; + + + Leave Room - + Αποχωρήσει από το δωμάτιο - + You are about to close the room. Any network connections will be closed. - + Ετοιμάζεστε να κλείσετε το δωμάτιο. Όλες οι δικτυακές συνδέσεις θα κλείσουν. - + Disconnect - + Αποσύνδεση - + You are about to leave the room. Any network connections will be closed. - + Ετοιμάζεστε να φύγετε από το δωμάτιο. Όλες οι δικτυακές συνδέσεις θα κλείσουν. NetworkMessage::ErrorManager - + Error - + Σφάλμα @@ -6327,42 +6502,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 δεν παίζει παιχνίδι - + %1 is playing %2 - + %1 παίζει %2 - + Not playing a game - + Δεν παίζει παιχνίδι - + Installed SD Titles - + Installed NAND Titles - + System Titles Τίτλοι Συστήματος - + Add New Game Directory Προσθήκη Νέας Τοποθεσίας Παιχνιδιών - + Favorites Αγαπημένα @@ -6392,7 +6567,7 @@ p, li { white-space: pre-wrap; } - + [not set] [μη ορισμένο] @@ -6407,12 +6582,12 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 - + Άξονας%1%2 @@ -6424,9 +6599,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [άγνωστο] @@ -6591,15 +6766,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6607,35 +6782,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [άδειο] @@ -6676,11 +6851,124 @@ p, li { white-space: pre-wrap; } - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Όνομα + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6784,6 +7072,7 @@ p, li { white-space: pre-wrap; } + Handheld Handheld @@ -6823,11 +7112,6 @@ p, li { white-space: pre-wrap; } Docked Docked - - - Undocked - Undocked - Vibration @@ -6979,7 +7263,7 @@ Please try again or contact the developer of the software. Select a user: - + Επιλογή Χρήστη @@ -7042,7 +7326,7 @@ p, li { white-space: pre-wrap; } Call stack - + Κλήση stack @@ -7068,7 +7352,7 @@ p, li { white-space: pre-wrap; } waiting for all objects - + αναμονή για όλα τα αντικείμενα @@ -7114,7 +7398,7 @@ p, li { white-space: pre-wrap; } waiting for objects - + αναμονή αντικειμένων @@ -7189,7 +7473,7 @@ p, li { white-space: pre-wrap; } priority = %1(current) / %2(normal) - + προτεραιότητα = %1(τρέχον) / %2(κανονικό) diff --git a/dist/languages/es.ts b/dist/languages/es.ts index 3b81336..dc032ef 100644 --- a/dist/languages/es.ts +++ b/dist/languages/es.ts @@ -29,9 +29,9 @@ p, li { white-space: pre-wrap; } <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu es un emulador experimental código abierto de la Nintendo Switch licenciada bajo GPLv3.0+.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu es un emulador experimental código abierto de Nintendo Switch licenciada bajo GPLv3.0+.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Este software no debe ser utilizado para jugar juegos que no has conseguido de manera legal.</span></p></body></html> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Este software no debe ser utilizado para jugar a juegos que no se hayan obtenido legalmente.</span></p></body></html> @@ -59,12 +59,12 @@ p, li { white-space: pre-wrap; } Touch the top left corner <br>of your touchpad. - Toque la esquina superior izquierda<br>de su trackpad. + Toque la esquina superior izquierda<br>del trackpad. Now touch the bottom right corner <br>of your touchpad. - Ahora toque la esquina inferior derecha <br>de su trackpad. + Ahora toque la esquina inferior derecha <br>del trackpad. @@ -95,82 +95,82 @@ p, li { white-space: pre-wrap; } Enviar mensaje - + Members Miembros - + %1 has joined %1 se ha unido - + %1 has left %1 se ha ido - + %1 has been kicked %1 ha sido expulsado - + %1 has been banned - %1 ha sido baneado + %1 ha sido vetado - + %1 has been unbanned - %1 ha sido desbaneado + %1 se le ha retirado el veto - + View Profile Ver perfil - - + + Block Player Bloquear jugador - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Cuando bloqueas a un jugador, ya no recibirás mensajes de ellos. <br><br> ¿Estás seguro de que quieres bloquear a %1? - + Kick Expulsar - + Ban - Banear + Vetar - + Kick Player Expulsar jugador - + Are you sure you would like to <b>kick</b> %1? - ¿Estás seguro de que quieres <b>expulsar</b> a %1? + ¿Estás seguro que quieres <b>expulsar</b> a %1? - + Ban Player - Banear jugador + Vetar jugador - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - ¿Estas seguro de que quieres <b>expulsar y banear a</b>%1? + ¿Estás seguro que quieres <b>expulsar y vetar a</b>%1? Esto banearía su nombre del foro y su dirección IP. @@ -212,8 +212,8 @@ Esto banearía su nombre del foro y su dirección IP. - %1 (%2/%3 members) - connected - %1 (%2/%3 miembros) - conectado + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 miembros) - conectado @@ -226,101 +226,141 @@ Esto banearía su nombre del foro y su dirección IP. + + + + + Report Game Compatibility Informar de compatibilidad del juego <html><head/><body><p><span style=" font-size:10pt;">Should you choose to submit a test case to the </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">yuzu Compatibility List</span></a><span style=" font-size:10pt;">, The following information will be collected and displayed on the site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware Information (CPU / GPU / Operating System)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Which version of yuzu you are running</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The connected yuzu account</li></ul></body></html> - <html><head/><body><p><span style=" font-size:10pt;">Si deseas presentar una prueba a la </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Lista de Compatibilidad de yuzu</span></a><span style=" font-size:10pt;">, La siguiente información será obtenida y mostrada en el sitio web:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informacion de Hardware (CPU / GPU / Sistema operativo)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Versión de yuzu que estés utilizando</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">La cuenta de yuzu conectada</li></ul></body></html> + <html><head/><body><p><span style=" font-size:10pt;">Si deseas presentar una prueba a la </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Lista de Compatibilidad de yuzu</span></a><span style=" font-size:10pt;">, La siguiente información será obtenida y publicada en el sitio web:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informacion de Hardware (CPU / GPU / Sistema operativo)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Versión de yuzu que estés utilizando</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">La cuenta de yuzu conectada</li></ul></body></html> - - Perfect - Perfecto + + <html><head/><body><p>Does the game boot?</p></body></html> + <html><head/><body><p>¿El juego se ejecuta?</p></body></html> - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>El juego funciona a la perfección sin ningún problema gráfico o de audio.</p></body></html> + + Yes The game starts to output video or audio + Sí El juego llega a reproducir vídeo o sonido - - Great - Excelente + + No The game doesn't get past the "Launching..." screen + No El juego no consigue avanzar más allá de la pantalla "Iniciando..." - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>El juego funciona con fallos gráficos o de audio menores y es jugable de principio a fin. Puede que sea necesario recurrir a arreglos temporales.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + Sí El juego avanza del menú y entra al juego - - Okay - Bien + + No The game crashes or freezes while loading or using the menu + No El juego se bloquea o se congela al cargar o al usar el menú - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>El juego funciona con fallos gráficos o de audio sustanciales, pero es jugable de principio a fin con arreglos temporales.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + <html><head/><body><p>¿Alcanza el juego la jugabilidad?</p></body></html> - - Bad - Mal + + Yes The game works without crashes + Sí El juego funciona sin errores - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>El juego funciona, pero tiene errores gráficos o de audio. Imposible progresar en ciertas zonas debido a fallos incluso con arreglos temporales.</p></body></html> + + No The game crashes or freezes during gameplay + No El juego se bloquea o se congela durante el juego - - Intro/Menu - Intro/Menu + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + <html><head/><body><p>¿Funciona el juego sin que se interrumpa, se congele o se bloquee durante la partida?</p></body></html> - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>No es posible jugar a este juego debido a errores gráficos o de audio importantes. Es imposible avanzar mas allá de la pantalla de inicio.</p></body></html> + + Yes The game can be finished without any workarounds + Sí El juego se puede terminar sin ningún tipo de solución temporal. - - Won't Boot - No inicia + + No The game can't progress past a certain area + No El juego no puede avanzar más allá de una zona concreta - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>El juego se bloquea al intentar iniciar.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + <html><head/><body><p>¿El juego es completamente jugable de principio a fin?</p></body></html> - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Independientemente de la velocidad o del rendimiento, ¿cómo definiría su experiencia de juego de principio a fin en esta versión de yuzu?</p></body></html> + + Major The game has major graphical errors + Importantes El juego tiene errores gráficos importantes - + + Minor The game has minor graphical errors + Menores El juego tiene pequeños errores gráficos + + + + None Everything is rendered as it looks on the Nintendo Switch + Ninguno Todo está renderizado conforme se ve en la Nintendo Switch + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + <html><head/><body><p>¿El juego tiene algún error gráfico?</p></body></html> + + + + Major The game has major audio errors + Importantes El juego tiene grandes problemas de sonido + + + + Minor The game has minor audio errors + Menores El juego tiene pequeños problemas de sonido + + + + None Audio is played perfectly + Ninguno El sonido se reproduce perfectamente + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + <html><head/><body><p>¿El juego tiene algún problema de sonido o falta de efectos?</p></body></html> + + + Thank you for your submission! Gracias por su contribución. - + Submitting Enviando - + Communication error Error de comunicación. - + An error occurred while sending the Testcase Ha ocurrido un fallo mientras se enviaba el caso de prueba. - + Next Siguiente @@ -418,7 +458,7 @@ Esto banearía su nombre del foro y su dirección IP. Restaurar valores predeterminados - + Auto Auto @@ -771,200 +811,235 @@ Esto banearía su nombre del foro y su dirección IP. ConfigureDebug - + Debugger Depurador - + Enable GDB Stub Activar GDB Stub - + Port: Puerto: - + Logging Registro - + Global Log Filter Filtro del registro global - + Show Log in Console Ver registro en consola - + Open Log Location Abrir ubicación del archivo de registro - + When checked, the max size of the log increases from 100 MB to 1 GB Cuando se marque, el tamaño maximo del registro aumenta de 100 MB a 1 GB - + Enable Extended Logging** Habilitar registro extendido** - + Homebrew Homebrew - + Arguments String Cadena de argumentos - + Graphics Gráficos - + When checked, the graphics API enters a slower debugging mode Cuando esté marcado, la API gráfica entrará en un modo de depuración más lento. - + Enable Graphics Debugging Activar depuración de gráficos - + When checked, it enables Nsight Aftermath crash dumps Cuando esté marcado, activará los volcados de los fallos Nsight Aftermath - + Enable Nsight Aftermath Activar Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Al activarlo, esto volcará todos los sombreadores originales del ensamblador de la caché de sombreadores en disco o del juego encontrado - + Dump Game Shaders Volcar sombreadores del juego - + When checked, it will dump all the macro programs of the GPU Cuando esté activado, se volcarán todos los programas macro de la GPU - + Dump Maxwell Macros Volcar Macros Maxwell - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Cuando esté marcado, se desactiva el compilador de macro Just In Time. Activar esto hace que los juegos se ejecuten más lento. - + Disable Macro JIT Desactivar macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Cuando esté marcado, yuzu hará un registro de las estadísticas del caché de tubería compilado - + Enable Shader Feedback Activar información de shaders - + When checked, it executes shaders without loop logic changes Cuando esté marcado, se ejecutarán los shaders sin cambios de bucles lógicos. - + Disable Loop safety checks Desactivar comprobaciones de seguridad de bucles - + Debugging Depuración - - Enable FS Access Log - Activar registro de acceso FS - - - - Dump Audio Commands To Console** - Volcar comandos de audio a la consola** - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - Activa esta opción para mostrar en la consola la última lista de comandos de audio generada. Solo afecta a los juegos que utilizan el renderizador de audio. - - - + Enable Verbose Reporting Services** Activar servicios de reporte detallados** - + + Enable FS Access Log + Activar registro de acceso FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + Activa esta opción para mostrar en la consola la última lista de comandos de audio generada. Solo afecta a los juegos que utilizan el renderizador de audio. + + + + Dump Audio Commands To Console** + Volcar comandos de audio a la consola** + + + + Create Minidump After Crash + Crear mini volcado tras un crash + + + Advanced Avanzado - + Kiosk (Quest) Mode Modo quiosco (Quest) - + Enable CPU Debugging Activar depuración de la CPU - + Enable Debug Asserts Activar alertas de depuración - + Enable Auto-Stub** Activar Auto-Stub** - + Enable All Controller Types Habilitar todo tipo de controles - + Disable Web Applet Desactivar Web applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + Permite que yuzu compruebe si el entorno de Vulkan funciona cuando el programa se inicia. Desactiva esto si está causando problemas con los programas externos ligados a yuzu. + + + + Perform Startup Vulkan Check + Realizar comprobación de Vulkan al ejecutar + + + **This will be reset automatically when yuzu closes. **Esto se reiniciará automáticamente cuando yuzu se cierre. + + + Restart Required + Reinicio requerido + + + + yuzu is required to restart in order to apply this setting. + Para aplicar estos ajustes es necesario reiniciar yuzu. + + + + Web applet not compiled + La web applet no se ha compilado + + + + MiniDump creation not compiled + La creación del mini volcado no se ha compilado + ConfigureDebugController @@ -1334,193 +1409,218 @@ Esto banearía su nombre del foro y su dirección IP. API: - + Graphics Settings - Ajustes de gráficos + Ajustes gráficos - + Use disk pipeline cache Usar caché de shaders de tubería - + Use asynchronous GPU emulation Usar emulación asíncrona de GPU - + Accelerate ASTC texture decoding Acelerar decodificación de texturas ASTC - + NVDEC emulation: Emulación NVDEC: - + No Video Output Sin salida de vídeo - + CPU Video Decoding Decodificación de vídeo en la CPU - + GPU Video Decoding (Default) Decodificación de vídeo en GPU (Por defecto) - + Fullscreen Mode: Modo pantalla completa: - + Borderless Windowed Ventana sin bordes - + Exclusive Fullscreen Pantalla completa - + Aspect Ratio: Relación de aspecto: - + Default (16:9) Valor predeterminado (16:9) - + Force 4:3 Forzar a 4:3 - + Force 21:9 Forzar a 21:9 - + + Force 16:10 + Forzar 16:10 + + + Stretch to Window Ajustar a la ventana - + Resolution: Resolución: - + 0.5X (360p/540p) [EXPERIMENTAL] x0.5 (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] x0.75 (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) x1 (720p/1080p) - + 2X (1440p/2160p) x2 (1440p/2160p) - + 3X (2160p/3240p) x3 (2160p/3240p) - + 4X (2880p/4320p) x4 (2880p/4320p) - + 5X (3600p/5400p) x5 (3600p/5400p) - + 6X (4320p/6480p) x6 (4320p/6480p) - + Window Adapting Filter: Filtro adaptable de ventana: - + Nearest Neighbor Vecino más próximo - + Bilinear Bilineal - + Bicubic Bicúbico - + Gaussian Gaussiano - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (Solo Vulkan) - + Anti-Aliasing Method: Método de Anti-Aliasing: - + None Ninguno - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Usar el color de fondo global - + Set background color: Establecer el color de fondo: - + Background Color: Color de fondo: @@ -1529,6 +1629,12 @@ Esto banearía su nombre del foro y su dirección IP. GLASM (Assembly Shaders, NVIDIA Only) GLASM (Assembly shaders, sólo NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1583,37 +1689,47 @@ Esto banearía su nombre del foro y su dirección IP. Usar tiempo rápido en la GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + Activa el flujo de búferes pesado. Esta opción forzará el flujo de los búferes no modificados, lo que puede afectar al rendimiento. + + + + Use pessimistic buffer flushes (Hack) + Utilizar flujos de búferes pesados (Hack) + + + Anisotropic Filtering: Filtrado anisotrópico: - + Automatic Automático - + Default Valor predeterminado - + 2x x2 - + 4x x4 - + 8x x8 - + 16x x16 @@ -2105,7 +2221,7 @@ Esto banearía su nombre del foro y su dirección IP. - + Left Stick Palanca izquierda @@ -2199,14 +2315,14 @@ Esto banearía su nombre del foro y su dirección IP. - + L L - + ZL ZL @@ -2225,7 +2341,7 @@ Esto banearía su nombre del foro y su dirección IP. - + Plus Más @@ -2238,15 +2354,15 @@ Esto banearía su nombre del foro y su dirección IP. - + R R - + ZR ZR @@ -2303,231 +2419,236 @@ Esto banearía su nombre del foro y su dirección IP. - + Right Stick Palanca derecha - - - - + + + + Clear Borrar - - - - - + + + + + [not set] [no definido] - - - Toggle button - Alternar botón - - - - + + Invert button Invertir botón - - + + + Toggle button + Alternar botón + + + + Invert axis Invertir ejes - - - + + + Set threshold Configurar umbral - - + + Choose a value between 0% and 100% Seleccione un valor entre 0% y 100%. - + + Toggle axis + Alternar ejes + + + Set gyro threshold Configurar umbral del Giroscopio - + Map Analog Stick Configuración de palanca analógico - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Después de pulsar OK, mueve primero el joystick de manera horizontal, y luego verticalmente. Para invertir los ejes, mueve primero el joystick de manera vertical, y luego horizontalmente. - + Center axis Centrar ejes - - + + Deadzone: %1% Punto muerto: %1% - - + + Modifier Range: %1% Rango del modificador: %1% - - + + Pro Controller Controlador Pro - + Dual Joycons Joycons duales - + Left Joycon Joycon izquierdo - + Right Joycon Joycon derecho - + Handheld Portátil - + GameCube Controller Controlador de GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Controlador NES - + SNES Controller Controlador SNES - + N64 Controller Controlador N64 - + Sega Genesis Sega Genesis - + Start / Pause Inicio / Pausa - + Z Z - + Control Stick Palanca de control - + C-Stick C-Stick - + Shake! ¡Agita! - + [waiting] [esperando] - + New Profile Nuevo perfil - + Enter a profile name: Introduce un nombre de perfil: - - + + Create Input Profile Crear perfil de entrada - + The given profile name is not valid! ¡El nombre de perfil introducido no es válido! - + Failed to create the input profile "%1" Error al crear el perfil de entrada "%1" - + Delete Input Profile Eliminar perfil de entrada - + Failed to delete the input profile "%1" Error al eliminar el perfil de entrada "%1" - + Load Input Profile Cargar perfil de entrada - + Failed to load the input profile "%1" Error al cargar el perfil de entrada "%1" - + Save Input Profile Guardar perfil de entrada - + Failed to save the input profile "%1" Error al guardar el perfil de entrada "%1" @@ -2782,42 +2903,42 @@ Para invertir los ejes, mueve primero el joystick de manera vertical, y luego ho Desarrollador - + Add-Ons Extras / Add-Ons - + General General - + System Sistema - + CPU CPU - + Graphics Gráficos - + Adv. Graphics Gráficos avanz. - + Audio Audio - + Properties Propiedades @@ -2873,37 +2994,37 @@ Para invertir los ejes, mueve primero el joystick de manera vertical, y luego ho Usuario actual - + Username Nombre de usuario - + Set Image Seleccionar imagen - + Add Añadir - + Rename Renombrar - + Remove Eliminar - + Profile management is available only when game is not running. El sistema de perfiles sólo se encuentra disponible cuando no se esté ejecutando ningún juego. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2911,96 +3032,106 @@ Para invertir los ejes, mueve primero el joystick de manera vertical, y luego ho %2 - + Enter Username Introduzca el nombre - + Users Usuarios - + Enter a username for the new user: Introduce un nombre para el nuevo usuario: - + Enter a new username: Introduce un nuevo nombre de usuario: - - Confirm Delete - Confirmar eliminación - - - - You are about to delete user with name "%1". Are you sure? - Estás a punto de eliminar al usuario "%1" ¿Estás seguro? - - - + Select User Image Selecciona una imagen de usuario - + JPEG Images (*.jpg *.jpeg) Imagenes JPEG (*.jpg *.jpeg) - + Error deleting image Error al eliminar la imagen - + Error occurred attempting to overwrite previous image at: %1. Ha ocurrido un error al intentar sobrescribir la imagen anterior en: %1. - + Error deleting file Error al eliminar el archivo - + Unable to delete existing file: %1. No se puede eliminar el archivo existente: %1. - + Error creating user image directory Error al crear el directorio de imagen del usuario - + Unable to create directory %1 for storing user images. No se puede crear el directorio %1 para almacenar imágenes de usuario. - + Error copying user image Error al copiar la imagen de usuario. - + Unable to copy image from %1 to %2 No se puede copiar la imagen de %1 a %2 - + Error resizing user image Error al redimensionar la imagen de usuario - + Unable to resize image No se puede cambiar el tamaño de la imagen + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + ¿Eliminar este usuario? Todos los datos de guardado del usuario serán eliminados. + + + + Confirm Delete + Confirmar eliminación + + + + Name: %1 +UUID: %2 + Nombre: %1 +UUID: %2 + + ConfigureRingController @@ -3529,47 +3660,47 @@ Para invertir los ejes, mueve primero el joystick de manera vertical, y luego ho <html><head/><body><p>Lee la entrada de los controles de los scripts en el mismo formato que los scripts TAS-nx.<br/>Para una mejor explicación, consulta la<a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">página de ayuda</span></a> en la página web de yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Para comprobar qué teclas de acceso rápido controlan la reproducción/grabación, por favor, revisa la configuración de las Teclas de acceso rápido (Configuración -> General -> Teclas de acceso rápido) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. AVISO: Esto es una característica experimental.<br/>No se reproducirán los scripts perfectamente con el método actual e imperfecto de sincronización. - + Settings Ajustes - + Enable TAS features Activar características TAS - + Loop script Repetir script en bucle - + Pause execution during loads Pausar ejecución durante las cargas - + Script Directory Directorio de scripts - + Path Ruta - + ... ... @@ -3821,56 +3952,71 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de + Show Compatibility List + Mostrar lista de compatibilidad + + + Show Add-Ons Column Mostrar columna de extras/Add-Ons - + + Show Size Column + Mostrar columna de tamaño + + + + Show File Types Column + Mostrar columna de tipos de archivo + + + Game Icon Size: Tamaño de los iconos de los juegos: - + Folder Icon Size: Tamaño de los iconos de la carpeta: - + Row 1 Text: Texto de fila 1: - + Row 2 Text: Texto de fila 2: - + Screenshots Capturas de pantalla - + Ask Where To Save Screenshots (Windows Only) Preguntar dónde guardar las capturas de pantalla (sólo en Windows) - + Screenshots Path: Ruta de las capturas de pantalla: - + ... ... - + Select Screenshots Path... Selecciona la ruta de las capturas de pantalla: - + <System> <System> @@ -3979,7 +4125,7 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de - + Verify Verificar @@ -4016,7 +4162,7 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de Share anonymous usage data with the yuzu team - Compartir datos de uso anónimos con el equipo de yuzu + Compartir datos de uso anónimo con el equipo de yuzu @@ -4066,7 +4212,7 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de - + Unspecified Sin especificar @@ -4081,17 +4227,36 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de El token no se puede verificar. Los cambios realizados en tu token no se ha guardado. - + + Unverified, please click Verify before saving configuration + Tooltip + No verificado, por favor, haz clic en Verificar antes de guardar los ajustes. + + + + Verifying... Verificando... - + + Verified + Tooltip + Verificado + + + + Verification failed + Tooltip + Error de verificación + + + Verification failed Error de verificación - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Error de verificación. Comprueba que has introducido el token correctamente, y que esté funcionando correctamente tu conexión a internet. @@ -4160,12 +4325,12 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de DirectConnectWindow - + Connecting Conectando - + Connect Conectar @@ -4173,488 +4338,491 @@ Arrastra los puntos para cambiar de posición, o haz doble clic en las celdas de GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Los datos de uso anónimos se recogen</a> para ayudar a mejorar yuzu. <br/><br/>¿Deseas compartir tus datos de uso con nosotros? - + Telemetry Telemetría - + Broken Vulkan Installation Detected Se ha detectado una instalación corrupta de Vulkan - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. La inicialización de Vulkan ha fallado durante la ejecución. Haz clic <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>aquí para más información sobre como arreglar el problema</a>. - + Loading Web Applet... Cargando Web applet... - - + + Disable Web Applet Desactivar Web applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Deshabilitar el Applet Web puede causar comportamientos imprevistos y debería solo ser usado con Super Mario 3D All-Stars. ¿Estas seguro que quieres deshabilitar el Applet Web? (Puede ser reactivado en las configuraciones de Depuración.) - + The amount of shaders currently being built La cantidad de shaders que se están construyendo actualmente - + The current selected resolution scaling multiplier. El multiplicador de escala de resolución seleccionado actualmente. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. La velocidad de emulación actual. Los valores superiores o inferiores al 100% indican que la emulación se está ejecutando más rápido o más lento que en una Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. La cantidad de fotogramas por segundo que se está mostrando el juego actualmente. Esto variará de un juego a otro y de una escena a otra. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tiempo que lleva emular un fotograma de la Switch, sin tener en cuenta la limitación de fotogramas o sincronización vertical. Para una emulación óptima, este valor debería ser como máximo de 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Eliminar archivos recientes - + &Continue &Continuar - + &Pause &Pausar - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu está ejecutando un juego - + Warning Outdated Game Format Advertencia: formato del juego obsoleto - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Está utilizando el formato de directorio de ROM deconstruido para este juego, que es un formato desactualizado que ha sido reemplazado por otros, como los NCA, NAX, XCI o NSP. Los directorios de ROM deconstruidos carecen de íconos, metadatos y soporte de actualizaciones.<br><br>Para ver una explicación de los diversos formatos de Switch que soporta yuzu,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>echa un vistazo a nuestra wiki</a>. Este mensaje no se volverá a mostrar. - - + + Error while loading ROM! ¡Error al cargar la ROM! - + The ROM format is not supported. El formato de la ROM no es compatible. - + An error occurred initializing the video core. Se ha producido un error al inicializar el núcleo de video. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu ha encontrado un error al ejecutar el núcleo de video. Esto suele ocurrir al no tener los controladores de la GPU actualizados, incluyendo los integrados. Por favor, revisa el registro para más detalles. Para más información sobre cómo acceder al registro, por favor, consulta la siguiente página: <a href='https://yuzu-emu.org/help/reference/log-files/'>Como cargar el archivo de registro</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. ¡Error al cargar la ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Por favor, sigue <a href='https://yuzu-emu.org/help/quickstart/'>la guía de inicio rápido de yuzu</a> para revolcar los archivos.<br>Puedes consultar la wiki de yuzu</a> o el Discord de yuzu</a> para obtener ayuda. - + An unknown error occurred. Please see the log for more details. Error desconocido. Por favor, consulte el archivo de registro para ver más detalles. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Datos de guardado - + Mod Data Datos de mods - + Error Opening %1 Folder Error al abrir la carpeta %1 - - + + Folder does not exist! ¡La carpeta no existe! - + Error Opening Transferable Shader Cache Error al abrir el caché transferible de shaders - + Failed to create the shader cache directory for this title. No se pudo crear el directorio de la caché de los shaders para este título. - - Contents - Contenidos + + Error Removing Contents + Error al eliminar el contenido - - Update - Actualización + + Error Removing Update + Error al eliminar la actualización - - DLC - DLC + + Error Removing DLC + Error al eliminar el DLC - + + Remove Installed Game Contents? + ¿Eliminar el contenido del juego instalado? + + + + Remove Installed Game Update? + ¿Eliminar la actualización del juego instalado? + + + + Remove Installed Game DLC? + ¿Eliminar el DLC del juego instalado? + + + Remove Entry Eliminar entrada - - Remove Installed Game %1? - ¿Eliminar el juego instalado %1? - - - - - - - - + + + + + + Successfully Removed Se ha eliminado con éxito - + Successfully removed the installed base game. Se ha eliminado con éxito el juego base instalado. - - - - Error Removing %1 - Error al eliminar %1 - - - + The base game is not installed in the NAND and cannot be removed. El juego base no está instalado en el NAND y no se puede eliminar. - + Successfully removed the installed update. Se ha eliminado con éxito la actualización instalada. - + There is no update installed for this title. No hay ninguna actualización instalada para este título. - + There are no DLC installed for this title. No hay ningún DLC instalado para este título. - + Successfully removed %1 installed DLC. Se ha eliminado con éxito %1 DLC instalado(s). - + Delete OpenGL Transferable Shader Cache? ¿Deseas eliminar el caché transferible de shaders de OpenGL? - + Delete Vulkan Transferable Shader Cache? ¿Deseas eliminar el caché transferible de shaders de Vulkan? - + Delete All Transferable Shader Caches? ¿Deseas eliminar todo el caché transferible de shaders? - + Remove Custom Game Configuration? ¿Deseas eliminar la configuración personalizada del juego? - + Remove File Eliminar archivo - - + + Error Removing Transferable Shader Cache Error al eliminar la caché de shaders transferibles - - + + A shader cache for this title does not exist. No existe caché de shaders para este título. - + Successfully removed the transferable shader cache. El caché de shaders transferibles se ha eliminado con éxito. - + Failed to remove the transferable shader cache. No se ha podido eliminar la caché de shaders transferibles. - - + + Error Removing Transferable Shader Caches Error al eliminar las cachés de shaders transferibles - + Successfully removed the transferable shader caches. Cachés de shaders transferibles eliminadas con éxito. - + Failed to remove the transferable shader cache directory. No se ha podido eliminar el directorio de cachés de shaders transferibles. - - + + Error Removing Custom Configuration Error al eliminar la configuración personalizada del juego - + A custom configuration for this title does not exist. No existe una configuración personalizada para este título. - + Successfully removed the custom game configuration. Se eliminó con éxito la configuración personalizada del juego. - + Failed to remove the custom game configuration. No se ha podido eliminar la configuración personalizada del juego. - - + + RomFS Extraction Failed! ¡La extracción de RomFS ha fallado! - + There was an error copying the RomFS files or the user cancelled the operation. Se ha producido un error al copiar los archivos RomFS o el usuario ha cancelado la operación. - + Full Completo - + Skeleton - Esquema + En secciones - + Select RomFS Dump Mode Elegir método de volcado de RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - Seleccione la forma en que quieras volcar el RomFS. <br>Copiará todos los archivos en el nuevo directorio <br> mientras que el esqueleto solo creará la estructura del directorio. + Por favor, selecciona el método en que quieres volcar el RomFS.<br>Completo copiará todos los archivos al nuevo directorio <br> mientras que en secciones solo creará la estructura del directorio. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root No hay suficiente espacio en %1 para extraer el RomFS. Por favor, libera espacio o elige otro directorio de volcado en Emulación > Configuración > Sistema > Sistema de archivos > Raíz de volcado - + Extracting RomFS... Extrayendo RomFS... - - + + Cancel Cancelar - + RomFS Extraction Succeeded! ¡La extracción RomFS ha tenido éxito! - + The operation completed successfully. La operación se completó con éxito. - + Error Opening %1 Error al intentar abrir %1 - + Select Directory Seleccionar directorio - + Properties Propiedades - + The game properties could not be loaded. No se pueden cargar las propiedades del juego. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Ejecutable de Switch (%1);;Todos los archivos (*.*) - + Load File Cargar archivo - + Open Extracted ROM Directory Abrir el directorio de la ROM extraída - + Invalid Directory Selected Directorio seleccionado no válido - + The directory you have selected does not contain a 'main' file. El directorio que ha seleccionado no contiene ningún archivo 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Archivo de Switch Instalable (*.nca *.nsp *.xci);;Archivo de contenidos de Nintendo (*.nca);;Paquete de envío de Nintendo (*.nsp);;Imagen de cartucho NX (*.xci) - + Install Files Instalar archivos - + %n file(s) remaining %n archivo(s) restantes%n archivo(s) restantes%n archivo(s) restantes - + Installing file "%1"... Instalando el archivo "%1"... - - + + Install Results Instalar resultados - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - Para evitar posibles conflictos, no recomendamos a los usuarios que instalen juegos base en el NAND. + Para evitar posibles conflictos, no se recomienda a los usuarios que instalen juegos base en el NAND. Por favor, utiliza esta función sólo para instalar actualizaciones y DLCs. - + %n file(s) were newly installed %n archivo(s) recién instalado/s @@ -4663,7 +4831,7 @@ Por favor, utiliza esta función sólo para instalar actualizaciones y DLCs. - + %n file(s) were overwritten %n archivo(s) recién sobreescrito/s @@ -4672,7 +4840,7 @@ Por favor, utiliza esta función sólo para instalar actualizaciones y DLCs. - + %n file(s) failed to install %n archivo(s) no se instaló/instalaron @@ -4681,411 +4849,410 @@ Por favor, utiliza esta función sólo para instalar actualizaciones y DLCs. - + System Application Aplicación del sistema - + System Archive Archivo del sistema - + System Application Update Actualización de la aplicación del sistema - + Firmware Package (Type A) Paquete de firmware (Tipo A) - + Firmware Package (Type B) Paquete de firmware (Tipo B) - + Game Juego - + Game Update Actualización de juego - + Game DLC DLC del juego - + Delta Title Titulo delta - + Select NCA Install Type... Seleccione el tipo de instalación NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Seleccione el tipo de título en el que deseas instalar este NCA como: (En la mayoría de los casos, el 'Juego' predeterminado está bien). - + Failed to Install Fallo en la instalación - + The title type you selected for the NCA is invalid. El tipo de título que seleccionó para el NCA no es válido. - + File not found Archivo no encontrado - + File "%1" not found Archivo "%1" no encontrado - + OK Aceptar - + + Hardware requirements not met + No se cumplen los requisitos de hardware + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + El sistema no cumple los requisitos de hardware recomendados. Los informes de compatibilidad se han desactivado. + + + Missing yuzu Account Falta la cuenta de Yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Para enviar un caso de prueba de compatibilidad de juegos, debes vincular tu cuenta de yuzu.<br><br/> Para vincular tu cuenta de yuzu, ve a Emulación &gt; Configuración &gt; Web. - + Error opening URL Error al abrir la URL - + Unable to open the URL "%1". No se puede abrir la URL "%1". - + TAS Recording Grabación TAS - + Overwrite file of player 1? ¿Sobrescribir archivo del jugador 1? - + Invalid config detected Configuración no válida detectada - + Handheld controller can't be used on docked mode. Pro controller will be selected. El controlador del modo portátil no puede ser usado en el modo sobremesa. Se seleccionará el controlador Pro en su lugar. - - - Error - Error - - - - - The current game is not looking for amiibos - El juego actual no está buscando amiibos - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed El amiibo actual ha sido eliminado - + + Error + Error + + + + + The current game is not looking for amiibos + El juego actual no está buscando amiibos + + + Amiibo File (%1);; All Files (*.*) Archivo amiibo (%1);; Todos los archivos (*.*) - + Load Amiibo Cargar amiibo - - Error opening Amiibo data file - Error al abrir el archivo de datos amiibo - - - - Unable to open Amiibo file "%1" for reading. - No se puede abrir el archivo amiibo "%1" para leer. - - - - Error reading Amiibo data file - Error al leer el archivo de datos amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - No se puede leer completamente los datos Amiibo. Se esperaban leer %1 bytes, pero solo se puede leer %2 bytes. - - - + Error loading Amiibo data Error al cargar los datos Amiibo - - Unable to load Amiibo data. - No se pueden cargar los datos Amiibo. + + The selected file is not a valid amiibo + El archivo seleccionado no es un amiibo válido - + + The selected file is already on use + El archivo seleccionado ya se encuentra en uso + + + + An unknown error occurred + Ha ocurrido un error inesperado + + + Capture Screenshot Captura de pantalla - + PNG Image (*.png) Imagen PNG (*.png) - + TAS state: Running %1/%2 Estado TAS: ejecutando %1/%2 - + TAS state: Recording %1 Estado TAS: grabando %1 - + TAS state: Idle %1/%2 Estado TAS: inactivo %1/%2 - + TAS State: Invalid Estado TAS: nulo - + &Stop Running &Parar de ejecutar - + &Start &Iniciar - + Stop R&ecording Pausar g&rabación - + R&ecord G&rabar - + Building: %n shader(s) Creando: %n shader(s)Construyendo: %n shader(s)Construyendo: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Escalado: %1x - + Speed: %1% / %2% Velocidad: %1% / %2% - + Speed: %1% Velocidad: %1% - + Game: %1 FPS (Unlocked) Juego: %1 FPS (desbloqueado) - + Game: %1 FPS Juego: %1 FPS - + Frame: %1 ms Fotogramas: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU ALTA - + GPU EXTREME GPU EXTREMA - + GPU ERROR GPU ERROR - + DOCKED SOBREMESA - + HANDHELD PORTÁTIL - + NEAREST - PROXIMAL + PRÓXIMO - - + + BILINEAR BILINEAL - + BICUBIC BICÚBICO - + GAUSSIAN GAUSSIANO - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA NO AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. El juego que estás intentando cargar requiere archivos adicionales de tu Switch antes de poder jugar. <br/><br/>Para obtener más información sobre cómo obtener estos archivos, ve a la siguiente página de la wiki: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Volcar archivos del sistema y las fuentes compartidas desde una Consola Switch. </a>.<br/><br/>¿Quieres volver a la lista de juegos? Continuar con la emulación puede provocar fallos, datos de guardado corrompidos u otros errores. - + yuzu was unable to locate a Switch system archive. %1 yuzu no pudo localizar el archivo de sistema de la Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu no pudo localizar un archivo de sistema de la Switch: %1. %2 - + System Archive Not Found Archivo del sistema no encontrado - + System Archive Missing Faltan archivos del sistema - + yuzu was unable to locate the Switch shared fonts. %1 yuzu no pudo encontrar las fuentes compartidas de la Switch. %1 - + Shared Fonts Not Found Fuentes compartidas no encontradas - + Shared Font Missing Faltan fuentes compartidas - + Fatal Error Error fatal - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu ha encontrado un error fatal, consulta el registro para obtener más detalles. Para obtener más información sobre cómo acceder al registro, consulta la siguiente página: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>¿Cómo cargar el archivo de registro?</a>.<br/><br/> Continuar con la emulación puede provocar fallos, datos de guardado corruptos u otros errores. - + Fatal Error encountered Error fatal encontrado - + Confirm Key Rederivation Confirmar la clave de rederivación - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5102,37 +5269,37 @@ es lo que quieres hacer si es necesario. Esto eliminará los archivos de las claves generadas automáticamente y volverá a ejecutar el módulo de derivación de claves. - + Missing fuses Faltan fuses - + - Missing BOOT0 - Falta BOOT0 - + - Missing BCPKG2-1-Normal-Main - Falta BCPKG2-1-Normal-Main - + - Missing PRODINFO - Falta PRODINFO - + Derivation Components Missing Faltan componentes de derivación - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Faltan las claves de encriptación. <br>Por favor, sigue <a href='https://yuzu-emu.org/help/quickstart/'>la guía rápida de yuzu</a> para obtener todas tus claves, firmware y juegos.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5141,39 +5308,39 @@ Esto puede llevar unos minutos dependiendo del rendimiento de su sistema. - + Deriving Keys Obtención de claves - + Select RomFS Dump Target Selecciona el destinatario para volcar el RomFS - + Please select which RomFS you would like to dump. Por favor, seleccione los RomFS que deseas volcar. - + Are you sure you want to close yuzu? ¿Estás seguro de que quieres cerrar yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. ¿Estás seguro de que quieres detener la emulación? Cualquier progreso no guardado se perderá. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5185,38 +5352,38 @@ Would you like to bypass this and exit anyway? GRenderWindow - + OpenGL not available! ¡OpenGL no está disponible! - + yuzu has not been compiled with OpenGL support. yuzu no ha sido compilado con soporte de OpenGL. - - + + Error while initializing OpenGL! ¡Error al inicializar OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Tu GPU no soporta OpenGL, o no tienes instalados los últimos controladores gráficos. - + Error while initializing OpenGL 4.6! ¡Error al iniciar OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Tu GPU no soporta OpenGL 4.6, o no tienes instalado el último controlador de la tarjeta gráfica.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Es posible que la GPU no soporte una o más extensiones necesarias de OpenGL . Por favor, asegúrate de tener los últimos controladores de la tarjeta gráfica.<br><br>GL Renderer:<br>%1<br><br>Extensiones no soportadas:<br>%2 @@ -5224,153 +5391,153 @@ Would you like to bypass this and exit anyway? GameList - + Favorite Favorito - + Start Game Iniciar juego - + Start Game without Custom Configuration Iniciar juego sin la configuración personalizada - + Open Save Data Location Abrir ubicación de los archivos de guardado - + Open Mod Data Location Abrir ubicación de los mods - + Open Transferable Pipeline Cache Abrir caché transferible de shaders en tubería - + Remove Eliminar - + Remove Installed Update Eliminar la actualización instalada - + Remove All Installed DLC Eliminar todos los DLC instalados - + Remove Custom Configuration Eliminar la configuración personalizada - + Remove OpenGL Pipeline Cache Eliminar caché en tubería de OpenGL - + Remove Vulkan Pipeline Cache Eliminar caché en tubería de Vulkan - + Remove All Pipeline Caches Eliminar todas las cachés en tubería - + Remove All Installed Contents Eliminar todo el contenido instalado - + Dump RomFS Volcar RomFS - + Dump RomFS to SDMC Volcar RomFS a SDMC - + Copy Title ID to Clipboard Copiar la ID del título al portapapeles - + Navigate to GameDB entry Ir a la sección de bases de datos del juego - + Properties Propiedades - + Scan Subfolders Escanear subdirectorios - + Remove Game Directory Eliminar directorio de juegos - + ▲ Move Up ▲ Mover hacia arriba - + ▼ Move Down ▼ Mover hacia abajo - + Open Directory Location Abrir ubicación del directorio - + Clear Limpiar - + Name Nombre - + Compatibility Compatibilidad - + Add-ons Extras/Add-ons - + File type Tipo de archivo - + Size Tamaño @@ -5379,89 +5546,69 @@ Would you like to bypass this and exit anyway? GameListItemCompat - Perfect - Perfecto + Ingame + Inicia - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - El juego funciona a la perfección sin fallos de audio o gráficos, todas las funciones probadas funcionan según lo previsto -sin ninguna solución necesaria. - - - - Great - Excelente - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - El juego funciona con fallos gráficos o de audio menores y es jugable de principio a fin. Puede que sea necesario -recurrir a arreglos temporales. + Game starts, but crashes or major glitches prevent it from being completed. + El juego se inicia, pero los bloqueos o fallos importantes impiden que se pueda completar. - Okay - Bien + Perfect + Perfecta - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - El juego funciona con fallos gráficos o de audio sustanciales, pero el juego es jugable de principio a fin con -arreglos temporales. + Game can be played without issues. + El juego se puede jugar sin problemas. - Bad - Mal + Playable + Jugable - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - El juego funciona, pero tiene errores gráficos o de audio. Es imposible avanzar en ciertas zonas debido a fallos -incluso con arreglos temporales. + Game functions with minor graphical or audio glitches and is playable from start to finish. + El juego funciona con pequeños errores gráficos o de sonido y es jugable de principio a fin. - + Intro/Menu - Intro/Menu + Inicio/Menu - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - No es posible jugar a este juego debido a errores gráficos o de audio importantes. Es imposible avanzar mas allá de la pantalla -de inicio. + + Game loads, but is unable to progress past the Start Screen. + El juego se ejecuta, pero no puede pasar de la pantalla de inicio. - + Won't Boot - No inicia + No funciona - + The game crashes when attempting to startup. El juego se bloquea al intentar iniciar. - + Not Tested - Sin probar + Sin testear - + The game has not yet been tested. - El juego todavía no ha sido probado. + El juego todavía no ha sido testeado todavía. GameListPlaceholder - + Double-click to add a new folder to the game list Haz doble clic para agregar un nuevo directorio a la lista de juegos. @@ -5474,12 +5621,12 @@ de inicio. %1 de %n resultado(s)%1 de %n resultado(s)%1 de %n resultado(s) - + Filter: Búsqueda: - + Enter pattern to filter Introduce un patrón para buscar @@ -5514,7 +5661,7 @@ de inicio. (Leave blank for open game) - (Dejar en blanco para cualquier juego) + (Dejar en blanco para juego libre) @@ -5549,32 +5696,33 @@ de inicio. Host Room - Sala del anfitrión + Crear sala HostRoomWindow - + Error Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - Error al anunciar la sala al lobby público. Para poder publicar una sala en el lobby público, debe tener una cuenta válida de yuzu configurada en Emulación -> Configurar -> Web. Si no quieres publicar una sala en el lobby público, seleccione en su lugar Privada. + Error al publicar la sala al lobby público. Para poder publicar una sala en el lobby público, debes tener una cuenta válida de yuzu configurada en Emulación -> Configurar -> Web. Si no quieres publicar una sala en el lobby público, seleccione en su lugar "Privada". Mensaje de depuración: Hotkeys - + Audio Mute/Unmute Activar/Desactivar audio + @@ -5596,112 +5744,111 @@ Mensaje de depuración: - Main Window Ventana principal - + Audio Volume Down Bajar volumen del audio - + Audio Volume Up Subir volumen del audio - + Capture Screenshot Captura de pantalla - + Change Adapting Filter Cambiar filtro adaptable - + Change Docked Mode Cambiar a modo sobremesa - + Change GPU Accuracy Cambiar precisión de GPU - + Continue/Pause Emulation Continuar/Pausar emulación - + Exit Fullscreen Salir de pantalla completa - + Exit yuzu Cerrar yuzu - + Fullscreen Pantalla completa - + Load File Cargar archivo - + Load/Remove Amiibo Cargar/Eliminar Amiibo - + Restart Emulation Reiniciar emulación - + Stop Emulation Detener emulación - + TAS Record Grabar TAS - + TAS Reset Reiniciar TAS - + TAS Start/Stop Iniciar/detener TAS - + Toggle Filter Bar Alternar barra de filtro - + Toggle Framerate Limit Alternar limite de fotogramas - + Toggle Mouse Panning Alternar desplazamiento del ratón - + Toggle Status Bar Alternar barra de estado @@ -5716,7 +5863,7 @@ Mensaje de depuración: Installing an Update or DLC will overwrite the previously installed one. - Instalar una actualización o DLC reemplazará la que se instaló previamente. + Instalar una actualización o DLC reemplazará la instalada previamente. @@ -5816,42 +5963,42 @@ Mensaje de depuración: Actualizar lobby - + Password Required to Join Contraseña necesaria para unirse - + Password: Contraseña: - - Room Name - Nombre de la sala - - - - Preferred Game - Juego preferente - - - - Host - Anfitrión - - - + Players Jugadores - + + Room Name + Nombre de sala + + + + Preferred Game + Juego preferente + + + + Host + Anfitrión + + + Refreshing Actualizando - + Refresh List Actualizar lista @@ -5874,232 +6021,237 @@ Mensaje de depuración: &Archivos recientes - + &Emulation &Emulación - + &View &Ver - + &Reset Window Size &Reiniciar tamaño de ventana - + &Debugging &Depuración - + Reset Window Size to &720p Reiniciar el tamaño de la ventana a &720p - + Reset Window Size to 720p Reiniciar el tamaño de la ventana a 720p - + Reset Window Size to &900p Reiniciar el tamaño de la ventana a &900p - + Reset Window Size to 900p Reiniciar el tamaño de la ventana a 900p - + Reset Window Size to &1080p Reiniciar el tamaño de la ventana a &1080p - + Reset Window Size to 1080p Reiniciar el tamaño de la ventana a 1080p - + + &Multiplayer + &Multijugador + + + &Tools &Herramientas - + &TAS &TAS - + &Help &Ayuda - + &Install Files to NAND... &Instalar archivos en NAND... - + L&oad File... C&argar archivo... - + Load &Folder... Cargar &carpeta - + E&xit S&alir - + &Pause &Pausar - + &Stop &Detener - + &Reinitialize keys... &Reiniciar claves... - + &About yuzu &Acerca de yuzu - + Single &Window Mode Modo &ventana - + Con&figure... Con&figurar... - + Display D&ock Widget Headers Mostrar complementos de cabecera del D&ock - + Show &Filter Bar Mostrar barra de &búsqueda - + Show &Status Bar Mostrar barra de &estado - + Show Status Bar Mostrar barra de estado - - - Browse Public Game Lobby - Buscar en el lobby de juegos públicos - - - - Create Room - Crear sala - - Leave Room - Abandonar la sala + &Browse Public Game Lobby + &Buscar en el lobby de juegos públicos - - Direct Connect to Room - Conexión directa a la sala + + &Create Room + &Crear sala - - Show Current Room - Mostrar sala actual + + &Leave Room + &Abandonar sala + &Direct Connect to Room + &Conexión directa a la sala + + + + &Show Current Room + &Mostrar sala actual + + + F&ullscreen P&antalla completa - + &Restart &Reiniciar - + Load/Remove &Amiibo... Cargar/Eliminar &Amiibo... - + &Report Compatibility &Reporte de compatibilidad - + Open &Mods Page Abrir página de &mods - + Open &Quickstart Guide Abrir guía de &inicio rápido - + &FAQ &Preguntas frecuentes - + Open &yuzu Folder Abrir la carpeta de &yuzu - + &Capture Screenshot &Captura de pantalla - + &Configure TAS... &Configurar TAS... - + Configure C&urrent Game... Configurar j&uego actual... - + &Start &Iniciar - + &Reset &Reiniciar - + R&ecord G&rabar @@ -6133,7 +6285,7 @@ Mensaje de depuración: Unban - Desbanear + Quitar veto @@ -6164,47 +6316,42 @@ Mensaje de depuración: MultiplayerState - - + Current connection status Estado de la conexión actual - - + Not Connected. Click here to find a room! No conectado. Haz clic aquí para encontrar una sala. - - - Connected - Conectado - - - - Not Connected No conectado - + + Connected + Conectado + + + + New Messages Received + Nuevos mensajes recibidos + + + Error Error - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: No se ha podido actualizar la información de la sala. Por favor, comprueba tu conexión a internet e intenta alojar la sala de nuevo. Mensaje de depuración: - - - New Messages Received - Nuevos mensajes recibidos - NetworkMessage @@ -6246,42 +6393,42 @@ Mensaje de depuración: Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded. - + No se ha podido conectar con el anfitrión. Comprueba que la configuración de la conexión es correcta. Si todavía no puedes conectarte, contacta con el anfitrión de la sala y verifica que el anfitrión tiene configurado correctamente el puerto externo direccionado. Unable to connect to the room because it is already full. - + No es posible conectarse a la sala debido a que ya se encuentra llena. Creating a room failed. Please retry. Restarting yuzu might be necessary. - + La creación de la sala ha fallado. Por favor reintente. Reiniciar yuzu puede ser necesario. The host of the room has banned you. Speak with the host to unban you or try a different room. - + El anfitrión de la sala te ha vetado. Habla con el anfitrión para quitar el veto o prueba con una sala diferente. Version mismatch! Please update to the latest version of yuzu. If the problem persists, contact the room host and ask them to update the server. - + ¡No coinciden las versiones! Por favor, actualiza a la última versión de yuzu. Si el problema persiste, ponte en contacto con el anfitrión de la sala y pídele que actualice el servidor. Incorrect password. - + Contraseña incorrecta An unknown error occurred. If this error continues to occur, please open an issue - + Ha ocurrido un error desconocido. Si el error persiste, por favor, abre una solicitud de errores. Connection to room lost. Try to reconnect. - + Conexión a la sala perdida. Intenta reconectarte. @@ -6291,44 +6438,64 @@ Mensaje de depuración: IP address is already in use. Please choose another. - + La dirección IP ya se encuentra en uso. Por favor, selecciona otra. You do not have enough permission to perform this action. - + No tienes permisos suficientes para realizar esta acción. The user you are trying to kick/ban could not be found. They may have left the room. - + El usuario que estás intentando echar/vetar no se ha podido encontrar. +Es posible que haya abandonado la sala. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + No se ha seleccionado ninguna interfaz de red válida. +Por favor, vaya a Configuración -> Sistema -> Red y selecciona la interfaz. + + + + Game already running + El juego ya se está ejecutando + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + No se recomienda unirse a una sala cuando el juego se está ejecutando ya que puede provocar que la funcionalidad de la sala no funcione correctamente. +¿Proceder de todos modos? + + + Leave Room Salir de la sala - + You are about to close the room. Any network connections will be closed. - + Estás a punto de abandonar la sala. Las conexiones de red serán interrumpidas. - + Disconnect - + Desconectar - + You are about to leave the room. Any network connections will be closed. - + Estás a punto de abandonar la sala. Las conexiones de red serán interrumpidas. NetworkMessage::ErrorManager - + Error Error @@ -6377,42 +6544,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 no está jugando ningún juego - + %1 is playing %2 - + %1 esta jugando %2 - + Not playing a game - + No jugando ningún juego - + Installed SD Titles Títulos instalados en la SD - + Installed NAND Titles Títulos instalados en NAND - + System Titles Títulos del sistema - + Add New Game Directory Añadir un nuevo directorio de juegos - + Favorites Favoritos @@ -6442,7 +6609,7 @@ p, li { white-space: pre-wrap; } - + [not set] [no definido] @@ -6457,10 +6624,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Eje %1%2 @@ -6474,9 +6641,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [desconocido] @@ -6641,15 +6808,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [inválido] - - + + %1%2Hat %3 %1%2Rotación %3 @@ -6657,35 +6824,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Eje %3 - + %1%2Axis %3,%4,%5 %1%2Eje %3,%4,%5 - + %1%2Motion %3 %1%2Movimiento %3 - - + + %1%2Button %3 %1%2Botón %3 - + [unused] [no usado] @@ -6726,11 +6893,124 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + Ajustes de Amiibo + + + + Amiibo Info + Info. de Amiibo + + + + Series + Serie + + + + Type + Tipo + + + + Name + Nombre + + + + Amiibo Data + Datos de Amiibo + + + + Custom Name + Nombre personalizado + + + + Owner + Propietario + + + + Creation Date + Fecha de creación + + + + dd/MM/yyyy + dd/mm/aaaa + + + + Modification Date + Fecha de modificación + + + + dd/MM/yyyy + dd/mm/aaaa + + + + Game Data + Datos del juego + + + + Game Id + Id del juego + + + + Mount Amiibo + Soporte Amiibo + + + + ... + ... + + + + File Path + Ruta del archivo + + + + No game data present + No hay datos del juego + + + + The following amiibo data will be formatted: + Los siguientes datos de amiibo serán formateados: + + + + The following game data will removed: + Los siguientes datos del juego se eliminarán: + + + + Set nickname and owner: + Establece un apodo y un propietario: + + + + Do you wish to restore this amiibo? + ¿Deseas reestablecer este amiibo? + + QtControllerSelectorDialog @@ -6834,6 +7114,7 @@ p, li { white-space: pre-wrap; } + Handheld Portátil @@ -6873,11 +7154,6 @@ p, li { white-space: pre-wrap; } Docked Acoplado - - - Undocked - Desacoplado - Vibration diff --git a/dist/languages/fr.ts b/dist/languages/fr.ts index d876acc..552f109 100644 --- a/dist/languages/fr.ts +++ b/dist/languages/fr.ts @@ -25,12 +25,18 @@ p, li { white-space: pre-wrap; } <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">This software should not be used to play games you have not legally obtained.</span></p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu est un émulateur expérimental open-source pour la Nintendo Switch sous licence GPLv3.0+.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Ce logiciel ne doit pas être utilisé pour jouer à des jeux que vous n'avez pas obtenus légalement.</span></p></body></html> <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Site Web</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Code Source </span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributeurs</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> @@ -76,95 +82,97 @@ p, li { white-space: pre-wrap; } Room Window - + Fenêtre du Salon Send Chat Message - + Envoyer un message de chat Send Message - + Envoyer le message - + Members Membres - + %1 has joined %1 a rejoint - + %1 has left %1 a quitté - + %1 has been kicked - + %1 a été expulsé - + %1 has been banned - + %1 a été banni - + %1 has been unbanned - + %1 a été débanni - + View Profile - - - - - - Block Player - - - - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - - - - - Kick - - - - - Ban - + Voir le profile + - Kick Player - + Block Player + Bloquer le joueur + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + Lorsque vous bloquez un joueur, vous ne recevrez plus de messages de chat de sa part.<br><br>Êtes-vous sûr de vouloir bloquer %1 ? + + + + Kick + Expulser + + + + Ban + Bannir + + + + Kick Player + Expulser le joueur + + + Are you sure you would like to <b>kick</b> %1? - + Êtes-vous sûr de vouloir <b>expluser</b> %1 ? - + Ban Player - + Bannir le joueur - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - + Êtes-vous sûr de vouloir <b>expluser et bannir </b> %1 ? + +Cela bannirait à la fois son nom d'utilisateur du forum et son adresse IP. @@ -172,7 +180,7 @@ This would ban both their forum username and their IP address. Room Window - + Fenêtre du Salon @@ -182,7 +190,7 @@ This would ban both their forum username and their IP address. Moderation... - + Modération... @@ -200,12 +208,12 @@ This would ban both their forum username and their IP address. Disconnected - + Déconnecté - %1 (%2/%3 members) - connected - + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 membres) - connecté @@ -218,6 +226,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Signaler la compatibilité d'un jeu @@ -228,92 +241,127 @@ This would ban both their forum username and their IP address. </span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informations Système (Processeur / Carte Graphique / Système d'exploitation)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">La version de yuzu que vous employez</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Le compte yuzu sous lequel vous êtes connecté</li></ul></body></html> - - Perfect - Parfait + + <html><head/><body><p>Does the game boot?</p></body></html> + <html><head/><body><p>Est-ce-que le jeu se lance ?</p></body></html> - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Le jeu fonctionne parfaitement sans problèmes audio-visuels.</p></body></html> + + Yes The game starts to output video or audio + Oui Le jeu commence à afficher la video ou à émettre du son - - Great - Bon + + No The game doesn't get past the "Launching..." screen + Non Le jeu ne fonctionne plus après après l'écran "de lancement" - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Le jeu fonctionne avec des problèmes audio-visuels mineurs et est jouable du début jusqu'à la fin. Peut nécessiter des patchs temporaires. </p></body></html> + + Yes The game gets past the intro/menu and into gameplay + Oui Le jeu fonctionne après l'intro/menu et dans le gameplay - - Okay - Correct + + No The game crashes or freezes while loading or using the menu + Non Le jeu crash ou freeze pendant le chargement ou pendant l'utilisation du menu - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Le jeu fonctionne avec des problèmes audio ou graphiques majeurs, mais est jouable du début à la fin avec des modifications.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + <html><head/><body><p>Est-ce-que le jeu atteint le gameplay ?</p></body></html> - - Bad - Mauvais + + Yes The game works without crashes + Oui Le jeu fonctionne sans crasher - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Le jeu fonctionne, mais avec des problèmes graphiques ou audio majeurs. Impossible de progresser à certains endroits spécifiques à cause de ces problèmes même en utilisant des modifications. </p></body></html> + + No The game crashes or freezes during gameplay + Non Le jeu crash ou freeze pendant le gameplay - - Intro/Menu - Intro/Menu + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + <html><head/><body><p>Est-ce-que le jeu fonctionne sans crasher, freezer ou se verouiller pendant le gameplay ?</p></body></html> - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Le jeu est complètement injouable à cause de problèmes graphique ou audio majeur. Impossible de progresser plus loin que le menu de départ.</p></body></html> + + Yes The game can be finished without any workarounds + Oui Le jeu peut être fini sans manipulations - - Won't Boot - Ne se lance pas + + No The game can't progress past a certain area + Non Le jeu ne peut pas progresser après un certain temps - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Le jeu crash au lancement. .</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + <html><head/><body><p>Est-ce-que le jeu est complètement jouable du début à la fin ?</p></body></html> - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Sans prendre en compte la vitesse ou les performances, À quel point ce jeu est-il bien émulée du début à la fin sur cette version de yuzu ?</p></body></html> + + Major The game has major graphical errors + Majeur Le jeu a des erreurs graphiques majeures - + + Minor The game has minor graphical errors + Mineur Le jeu a des erreurs graphiques mineures + + + + None Everything is rendered as it looks on the Nintendo Switch + Aucun Tout est render comme cela apparait sur la Nintendo Switch + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + <html><head/><body><p>Est-ce-que le jeu a des glitchs graphiques ?</p></body></html> + + + + Major The game has major audio errors + Majeur Le jeu a des erreurs d'audio majeures + + + + Minor The game has minor audio errors + Mineur Le jeu a des erreurs d'audio mineures + + + + None Audio is played perfectly + Aucun L'audio est joué parfaitement + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + <html><head/><body><p> Est-ce-que le jeu a des glitchs audio ou des effets manquants ? </p></body></html> + + + Thank you for your submission! Merci de votre Suggestion ! - + Submitting Soumission en cours - + Communication error Erreur de communication - + An error occurred while sending the Testcase Une erreur est survenue lors de l'envoi du cas-type - + Next Suivant @@ -334,7 +382,7 @@ This would ban both their forum username and their IP address. Output Device - + Périphérique de sortie @@ -373,37 +421,37 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + Configurer la caméra infrarouge Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + Sélectionnez d'où provient l'image de la caméra émulée. Il peut s'agir d'une caméra virtuelle ou d'une caméra réelle. Camera Image Source: - + Source de l'image de la caméra : Input device: - + Périphérique d'entrée : Preview - + Aperçu Resolution: 320*240 - + Résolution : 320*240 Click to preview - + Cliquez pour prévisualiser @@ -411,7 +459,7 @@ This would ban both their forum username and their IP address. Restaurer les défauts - + Auto Auto @@ -726,7 +774,11 @@ Cette option améliore la vitesse en réduisant la précision des instructions f <div style="white-space: nowrap">Enabling it causes guest exclusive memory reads/writes to be done directly into memory and make use of Host's MMU.</div> <div style="white-space: nowrap">Disabling this forces all exclusive memory accesses to use Software MMU Emulation.</div> - + + <div style="white-space: nowrap">Cette optimisation accélère les accès exclusifs à la mémoire par le programme invité.</div> + <div style="white-space: nowrap">Son activation entraîne l'exécution directe de lectures/écritures de la mémoire exclusive de l'invité dans la mémoire et l'utilisation du MMU de l'hôte.</div> + <div style="white-space: nowrap">La désactivation de cette option force tous les accès exclusifs à la mémoire à utiliser l'émulation logicielle MMU.</div> + @@ -739,12 +791,15 @@ Cette option améliore la vitesse en réduisant la précision des instructions f <div style="white-space: nowrap">This optimization speeds up exclusive memory accesses by the guest program.</div> <div style="white-space: nowrap">Enabling it reduces the overhead of fastmem failure of exclusive memory accesses.</div> - + + <div style="white-space: nowrap">Cette optimisation accélère les accès exclusifs à la mémoire par le programme invité.</div> + <div style="white-space: nowrap">L'activer réduit la surcharge de l'échec fastmem des accès exclusifs à la mémoire.</div> + Enable recompilation of exclusive memory instructions - + Activer la recompilation des instructions de mémoire exclusives @@ -755,200 +810,235 @@ Cette option améliore la vitesse en réduisant la précision des instructions f ConfigureDebug - + Debugger - + Débogueur - + Enable GDB Stub Activer le "stub" de GDB - + Port: Port : - + Logging S'enregistrer - + Global Log Filter Filtre de log global - + Show Log in Console Afficher le relevé d'événements dans la console - + Open Log Location Ouvrir l'emplacement du journal de logs - + When checked, the max size of the log increases from 100 MB to 1 GB Lorsque coché, la taille maximum du relevé d'événements augmente de 100 Mo à 1 Go - + Enable Extended Logging** Activer la Journalisation Étendue** - + Homebrew Homebrew - + Arguments String Chaîne d'arguments - + Graphics Graphismes - + When checked, the graphics API enters a slower debugging mode Lorsque coché, l'API graphique entre dans un mode de débogage plus lent - + Enable Graphics Debugging Activer le débogage des graphismes - + When checked, it enables Nsight Aftermath crash dumps Une fois cochée, cette option active les "crash dumps" pour Nsight Aftermath - + Enable Nsight Aftermath Activer Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Lorsqu'il est coché, il videra tous les shaders d'assemblage d'origine du cache de shader de disque ou du jeu tels qu'ils ont été trouvés - + Dump Game Shaders Récupérer Shaders Jeu - + When checked, it will dump all the macro programs of the GPU - + Lorsqu'il est coché, il videra tous les programmes de macro du GPU - + Dump Maxwell Macros - + Copier les "Maxwell Macros" - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Lorsque coché, désactive le compilateur de macros JIT. L'activer ralentit les jeux - + Disable Macro JIT Désactiver les macros JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Lorsque la case est cochée, yuzu enregistrera les journaux de statistiques à propos de la cache de pipeline compilée - + Enable Shader Feedback Activer le retour d'information des shaders - + When checked, it executes shaders without loop logic changes Lorsque la case est cochée, exécuter les shaders sans changer la boucle de logique - + Disable Loop safety checks Désactiver les vérifications de boucle - + Debugging Débogage - - Enable FS Access Log - Activer la journalisation des accès du système de fichiers - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Activer les services de rapport verbeux** - + + Enable FS Access Log + Activer la journalisation des accès du système de fichiers + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + Activez cette option pour afficher la dernière liste de commandes audio générée sur la console. N'affecte que les jeux utilisant le moteur de rendu audio. + + + + Dump Audio Commands To Console** + Déversez les commandes audio à la console** + + + + Create Minidump After Crash + Crée un Minidump après un crash + + + Advanced Avancé - + Kiosk (Quest) Mode Mode Kiosk (Quest) - + Enable CPU Debugging Activer le Débogage CPU - + Enable Debug Asserts Activer les assertions de débogage - + Enable Auto-Stub** Activer l'Auto-Stub** - + Enable All Controller Types - + Activer tous les types de contrôleurs - + Disable Web Applet Désactiver l'applet web - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + Active yuzu pour chercher pour un environnement Vulkan fonctionnel quand le programme démarre. Desactiver ceci si cela cause des problèmes avec des programmes externes. + + + + Perform Startup Vulkan Check + Performe un check de Vulkan au démarrage + + + **This will be reset automatically when yuzu closes. **Ces options seront réinitialisées automatiquement lorsque yuzu fermera. + + + Restart Required + Redémarrage nécessaire + + + + yuzu is required to restart in order to apply this setting. + yuzu doit redémarrer pour appliquer ce paramètre. + + + + Web applet not compiled + Applet Web non compilé + + + + MiniDump creation not compiled + Création de MiniDump non compilé + ConfigureDebugController @@ -1242,7 +1332,7 @@ Cette option améliore la vitesse en réduisant la précision des instructions f Extended memory layout (6GB DRAM) - + Disposition de la mémoire étendue (6GB DRAM) @@ -1318,193 +1408,218 @@ Cette option améliore la vitesse en réduisant la précision des instructions f API : - + Graphics Settings Paramètres Vidéo - + Use disk pipeline cache Utiliser la cache de pipeline sur disque - + Use asynchronous GPU emulation Utiliser l'émulation GPU asynchrone - + Accelerate ASTC texture decoding Accélérer le décodage des textures ASTC - + NVDEC emulation: Émulation NVDEC - + No Video Output Pas de sortie vidéo - + CPU Video Decoding Décodage Vidéo sur le CPU - + GPU Video Decoding (Default) Décodage Vidéo sur le GPU (par défaut) - + Fullscreen Mode: Mode Plein écran : - + Borderless Windowed Fenêtré sans bordure - + Exclusive Fullscreen Plein écran exclusif - + Aspect Ratio: Format : - + Default (16:9) Par défaut (16:9) - + Force 4:3 Forcer le 4:3 - + Force 21:9 Forcer le 21:9 - + + Force 16:10 + Forcer 16:10 + + + Stretch to Window Étirer à la fenêtre - + Resolution: Résolution : - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EXPÉRIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EXPÉRIMENTAL] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: Filtre de fenêtre adaptatif - + Nearest Neighbor Plus proche voisin - + Bilinear Bilinéaire - + Bicubic Bicubique - + Gaussian Gaussien - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (Vulkan seulement) - + Anti-Aliasing Method: Méthode d'anticrénelage : - + None Aucune - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + 100% + + + + Use global background color Utiliser une couleur d'arrière-plan globale - + Set background color: Définir la couleur d'arrière-plan : - + Background Color: Couleur de L’arrière plan : @@ -1513,6 +1628,12 @@ Cette option améliore la vitesse en réduisant la précision des instructions f GLASM (Assembly Shaders, NVIDIA Only) GLASM (Shaders en Assembleur, NVIDIA Seulement) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1544,7 +1665,7 @@ Cette option améliore la vitesse en réduisant la précision des instructions f Use VSync - + Utiliser VSync @@ -1567,37 +1688,47 @@ Cette option améliore la vitesse en réduisant la précision des instructions f Utiliser le Temps GPU Rapide (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + Active les vidages de tampon pessimistes. Cette option va forcer les tampons non-modifiés à être vidé, cela peut affecter la performance. + + + + Use pessimistic buffer flushes (Hack) + Utiliser des vidages de tampon pessimistes (Hack) + + + Anisotropic Filtering: Filtrage anisotropique : - + Automatic Automatique - + Default Défaut - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -1992,12 +2123,12 @@ Cette option améliore la vitesse en réduisant la précision des instructions f Ring Controller - + Contrôleur Anneau Infrared Camera - + Caméra infrarouge @@ -2027,7 +2158,7 @@ Cette option améliore la vitesse en réduisant la précision des instructions f Controller navigation - + Manette de navigation @@ -2089,7 +2220,7 @@ Cette option améliore la vitesse en réduisant la précision des instructions f - + Left Stick Stick Gauche @@ -2183,14 +2314,14 @@ Cette option améliore la vitesse en réduisant la précision des instructions f - + L L - + ZL ZL @@ -2209,7 +2340,7 @@ Cette option améliore la vitesse en réduisant la précision des instructions f - + Plus Plus @@ -2222,15 +2353,15 @@ Cette option améliore la vitesse en réduisant la précision des instructions f - + R R - + ZR ZR @@ -2287,231 +2418,236 @@ Cette option améliore la vitesse en réduisant la précision des instructions f - + Right Stick Stick Droit - - - - + + + + Clear Effacer - - - - - + + + + + [not set] [non défini] - - - Toggle button - Bouton d'activation - - - - + + Invert button Inverser les boutons - - + + + Toggle button + Bouton d'activation + + + + Invert axis Inverser l'axe - - - + + + Set threshold Définir le seuil - - + + Choose a value between 0% and 100% Choisissez une valeur entre 0% et 100% - + + Toggle axis + Basculer les axes + + + Set gyro threshold Définir le seuil du gyroscope - + Map Analog Stick Mapper le stick analogique - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Après avoir appuyé sur OK, bougez d'abord votre joystick horizontalement, puis verticalement. Pour inverser les axes, bougez d'abord votre joystick verticalement, puis horizontalement. - + Center axis - + Axe central - - + + Deadzone: %1% Zone morte : %1% - - + + Modifier Range: %1% Modification de la course : %1% - - + + Pro Controller Pro Controller - + Dual Joycons Deux Joycons - + Left Joycon Joycon de gauche - + Right Joycon Joycon de droit - + Handheld Mode Portable - + GameCube Controller Manette GameCube - + Poke Ball Plus Poké Ball Plus - + NES Controller Manette NES - + SNES Controller Manette SNES - + N64 Controller Manette N64 - + Sega Genesis Sega Genesis - + Start / Pause Start / Pause - + Z Z - + Control Stick Stick de contrôle - + C-Stick C-Stick - + Shake! Secouez ! - + [waiting] [en attente] - + New Profile Nouveau Profil - + Enter a profile name: Entrez un nom de profil : - - + + Create Input Profile Créer un profil d'entrée - + The given profile name is not valid! Le nom de profil donné est invalide ! - + Failed to create the input profile "%1" Échec de la création du profil d'entrée "%1" - + Delete Input Profile Supprimer le profil d'entrée - + Failed to delete the input profile "%1" Échec de la suppression du profil d'entrée "%1" - + Load Input Profile Charger le profil d'entrée - + Failed to load the input profile "%1" Échec du chargement du profil d'entrée "%1" - + Save Input Profile Sauvegarder le profil d'entrée - + Failed to save the input profile "%1" Échec de la sauvegarde du profil d'entrée "%1" @@ -2566,7 +2702,7 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h Touch from button profile: - + Appuyer sur le profil du bouton : @@ -2766,42 +2902,42 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h Développeur - + Add-Ons Extensions - + General Général - + System Système - + CPU CPU - + Graphics Graphiques - + Adv. Graphics Adv. Graphiques - + Audio Audio - + Properties Propriétés @@ -2857,37 +2993,37 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h Utilisateur actuel - + Username Nom d'utilisateur - + Set Image Mettre une image - + Add Ajouter - + Rename Renommer - + Remove Supprimer - + Profile management is available only when game is not running. La gestion de profil est accessible uniquement lorsque aucun jeu n'est en cours. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2895,96 +3031,106 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h %2 - + Enter Username Entrez un nom d'utilisateur - + Users Utilisateurs - + Enter a username for the new user: Entrez un nom d'utilisateur pour le nouvel utilisateur : - + Enter a new username: Entrez un nouveau nom d'utilisateur : - - Confirm Delete - Confirmez la suppression - - - - You are about to delete user with name "%1". Are you sure? - Vous êtes sur le point de supprimer l'utilisateur avec le nom "%1". Êtes-vous sûr ? - - - + Select User Image Sélectionner l'image de l'utilisateur - + JPEG Images (*.jpg *.jpeg) Images JPEG (*.jpg *.jpeg) - + Error deleting image Erreur dans la suppression de l'image - + Error occurred attempting to overwrite previous image at: %1. Une erreur est survenue en essayant de changer l'image précédente à : %1. - + Error deleting file Erreur dans la suppression du fichier - + Unable to delete existing file: %1. Impossible de supprimer le fichier existant : %1. - + Error creating user image directory Erreur dans la création du répertoire d'image de l'utilisateur - + Unable to create directory %1 for storing user images. Impossible de créer le répertoire %1 pour stocker les images de l'utilisateur. - + Error copying user image Erreur dans la copie de l'image de l'utilisateur - + Unable to copy image from %1 to %2 Impossible de copier l'image de %1 à %2 - + Error resizing user image Erreur de redimensionnement de l'image utilisateur - + Unable to resize image Impossible de redimensionner l'image + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + Supprimer cet utilisateur ? Toutes les données de l'utilisateur vont être supprimées. + + + + Confirm Delete + Confirmez la suppression + + + + Name: %1 +UUID: %2 + Nom : %1 +UUID : %2 + + ConfigureRingController @@ -2995,24 +3141,24 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly. - + Si vous souhaitez utiliser cette manette, configurez le joueur 1 comme manette droite et le joueur 2 comme double joycon avant de lancer le jeu pour permettre à cette manette d'être détectée correctement. Ring Sensor Parameters - + Paramètres du Capteur de l'Anneau Pull - + Tirer Push - + Pousser @@ -3513,47 +3659,47 @@ Pour inverser les axes, bougez d'abord votre joystick verticalement, puis h <html><head/><body><p>Lit l'entrée du contrôleur à partir des scripts dans le même format que 'TAS-nx' <br/> Pour une explication plus détaillée, veuillez consulter le <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">page d'aide </span></a>sur le site Yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Pour vérifier quelles touches de raccourci contrôlent la lecture/l'enregistrement, veuillez vous reporter aux paramètres des touches de raccourci (Configurer -> Général -> Touches de raccourci). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. AVERTISSEMENT : Cette fonctionnalité est expérimentale.<br/>Elle n'exécutera pas les scripts à l'image près avec l'actuelle méthode, imparfaite, de synchronisation. - + Settings Paramètres - + Enable TAS features Activer les fonctions TAS - + Loop script Script de boucle - + Pause execution during loads Mettre en pause l'exécution pendant le chargement - + Script Directory Dossier de script - + Path Chemin - + ... ... @@ -3805,56 +3951,71 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce + Show Compatibility List + Afficher la liste de compatibilité + + + Show Add-Ons Column Afficher la colonne des Add-Ons - + + Show Size Column + Afficher la taille des colonnes + + + + Show File Types Column + Afficher la colonne des types de fichier + + + Game Icon Size: Taille de l'icône jeu: - + Folder Icon Size: Taille de l'icône dossier: - + Row 1 Text: Texte rangée 1 : - + Row 2 Text: Texte rangée 2 : - + Screenshots Captures d'écran - + Ask Where To Save Screenshots (Windows Only) Demander où enregistrer les captures d'écran (Windows uniquement) - + Screenshots Path: Chemin du dossier des captures d'écran : - + ... ... - + Select Screenshots Path... Sélectionnez le chemin du dossier des captures d'écran... - + <System> <System> @@ -3869,7 +4030,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce Press any controller button to vibrate the controller. - + Appuyez sur n'importe quel bouton du contrôleur pour faire vibrer le contrôleur. @@ -3963,7 +4124,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce - + Verify Vérifier @@ -3990,7 +4151,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce Web Service configuration can only be changed when a public room isn't being hosted. - + La configuration du service Web ne peut être modifiée que lorsqu'une salle publique n'est pas hébergée. @@ -4050,7 +4211,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce - + Unspecified Non-spécifié @@ -4065,17 +4226,36 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce Le token n'a pas été vérifié. Le changement à votre token n'a pas été enregistré. - + + Unverified, please click Verify before saving configuration + Tooltip + Non-verifié, veuillez clicker Verifier avant de sauvergarder la configuration + + + + Verifying... Vérification... - + + Verified + Tooltip + Vérifié + + + + Verification failed + Tooltip + Échec de la vérification + + + Verification failed Échec de la vérification - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Échec de la vérification. Vérifiez si vous avez correctement entrez votre token, et que votre connection internet fonctionne. @@ -4113,7 +4293,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce <html><head/><body><p>IPv4 address of the host</p></body></html> - + <html><head/><body><p>Adresse IPv4 de l'hôte</p></body></html> @@ -4123,7 +4303,7 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce <html><head/><body><p>Port number the host is listening on</p></body></html> - + <html><head/><body><p>Numéro de port sur lequel l'hôte écoute</p></body></html> @@ -4138,928 +4318,931 @@ Faites glisser les points pour modifier la position ou double-cliquez sur les ce Connect - + Connecter DirectConnectWindow - + Connecting - + Connexion - + Connect - + Connecter GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Des données anonymes sont collectées</a> pour aider à améliorer yuzu. <br/><br/>Voulez-vous partager vos données d'utilisations avec nous ? - + Telemetry Télémétrie - + Broken Vulkan Installation Detected Installation Vulkan Cassée Détectée - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + L'initialisation de Vulkan a échoué lors du démarrage.<br><br>Cliquez <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>ici pour obtenir des instructions pour résoudre le problème</a>. - + Loading Web Applet... Chargement du Web Applet... - - + + Disable Web Applet Désactiver l'applet web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + La désactivation de l'applet Web peut entraîner un comportement indéfini et ne doit être utilisée qu'avec Super Mario 3D All-Stars. Voulez-vous vraiment désactiver l'applet Web ? +(Cela peut être réactivé dans les paramètres de débogage.) - + The amount of shaders currently being built La quantité de shaders en cours de construction - + The current selected resolution scaling multiplier. Le multiplicateur de mise à l'échelle de résolution actuellement sélectionné. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Valeur actuelle de la vitesse de l'émulation. Des valeurs plus hautes ou plus basses que 100% indique que l'émulation fonctionne plus vite ou plus lentement qu'une véritable Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Combien d'image par seconde le jeu est en train d'afficher. Ceci vas varier de jeu en jeu et de scènes en scènes. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Temps pris pour émuler une image par seconde de la switch, sans compter le limiteur d'image par seconde ou la synchronisation verticale. Pour une émulation à pleine vitesse, ceci devrait être au maximum à 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Effacer les fichiers récents - + &Continue &Continuer - + &Pause &Pause - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu exécute un jeu - + Warning Outdated Game Format Avertissement : Le Format de jeu est dépassé - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Vous utilisez un format de ROM déconstruite pour ce jeu, qui est donc un format dépassé qui à été remplacer par d'autre. Par exemple les formats NCA, NAX, XCI, ou NSP. Les destinations de ROM déconstruites manque des icônes, des métadonnée et du support de mise à jour.<br><br>Pour une explication des divers formats Switch que yuzu supporte, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>Regardez dans le wiki</a>. Ce message ne sera pas montré une autre fois. - - + + Error while loading ROM! Erreur lors du chargement de la ROM ! - + The ROM format is not supported. Le format de la ROM n'est pas supporté. - + An error occurred initializing the video core. Une erreur s'est produite lors de l'initialisation du noyau dédié à la vidéo. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu a rencontré une erreur en exécutant le cœur vidéo. Cela est généralement causé par des pilotes graphiques trop anciens. Veuillez consulter les logs pour plus d'informations. Pour savoir comment accéder aux logs, veuillez vous référer à la page suivante : <a href='https://yuzu-emu.org/help/reference/log-files/'>Comment partager un fichier de log </a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Erreur lors du chargement de la ROM ! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Veuillez suivre <a href='https://yuzu-emu.org/help/quickstart/'>le guide de démarrage rapide yuzu</a> pour retransférer vos fichiers.<br>Vous pouvez vous référer au wiki yuzu</a> ou le Discord yuzu</a> pour de l'assistance. - + An unknown error occurred. Please see the log for more details. Une erreur inconnue est survenue. Veuillez consulter le journal des logs pour plus de détails. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Enregistrer les données - + Mod Data Donnés du Mod - + Error Opening %1 Folder Erreur dans l'ouverture du dossier %1. - - + + Folder does not exist! Le dossier n'existe pas ! - + Error Opening Transferable Shader Cache Erreur lors de l'ouverture des Shader Cache Transferable - + Failed to create the shader cache directory for this title. Impossible de créer le dossier de cache du shader pour ce jeu. - - Contents - Contenus + + Error Removing Contents + Erreur en enlevant le contenu - - Update - Mise à jour + + Error Removing Update + Erreur en enlevant la Mise à Jour - - DLC - DLC + + Error Removing DLC + Erreur en enlevant le DLC - + + Remove Installed Game Contents? + Enlever les données des jeux installés ? + + + + Remove Installed Game Update? + Enlever la mise à jour du jeu installé ? + + + + Remove Installed Game DLC? + Enlever le DLC du jeu installé ? + + + Remove Entry Supprimer l'entrée - - Remove Installed Game %1? - Supprimer le jeu installé %1? - - - - - - - - + + + + + + Successfully Removed Supprimé avec succès - + Successfully removed the installed base game. Suppression du jeu de base installé avec succès. - - - - Error Removing %1 - Erreur lors de la suppression %1 - - - + The base game is not installed in the NAND and cannot be removed. Le jeu de base n'est pas installé dans la NAND et ne peut pas être supprimé. - + Successfully removed the installed update. Suppression de la mise à jour installée avec succès. - + There is no update installed for this title. Il n'y a pas de mise à jour installée pour ce titre. - + There are no DLC installed for this title. Il n'y a pas de DLC installé pour ce titre. - + Successfully removed %1 installed DLC. Suppression de %1 DLC installé(s) avec succès. - + Delete OpenGL Transferable Shader Cache? Supprimer la Cache OpenGL de Shader Transférable? - + Delete Vulkan Transferable Shader Cache? Supprimer la Cache Vulkan de Shader Transférable? - + Delete All Transferable Shader Caches? Supprimer Toutes les Caches de Shader Transférable? - + Remove Custom Game Configuration? Supprimer la configuration personnalisée du jeu? - + Remove File Supprimer fichier - - + + Error Removing Transferable Shader Cache Erreur lors de la suppression du cache de shader transférable - - + + A shader cache for this title does not exist. Un shader cache pour ce titre n'existe pas. - + Successfully removed the transferable shader cache. Suppression du cache de shader transférable avec succès. - + Failed to remove the transferable shader cache. Échec de la suppression du cache de shader transférable. - - + + Error Removing Transferable Shader Caches Erreur durant la Suppression des Caches de Shader Transférable - + Successfully removed the transferable shader caches. Suppression des caches de shader transférable effectuée avec succès. - + Failed to remove the transferable shader cache directory. Impossible de supprimer le dossier de la cache de shader transférable. - - + + Error Removing Custom Configuration Erreur lors de la suppression de la configuration personnalisée - + A custom configuration for this title does not exist. Il n'existe pas de configuration personnalisée pour ce titre. - + Successfully removed the custom game configuration. Suppression de la configuration de jeu personnalisée avec succès. - + Failed to remove the custom game configuration. Échec de la suppression de la configuration personnalisée du jeu. - - + + RomFS Extraction Failed! L'extraction de la RomFS a échoué ! - + There was an error copying the RomFS files or the user cancelled the operation. Une erreur s'est produite lors de la copie des fichiers RomFS ou l'utilisateur a annulé l'opération. - + Full Plein - + Skeleton Squelette - + Select RomFS Dump Mode Sélectionnez le mode d'extraction de la RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Veuillez sélectionner la manière dont vous souhaitez que le fichier RomFS soit extrait.<br>Full copiera tous les fichiers dans le nouveau répertoire, tandis que<br>skeleton créera uniquement la structure de répertoires. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Il n'y a pas assez d'espace libre dans %1 pour extraire la RomFS. Veuillez libérer de l'espace ou sélectionner un autre dossier d'extraction dans Émulation > Configurer > Système > Système de fichiers > Extraire la racine - + Extracting RomFS... Extraction de la RomFS ... - - + + Cancel Annuler - + RomFS Extraction Succeeded! Extraction de la RomFS réussi ! - + The operation completed successfully. L'opération s'est déroulée avec succès. - + Error Opening %1 Erreur lors de l'ouverture %1 - + Select Directory Sélectionner un répertoire - + Properties Propriétés - + The game properties could not be loaded. Les propriétés du jeu n'ont pas pu être chargées. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Exécutable Switch (%1);;Tous les fichiers (*.*) - + Load File Charger un fichier - + Open Extracted ROM Directory Ouvrir le dossier des ROM extraites - + Invalid Directory Selected Destination sélectionnée invalide - + The directory you have selected does not contain a 'main' file. Le répertoire que vous avez sélectionné ne contient pas de fichier "main". - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Fichier Switch installable (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Installer les fichiers - + %n file(s) remaining %n fichier restant%n fichiers restants%n fichiers restants - + Installing file "%1"... Installation du fichier "%1" ... - - + + Install Results Résultats d'installation - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Pour éviter d'éventuels conflits, nous déconseillons aux utilisateurs d'installer des jeux de base sur la NAND. Veuillez n'utiliser cette fonctionnalité que pour installer des mises à jour et des DLC. - + %n file(s) were newly installed %n fichier a été nouvellement installé%n fichiers ont été nouvellement installés%n fichiers ont été nouvellement installés - + %n file(s) were overwritten %n fichier a été écrasé%n fichiers ont été écrasés%n fichiers ont été écrasés - + %n file(s) failed to install %n fichier n'a pas pu être installé%n fichiers n'ont pas pu être installés%n fichiers n'ont pas pu être installés - + System Application Application Système - + System Archive Archive Système - + System Application Update Mise à jour de l'application système - + Firmware Package (Type A) Paquet micrologiciel (Type A) - + Firmware Package (Type B) Paquet micrologiciel (Type B) - + Game Jeu - + Game Update Mise à jour de jeu - + Game DLC DLC de jeu - + Delta Title Titre Delta - + Select NCA Install Type... Sélectionner le type d'installation du NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Veuillez sélectionner le type de titre auquel vous voulez installer ce NCA : (Dans la plupart des cas, le titre par défaut : 'Jeu' est correct.) - + Failed to Install Échec de l'installation - + The title type you selected for the NCA is invalid. Le type de titre que vous avez sélectionné pour le NCA n'est pas valide. - + File not found Fichier non trouvé - + File "%1" not found Fichier "%1" non trouvé - + OK OK - + + Hardware requirements not met + Éxigences matérielles non respectées + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + Votre système ne correspond pas aux éxigences matérielles. Les rapports de comptabilité ont été désactivés. + + + Missing yuzu Account Compte yuzu manquant - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Pour soumettre un test de compatibilité pour un jeu, vous devez lier votre compte yuzu.<br><br/>Pour lier votre compte yuzu, aller à Emulation &gt; Configuration&gt; Web. - + Error opening URL Erreur lors de l'ouverture de l'URL - + Unable to open the URL "%1". Impossible d'ouvrir l'URL "%1". - + TAS Recording Enregistrement TAS - + Overwrite file of player 1? Écraser le fichier du joueur 1 ? - + Invalid config detected Configuration invalide détectée - + Handheld controller can't be used on docked mode. Pro controller will be selected. Contrôleur portable ne peut pas être utilisé en mode téléviseur. La manette Pro sera sélectionnée. - - - Error - Erreur - - - - - The current game is not looking for amiibos - Le jeu actuel ne cherche pas d'amiibos. - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed L'amiibo actuel a été retiré - + + Error + Erreur + + + + + The current game is not looking for amiibos + Le jeu actuel ne cherche pas d'amiibos. + + + Amiibo File (%1);; All Files (*.*) Fichier Amiibo (%1);; Tous les fichiers (*.*) - + Load Amiibo Charger un Amiibo - - Error opening Amiibo data file - Erreur lors de l'ouverture du fichier de données Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Impossible d'ouvrir le fichier Amiibo "%1" à lire. - - - - Error reading Amiibo data file - Erreur lors de la lecture du fichier de données Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Impossible de lire entièrement les données Amiibo. On s'attend à lire %1 octets, mais il n'a pu lire que %2 octets - - - + Error loading Amiibo data Erreur lors du chargement des données Amiibo - - Unable to load Amiibo data. - Impossible de charger les données Amiibo. + + The selected file is not a valid amiibo + Le fichier choisi n'est pas un amiibo valide - + + The selected file is already on use + Le fichier sélectionné est déjà utilisé + + + + An unknown error occurred + Une erreur inconnue s'est produite + + + Capture Screenshot Capture d'écran - + PNG Image (*.png) Image PNG (*.png) - + TAS state: Running %1/%2 État du TAS : En cours d'exécution %1/%2 - + TAS state: Recording %1 État du TAS : Enregistrement %1 - + TAS state: Idle %1/%2 État du TAS : Inactif %1:%2 - + TAS State: Invalid État du TAS : Invalide - + &Stop Running &Stopper l'exécution - + &Start &Start - + Stop R&ecording Stopper l'en&registrement - + R&ecord En&registrer - + Building: %n shader(s) Compilation: %n shaderCompilation : %n shadersCompilation : %n shaders - + Scale: %1x %1 is the resolution scaling factor Échelle : %1x - + Speed: %1% / %2% Vitesse : %1% / %2% - + Speed: %1% Vitesse : %1% - + Game: %1 FPS (Unlocked) Jeu : %1 IPS (Débloqué) - + Game: %1 FPS Jeu : %1 FPS - + Frame: %1 ms Frame : %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU HAUT - + GPU EXTREME GPU EXTRÊME - + GPU ERROR GPU ERREUR - + DOCKED MODE TV - + HANDHELD PORTABLE - + NEAREST PLUS PROCHE - - + + BILINEAR BILINÉAIRE - + BICUBIC BICUBIQUE - + GAUSSIAN GAUSSIEN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA AUCUN AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Le jeu que vous essayez de charger a besoin de fichiers additionnels que vous devez extraire depuis votre Switch avant de jouer.<br/><br/>Pour plus d'information sur l'extraction de ces fichiers, veuillez consulter la page du wiki suivante : <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Extraction des archives système et des Shared Fonts depuis la Switch</a>.<br/><br/>Voulez-vous quitter la liste des jeux ? Une émulation continue peut entraîner des crashs, la corruption de données de sauvegarde ou d’autres bugs. - + yuzu was unable to locate a Switch system archive. %1 yuzu n'a pas été capable de localiser un système d'archive Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu n'a pas été capable de localiser un système d'archive Switch. %1. %2 - + System Archive Not Found Archive système introuvable - + System Archive Missing Archive Système Manquante - + yuzu was unable to locate the Switch shared fonts. %1 Yuzu n'a pas été capable de localiser les polices partagées de la Switch. %1 - + Shared Fonts Not Found Les polices partagées non pas été trouvées - + Shared Font Missing Police Partagée Manquante - + Fatal Error Erreur fatale - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu a rencontré une erreur fatale, veuillez consulter les logs pour plus de détails. Pour plus d'informations sur l'accès aux logs, veuillez consulter la page suivante : <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'> Comment télécharger le fichier des logs </a>.<br/><br/>Voulez-vous quitter la liste des jeux ? Une émulation continue peut entraîner des crashs, la corruption de données de sauvegarde ou d’autres bugs. - + Fatal Error encountered Erreur Fatale rencontrée - + Confirm Key Rederivation Confirmer la réinstallation de la clé - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5076,37 +5259,37 @@ et éventuellement faites des sauvegardes. Cela supprimera vos fichiers de clé générés automatiquement et ré exécutera le module d'installation de clé. - + Missing fuses Fusibles manquants - + - Missing BOOT0 - BOOT0 manquant - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main manquant - + - Missing PRODINFO - PRODINFO manquant - + Derivation Components Missing Composants de dérivation manquants - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Les clés de chiffrement sont manquantes. <br>Veuillez suivre <a href='https://yuzu-emu.org/help/quickstart/'>le guide de démarrage rapide yuzu</a> pour obtenir tous vos clés, firmware et jeux.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5115,39 +5298,39 @@ Cela peut prendre jusqu'à une minute en fonction des performances de votre système. - + Deriving Keys Installation des clés - + Select RomFS Dump Target Sélectionner la cible d'extraction du RomFS - + Please select which RomFS you would like to dump. Veuillez sélectionner quel RomFS vous voulez extraire. - + Are you sure you want to close yuzu? Êtes vous sûr de vouloir fermer yuzu ? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Êtes-vous sûr d'arrêter l'émulation ? Tout progrès non enregistré sera perdu. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5159,38 +5342,38 @@ Voulez-vous ignorer ceci and quitter quand même ? GRenderWindow - + OpenGL not available! OpenGL n'est pas disponible ! - + yuzu has not been compiled with OpenGL support. yuzu n'a pas été compilé avec le support OpenGL. - - + + Error while initializing OpenGL! Erreur lors de l'initialisation d'OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Votre GPU peut ne pas prendre en charge OpenGL, ou vous n'avez pas les derniers pilotes graphiques. - + Error while initializing OpenGL 4.6! Erreur lors de l'initialisation d'OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Votre GPU peut ne pas prendre en charge OpenGL 4.6 ou vous ne disposez pas du dernier pilote graphique: %1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Votre GPU peut ne pas prendre en charge une ou plusieurs extensions OpenGL requises. Veuillez vous assurer que vous disposez du dernier pilote graphique.<br><br>GL Renderer :<br>%1<br><br>Extensions non prises en charge :<br>%2 @@ -5198,153 +5381,153 @@ Voulez-vous ignorer ceci and quitter quand même ? GameList - + Favorite Préférer - + Start Game Démarrer le jeu - + Start Game without Custom Configuration Démarrer le jeu sans configuration personnalisée - + Open Save Data Location Ouvrir l'emplacement des données de sauvegarde - + Open Mod Data Location Ouvrir l'emplacement des données des mods - + Open Transferable Pipeline Cache Ouvrir la Cache de Pipeline Transférable - + Remove Supprimer - + Remove Installed Update Supprimer mise à jour installée - + Remove All Installed DLC Supprimer tous les DLC installés - + Remove Custom Configuration Supprimer la configuration personnalisée - + Remove OpenGL Pipeline Cache Supprimer la Cache de Pipeline OpenGL - + Remove Vulkan Pipeline Cache Supprimer la Cache de Pipeline Vulkan - + Remove All Pipeline Caches Supprimer Toutes les Caches de Pipeline - + Remove All Installed Contents Supprimer tout le contenu installé - + Dump RomFS Extraire la RomFS - + Dump RomFS to SDMC Décharger RomFS vers SDMC - + Copy Title ID to Clipboard Copier l'ID du titre dans le Presse-papiers - + Navigate to GameDB entry Accédez à l'entrée GameDB - + Properties Propriétés - + Scan Subfolders Scanner les sous-dossiers - + Remove Game Directory Supprimer le répertoire du jeu - + ▲ Move Up ▲ Monter - + ▼ Move Down ▼ Descendre - + Open Directory Location Ouvrir l'emplacement du répertoire - + Clear Effacer - + Name Nom - + Compatibility Compatibilité - + Add-ons Extensions - + File type Type de fichier - + Size Taille @@ -5353,79 +5536,61 @@ Voulez-vous ignorer ceci and quitter quand même ? GameListItemCompat + Ingame + En jeu + + + + Game starts, but crashes or major glitches prevent it from being completed. + Le jeu se lance, mais crash ou des bugs majeurs l'empêchent d'être complété. + + + Perfect Parfait - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - Le jeu fonctionne parfaitement, de manière fluide sans aucun bug audio ou graphique, toutes les fonctionnalités testées fonctionnent comme prévu sans -aucune modification nécessaire. - - - - Great - Bon - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - Le jeu fonctionne correctement avec des bugs audio ou graphiques mineurs et est jouable du début à la fin. Nécessite peut être des -modifications - - Okay - Ok - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - Le jeu fonctionne avec des bugs audio ou graphiques majeurs, mais il est jouable du début à la fin avec des modifications. + Game can be played without issues. + Le jeu peut être joué sans problèmes. - Bad - Mauvais + Playable + Jouable - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Le jeu fonctionne mais avec des bugs audio et graphiques majeurs. Impossible de progresser dans certaines zones à causes des bugs -même avec des modifications. + Game functions with minor graphical or audio glitches and is playable from start to finish. + Le jeu fonctionne avec des glitchs graphiques ou audio mineurs et est jouable du début à la fin. - + Intro/Menu Intro/Menu - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Le jeu est complètement injouable à cause de bugs audio et graphiques. Impossible de progresser plus loin que l'écran de démarrage. + + Game loads, but is unable to progress past the Start Screen. + Le jeu charge, mais ne peut pas progresser après le menu de démarrage. - + Won't Boot Ne démarre pas - + The game crashes when attempting to startup. Le jeu crash au lancement. - + Not Tested Non testé - + The game has not yet been tested. Le jeu n'a pas encore été testé. @@ -5433,7 +5598,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Double-cliquez pour ajouter un nouveau dossier à la liste de jeux @@ -5446,12 +5611,12 @@ Screen. %1 sur %n résultat%1 sur %n résultats%1 sur %n résultats - + Filter: Filtre : - + Enter pattern to filter Entrez un motif à filtrer @@ -5486,7 +5651,7 @@ Screen. (Leave blank for open game) - + (Laisser vide pour ouvrir le jeu) @@ -5506,7 +5671,7 @@ Screen. Load Previous Ban List - + Charger la liste de bannissement précédente @@ -5527,25 +5692,27 @@ Screen. HostRoomWindow - + Error Erreur - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - + Échec de l'annonce de la salle dans le hall public. Pour héberger une salle publiquement, vous devez avoir un compte yuzu valide configuré dans Emulation -> Configurer -> Web. Si vous ne souhaitez pas publier une salle dans le hall public, sélectionnez plutôt Non Répertorié. +Message de débogage : Hotkeys - + Audio Mute/Unmute - + Désactiver/Activer le Son + @@ -5567,114 +5734,113 @@ Debug Message: - Main Window - + Fenêtre Principale + + + + Audio Volume Down + Baisser le volume audio - Audio Volume Down - + Audio Volume Up + Augmenter le volume audio - Audio Volume Up - - - - Capture Screenshot Prendre une capture d'ecran - + Change Adapting Filter - + Modifier le filtre d'adaptation + + + + Change Docked Mode + Changer le mode de la station d'accueil - Change Docked Mode - + Change GPU Accuracy + Modifier la précision du GPU - Change GPU Accuracy - - - - Continue/Pause Emulation Continuer/Suspendre l'Émulation - + Exit Fullscreen Quitter le plein écran - + Exit yuzu Quitter yuzu - + Fullscreen Plein écran - + Load File Charger un fichier - + Load/Remove Amiibo Charger/Supprimer un Amiibo - + Restart Emulation Redémarrer l'Émulation - + Stop Emulation Arrêter l'Émulation - + TAS Record - + Enregistrement TAS + + + + TAS Reset + Réinitialiser le TAS - TAS Reset - + TAS Start/Stop + Démarrer/Arrêter le TAS - TAS Start/Stop - + Toggle Filter Bar + Activer la barre de filtre - Toggle Filter Bar - + Toggle Framerate Limit + Activer la limite de fréquence d'images - Toggle Framerate Limit - + Toggle Mouse Panning + Activer le panoramique de la souris - Toggle Mouse Panning - - - - Toggle Status Bar - + Activer la barre d'état @@ -5784,45 +5950,45 @@ Debug Message: Refresh Lobby - + Rafraichir le menu - + Password Required to Join - + Mot de passe requis pour rejoindre - + Password: Mot de passe: - - Room Name - Nom du salon - - - - Preferred Game - Jeu préféré - - - - Host - Hôte - - - + Players Joueurs - + + Room Name + Nom du salon + + + + Preferred Game + Jeu préféré + + + + Host + Hôte + + + Refreshing Rafraîchissement - + Refresh List Rafraîchir la liste @@ -5845,232 +6011,237 @@ Debug Message: &Fichiers récents - + &Emulation &Émulation - + &View &Vue - + &Reset Window Size &Réinitialiser la taille de la fenêtre - + &Debugging &Débogage - + Reset Window Size to &720p &Réinitialiser la taille de la fenêtre à 720p - + Reset Window Size to 720p Réinitialiser la taille de la fenêtre à 720p - + Reset Window Size to &900p Réinitialiser la taille de la fenêtre à &900p - + Reset Window Size to 900p Réinitialiser la taille de la fenêtre à 900p - + Reset Window Size to &1080p Réinitialiser la taille de la fenêtre à &1080p - + Reset Window Size to 1080p Réinitialiser la taille de la fenêtre à 1080p - + + &Multiplayer + &Multijoueur + + + &Tools &Outils - + &TAS &TAS - + &Help &Aide - + &Install Files to NAND... &Installer des fichiers sur la NAND... - + L&oad File... &Charger un fichier... - + Load &Folder... &Charger un dossier - + E&xit Q&uitter - + &Pause &Pause - + &Stop &Arrêter - + &Reinitialize keys... &Réinitialiser les clés... - + &About yuzu &À propos de yuzu - + Single &Window Mode &Mode fenêtre unique - + Con&figure... &Configurer... - + Display D&ock Widget Headers &Afficher les en-têtes du widget Dock - + Show &Filter Bar &Afficher la barre de filtre - + Show &Status Bar &Afficher la barre d'état - + Show Status Bar Afficher la barre d'état - - - Browse Public Game Lobby - - - - - Create Room - Créer un salon - - Leave Room - Quitter le salon + &Browse Public Game Lobby + &Parcourir le menu des jeux publics - - Direct Connect to Room - + + &Create Room + &Créer une room - - Show Current Room - + + &Leave Room + &Quitter la room + &Direct Connect to Room + &Connexion directe à la room + + + + &Show Current Room + &Afficher la room actuelle + + + F&ullscreen P&lein écran - + &Restart &Redémarrer - + Load/Remove &Amiibo... Charger/Retirer &Amiibo… - + &Report Compatibility &Signaler la compatibilité - + Open &Mods Page Ouvrir la &Page des Mods - + Open &Quickstart Guide Ouvrir le &Guide de Démarrage rapide - + &FAQ &FAQ - + Open &yuzu Folder Ouvrir le &Dossier de Yuzu - + &Capture Screenshot &Capture d'écran - + &Configure TAS... &Configurer TAS... - + Configure C&urrent Game... Configurer le J&eu actuel... - + &Start &Démarrer - + &Reset &Réinitialiser - + R&ecord En&registrer @@ -6088,12 +6259,12 @@ Debug Message: Moderation - + Modération Ban List - + Liste de bannissement @@ -6104,22 +6275,22 @@ Debug Message: Unban - + Débannir Subject - + Sujet Type - + Type Forum Username - + Nom d'utilisateur du forum @@ -6135,45 +6306,41 @@ Debug Message: MultiplayerState - - + Current connection status - + État actuel de la connexion - - + Not Connected. Click here to find a room! - + Pas connecté. Cliquez ici pour trouver un salon ! - - - Connected - Connecté - - - - Not Connected Non Connecté - + + Connected + Connecté + + + + New Messages Received + Nouveaux messages reçus + + + Error Erreur - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - - - New Messages Received - + Impossible de mettre à jour les informations de la room. Veuillez vérifier votre connexion internet et d'héberger la room à nouveau. +Message de Débogage : @@ -6181,62 +6348,62 @@ Debug Message: Username is not valid. Must be 4 to 20 alphanumeric characters. - + Le nom d'utilisateur n'est pas valide. Doit être de 4 à 20 caractères alphanumériques. Room name is not valid. Must be 4 to 20 alphanumeric characters. - + Le nom du salon n'est pas valide. Doit être de 4 à 20 caractères alphanumériques. Username is already in use or not valid. Please choose another. - + Le nom d'utilisateur est déjà utilisé ou n'est pas valide. Veuillez en sélectionner un autre. IP is not a valid IPv4 address. - + IP n'est pas une adresse IPv4 valide. Port must be a number between 0 to 65535. - + Le port doit être un nombre compris entre 0 et 65535. You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list. - + Vous devez choisir un jeu préféré pour héberger un salon. Si vous n'avez pas encore de jeux dans votre liste de jeux, ajoutez un dossier de jeu en cliquant sur l'icône plus dans la liste de jeux. Unable to find an internet connection. Check your internet settings. - + Impossible de trouver une connexion Internet. Vérifiez vos paramètres Internet. Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded. - + Impossible de se connecter à l'hôte. Vérifiez que les paramètres de connexion sont corrects. Si vous ne parvenez toujours pas à vous connecter, contactez l'hôte de la salle et vérifiez que l'hôte a correctement configuré le port externe redirigé. Unable to connect to the room because it is already full. - + Impossible de se connecter au salon car il est déjà plein. Creating a room failed. Please retry. Restarting yuzu might be necessary. - + La création d'un salon a échoué. Veuillez réessayer. Peut être que vous devriez redémarrer yuzu. The host of the room has banned you. Speak with the host to unban you or try a different room. - + L'hôte du salon vous a banni. Parlez à l'hôte pour vous débannir ou essayez un autre salon. Version mismatch! Please update to the latest version of yuzu. If the problem persists, contact the room host and ask them to update the server. - + Décalage de version ! Veuillez mettre à jour la dernière version de yuzu. Si le problème persiste, contactez l'hôte de la salle et demandez lui de mettre à jour le serveur. @@ -6246,59 +6413,79 @@ Debug Message: An unknown error occurred. If this error continues to occur, please open an issue - + Une erreur inconnue s'est produite. Si cette erreur continue d'arriver, veuillez faire un rapport Connection to room lost. Try to reconnect. - + Connexion au salon perdue. Essayez de vous reconnecter. You have been kicked by the room host. - + Vous avez été expulsé par l'hôte du salon. IP address is already in use. Please choose another. - + L'adresse IP est déjà utilisée. Veuillez en sélectionner une autre. You do not have enough permission to perform this action. - + Vous ne disposez pas des autorisations suffisantes pour effectuer cette action. The user you are trying to kick/ban could not be found. They may have left the room. - + L'utilisateur que vous essayez d'exclure/bannir est introuvable. +Il a peut-être quitté la salon. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + Aucune interface réseau valide n'est séléctionnée. +Veuillez aller dans Configurer -> Système -> Réseau et faites un choix. + + + + Game already running + Jeu déjà en cours + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + Rejoindre un salon lorsque le jeu est déjà en cours d'exécution est déconseillé et peut empêcher le salon de fonctionner correctement. +Continuer quand même ? + + + Leave Room Quitter le salon - + You are about to close the room. Any network connections will be closed. - + Vous êtes sur le point de fermer le salon. Toutes les connexions réseau seront fermées. - + Disconnect - + Déconnecter - + You are about to leave the room. Any network connections will be closed. - + Vous êtes sur le point de quitter le salon. Toutes les connexions réseau seront fermées. NetworkMessage::ErrorManager - + Error Erreur @@ -6347,42 +6534,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 ne joue pas à un jeu - + %1 is playing %2 - + %1 joue à %2 - + Not playing a game Ne joue pas à un jeu - + Installed SD Titles Titres installés sur la SD - + Installed NAND Titles Titres installés sur la NAND - + System Titles Titres Système - + Add New Game Directory Ajouter un nouveau répertoire de jeu - + Favorites Favoris @@ -6412,7 +6599,7 @@ p, li { white-space: pre-wrap; } - + [not set] [non défini] @@ -6427,10 +6614,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Axe %1%2 @@ -6444,9 +6631,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [inconnu] @@ -6611,15 +6798,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [invalide] - - + + %1%2Hat %3 %1%2Chapeau %3 @@ -6627,35 +6814,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Axe %3 - + %1%2Axis %3,%4,%5 %1%2Axe %3,%4,%5 - + %1%2Motion %3 %1%2Mouvement %3 - - + + %1%2Button %3 %1%2Bouton %3 - + [unused] [inutilisé] @@ -6696,11 +6883,124 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + Paramètres Amiibo + + + + Amiibo Info + Info de l'Amiibo + + + + Series + Séries + + + + Type + Type + + + + Name + Nom + + + + Amiibo Data + Données de l'Amiibo + + + + Custom Name + Nom personnalisé + + + + Owner + Propriétaire + + + + Creation Date + Date de Création + + + + dd/MM/yyyy + JJ/MM/AAAA + + + + Modification Date + Date de Modification + + + + dd/MM/yyyy + JJ/MM/AAAA + + + + Game Data + Données du Jeu + + + + Game Id + Id du Jeu + + + + Mount Amiibo + Monter Amiibo + + + + ... + ... + + + + File Path + Chemin du fichier + + + + No game data present + Aucune données de jeu présent + + + + The following amiibo data will be formatted: + Les données de cet Amiibo vont être formatées : + + + + The following game data will removed: + Les données de ce jeu vont être enlevés : + + + + Set nickname and owner: + Mettre un surnom et un propriétaire + + + + Do you wish to restore this amiibo? + Voulez-vous restaurer cet Amiibo ? + + QtControllerSelectorDialog @@ -6804,6 +7104,7 @@ p, li { white-space: pre-wrap; } + Handheld Portable @@ -6843,11 +7144,6 @@ p, li { white-space: pre-wrap; } Docked Mode TV - - - Undocked - Mode portable - Vibration diff --git a/dist/languages/id.ts b/dist/languages/id.ts index 7a9b2a6..91ee85f 100644 --- a/dist/languages/id.ts +++ b/dist/languages/id.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -204,7 +204,7 @@ This would ban both their forum username and their IP address. - %1 (%2/%3 members) - connected + %1 - %2 (%3/%4 members) - connected @@ -218,6 +218,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Laporkan Kekompatibelan Permainan @@ -227,92 +232,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Anda akan mengirimkan berkas hasil uji ke </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Daftar Kekompatibelan yuzu</span></a><span style=" font-size:10pt;">, Informasi berikut akan dikumpulkan dan ditampilkan pada situs:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informasi perangkat keras (CPU / GPU / Sistem Operasi)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Versi yuzu yang Anda jalankan</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Akun yuzu yang tersambung</li></ul></body></html> - - Perfect - Sempurna + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Permainan berfungsi dengan mulus tanpa kecacatan audio atau grafis.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Bagus + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Permainan berfungsi dengan sedikit kecacatan grafis atau audio dan dapat dimainkan dari awal hingga selesai. Mungkin membutuhkan beberapa oprekan.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Oke + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Permainan berfungsi dengan kecacatan grafis atau audio berjumlah besar, namun dapat dimainkan dari awal hingga selesai dengan menggunakan beberapa oprekan.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Buruk + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Permainan berfungsi, namun memiliki kecacatan grafis atau audio dalam jumlah besar. Tak dapat melanjutkan kelajuan pada area spesifik dikarenakan kecacatan bahkan dengan menggunakan oprekan.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Awal/Menu + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Permainan sepenuhnya todak dapat dimainkan karena kecacatan grafis dan audio dalam jumlah besar. Tak dapat melanjutkan setelah Layar Permulaan.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Tidak Memulai Sama Sekali + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Permainan mogok ketika mencoba dijalankan.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Terlepas dari kecepatan dan kinerka, bagaimana permainan ini berjalan dari awal hingga selesai dalam versi yuzu saat ini?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Terima kasih atas pengajuan yang Anda kirimkan! - + Submitting Mengajukan - + Communication error Kesalahan komunikasi - + An error occurred while sending the Testcase Terjadi kesalahan saat mengirim Testcase - + Next Berikutnya @@ -410,7 +450,7 @@ This would ban both their forum username and their IP address. Kembalikan ke Semula - + Auto Otomatis @@ -730,200 +770,235 @@ Memungkinkan berbagai macam optimasi IR. ConfigureDebug - + Debugger - + Enable GDB Stub Aktifkan GDB Stub - + Port: Port: - + Logging Pencatatan - + Global Log Filter Catatan Penyaring Global - + Show Log in Console Tampilkan Catatan Di Konsol - + Open Log Location Buka Lokasi Catatan - + When checked, the max size of the log increases from 100 MB to 1 GB Saat dicentang, ukuran maksimum log meningkat dari 100 MB menjadi 1 GB - + Enable Extended Logging** Aktifkan Pencatatan Yang Diperluas** - + Homebrew Homebrew - + Arguments String String Argumen - + Graphics Grafis - + When checked, the graphics API enters a slower debugging mode - + Enable Graphics Debugging Nyalakan Pengawakutuan Grafis - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath Aktifkan Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Saat dicentang, ini menonaktifkan kompiler makro Just In Time. Mengaktifkan ini membuat game berjalan lebih lambat - + Disable Macro JIT Matikan Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Saat dinyalakan, yuzu akan mencatat statstik tentang pipeline cache yang disusun - + Enable Shader Feedback Nyalakan Umpan Balik Shader - + When checked, it executes shaders without loop logic changes Saat dinyalakan, akan menjalankan shader tanpa perubahan logika loop - + Disable Loop safety checks Matikan cek keamanan Loop - + Debugging Pengawakutuan - - Enable FS Access Log - Nyalakan Log Akses FS - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Nyalakan Layanan Laporan Bertele-tele** - + + Enable FS Access Log + Nyalakan Log Akses FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Lanjutan - + Kiosk (Quest) Mode Mode Kiosk (Pencarian) - + Enable CPU Debugging Nyalakan Pengawakutuan CPU - + Enable Debug Asserts Nyalakan Awakutu Assert - + Enable Auto-Stub** - + Enable All Controller Types Aktifkan Semua Jenis Controller - + Disable Web Applet Matikan Applet Web - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Ini akan diatur ulang secara otomatis ketika yuzu ditutup. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1293,193 +1368,218 @@ Memungkinkan berbagai macam optimasi IR. API: - + Graphics Settings Pengaturan Grafis - + Use disk pipeline cache Gunakan pipeline cache diska - + Use asynchronous GPU emulation Gunakan pengemulasian GPU yang asinkron - + Accelerate ASTC texture decoding Percepat penguraian tekstur ASTC - + NVDEC emulation: Emulasi NVDEC: - + No Video Output Tidak ada Keluaran Suara - + CPU Video Decoding Penguraian Video menggunakan CPU - + GPU Video Decoding (Default) Penguraian Video menggunakan GPU (Bawaan) - + Fullscreen Mode: Mode Layar Penuh: - + Borderless Windowed Layar Tanpa Batas - + Exclusive Fullscreen Layar Penuh Eksklusif - + Aspect Ratio: Rasio Aspek: - + Default (16:9) Bawaan (16:9) - + Force 4:3 Paksa 4:3 - + Force 21:9 Paksa 21:9 - + + Force 16:10 + + + + Stretch to Window Regangkan ke Layar - + Resolution: Resolusi: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EKSPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EKSPERIMENTAL] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: Filter Menyelaraskan dengan Layar: - + Nearest Neighbor Nearest Neighbor - + Bilinear Biliner - + Bicubic Bikubik - + Gaussian Gaussian - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) - + Anti-Aliasing Method: Metode Anti-Aliasing: - + None Tak ada - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Gunakan warna latar global - + Set background color: Setel warna latar: - + Background Color: Warna Latar: @@ -1488,6 +1588,12 @@ Memungkinkan berbagai macam optimasi IR. GLASM (Assembly Shaders, NVIDIA Only) GLASM (Shader perakit, hanya NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1542,37 +1648,47 @@ Memungkinkan berbagai macam optimasi IR. - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotropic Filtering: - + Automatic Otomatis - + Default Bawaan - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2064,7 +2180,7 @@ Memungkinkan berbagai macam optimasi IR. - + Left Stick Stik Kiri @@ -2158,14 +2274,14 @@ Memungkinkan berbagai macam optimasi IR. - + L L - + ZL ZL @@ -2184,7 +2300,7 @@ Memungkinkan berbagai macam optimasi IR. - + Plus Tambah @@ -2197,15 +2313,15 @@ Memungkinkan berbagai macam optimasi IR. - + R R - + ZR ZR @@ -2262,231 +2378,236 @@ Memungkinkan berbagai macam optimasi IR. - + Right Stick Stik Kanan - - - - + + + + Clear Bersihkan - - - - - + + + + + [not set] [belum diatur] - - - Toggle button - Atur tombol - - - - + + Invert button Balikkan tombol - - + + + Toggle button + Atur tombol + + + + Invert axis Balikkan poros - - - + + + Set threshold Atur batasan - - + + Choose a value between 0% and 100% Pilih sebuah angka diantara 0% dan 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Petakan Stik Analog - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Setelah menekan OK, pertama gerakkan joystik secara mendatar, lalu tegak lurus. Untuk membalikkan sumbu, pertama gerakkan joystik secara tegak lurus, lalu mendatar. - + Center axis - - + + Deadzone: %1% Titik Mati: %1% - - + + Modifier Range: %1% Rentang Pengubah: %1% - - + + Pro Controller Kontroler Pro - + Dual Joycons Joycon Dual - + Left Joycon Joycon Kiri - + Right Joycon Joycon Kanan - + Handheld Jinjing - + GameCube Controller Kontroler GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Kontroler NES - + SNES Controller Kontroler SNES - + N64 Controller Kontroler N64 - + Sega Genesis Sega Genesis - + Start / Pause Mulai / Jeda - + Z Z - + Control Stick Stik Kendali - + C-Stick C-Stick - + Shake! Getarkan! - + [waiting] [menunggu] - + New Profile Profil Baru - + Enter a profile name: Masukkan nama profil: - - + + Create Input Profile Ciptakan Profil Masukan - + The given profile name is not valid! Nama profil yang diberi tidak sah! - + Failed to create the input profile "%1" Gagal membuat profil masukan "%1" - + Delete Input Profile Hapus Profil Masukan - + Failed to delete the input profile "%1" Gagal menghapus profil masukan "%1" - + Load Input Profile Muat Profil Masukan - + Failed to load the input profile "%1" Gagal memuat profil masukan "%1" - + Save Input Profile Simpat Profil Masukan - + Failed to save the input profile "%1" Gagal menyimpan profil masukan "%1" @@ -2741,42 +2862,42 @@ Untuk membalikkan sumbu, pertama gerakkan joystik secara tegak lurus, lalu menda Pengembang - + Add-Ons Pengaya (Add-On) - + General Umum - + System Sistem - + CPU CPU - + Graphics Grafis - + Adv. Graphics Ljtan. Grafik - + Audio Audio - + Properties Properti @@ -2832,37 +2953,37 @@ Untuk membalikkan sumbu, pertama gerakkan joystik secara tegak lurus, lalu menda Pengguna Saat Ini - + Username Nama Pengguna - + Set Image Atur Gambar - + Add Tambahkan - + Rename Ubah Nama - + Remove Singkirkan - + Profile management is available only when game is not running. Pengelolaan profil hanya tersedia saat permainan tidak dijalankan. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2870,96 +2991,105 @@ Untuk membalikkan sumbu, pertama gerakkan joystik secara tegak lurus, lalu menda %2 - + Enter Username Masukkan Nama Pengguna - + Users Pengguna - + Enter a username for the new user: Masukkan nama pengguna untuk pengguna baru: - + Enter a new username: Masukkan nama pengguna baru: - - Confirm Delete - Konfirmasi Penghapusan - - - - You are about to delete user with name "%1". Are you sure? - Anda akan menghapus pengguna dengan nama "%1". Apakah Anda yakin? - - - + Select User Image Pilih Gambar Pengguna - + JPEG Images (*.jpg *.jpeg) Gambar JPEG (*.jpg *.jpeg) - + Error deleting image Kesalahan ketika menghapus gambar - + Error occurred attempting to overwrite previous image at: %1. Kesalahan saat mencoba menimpa gambar sebelumnya di: %1. - + Error deleting file Kesalahan saat menghapus berkas - + Unable to delete existing file: %1. Tak dapat menghapus berkas yang ada: %1. - + Error creating user image directory Kesalahan saat menciptakan direktori pengguna - + Unable to create directory %1 for storing user images. Tidak bisa menciptakan direktori %1 untuk menyimpan gambar pengguna. - + Error copying user image Kesalahan ketika menyalin gambar pengguna - + Unable to copy image from %1 to %2 Gagal menyalin berkas dari %1 ke %2 - + Error resizing user image Kesalahan ketika mengubah ukuran gambar pengguna - + Unable to resize image Tidak dapat mengubah ukuran gambar + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Konfirmasi Penghapusan + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3488,47 +3618,47 @@ Untuk membalikkan sumbu, pertama gerakkan joystik secara tegak lurus, lalu menda - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings Pengaturan - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Jalur - + ... ... @@ -3779,56 +3909,71 @@ Drag points to change position, or double-click table cells to edit values. + Show Compatibility List + + + + Show Add-Ons Column Tampilkan Kolom Pengaya (Add-On) - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: Ukuran Ikon Game: - + Folder Icon Size: Ukuran Ikon Folder: - + Row 1 Text: Teks Baris 1: - + Row 2 Text: Teks Baris 2: - + Screenshots Screenshot - + Ask Where To Save Screenshots (Windows Only) Menanya Dimana Untuk Menyimpan Screenshot (Hanya untuk Windows) - + Screenshots Path: Jalur Screenshot: - + ... ... - + Select Screenshots Path... Pilih Jalur Screenshot... - + <System> <System> @@ -3937,7 +4082,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify Verifikasi @@ -4024,7 +4169,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified Tak dispesifikasikan @@ -4039,17 +4184,36 @@ Drag points to change position, or double-click table cells to edit values.Token tidak terverifikasi. Perubahan ke token Anda tak tersimpan - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Memverifikasi... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verifikasi gagal + + + Verification failed Verifikasi gagal - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Pemverifikasian gagal. Periksa apakah token yang dimasukkan sudah benar dan apakah koneksi internet Anda berjalan. @@ -4118,12 +4282,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting - + Connect @@ -4131,911 +4295,913 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Data anonim dikumpulkan</a> untuk membantu yuzu. <br/><br/>Apa Anda ingin membagi data penggunaan dengan kami? - + Telemetry Telemetri - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Memuat Applet Web... - - + + Disable Web Applet Matikan Applet Web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Jumlah shader yang sedang dibuat - + The current selected resolution scaling multiplier. Pengali skala resolusi yang terpilih. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Kecepatan emulasi saat ini. Nilai yang lebih tinggi atau rendah dari 100% menandakan pengemulasian berjalan lebih cepat atau lambat dibanding Switch aslinya. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Berapa banyak frame per second (bingkai per detik) permainan akan ditampilkan. Ini akan berubah dari berbagai permainan dan pemandangan. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Waktu yang diperlukan untuk mengemulasikan bingkai Switch, tak menghitung pembatas bingkai atau v-sync. Agar emulasi berkecepatan penuh, ini harus 16.67 mdtk. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Bersihkan Berkas Baru-baru Ini - + &Continue &Lanjutkan - + &Pause &Jeda - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu sedang menjalankan game - + Warning Outdated Game Format Peringatan Format Permainan yang Usang - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Anda menggunakan format direktori ROM yang sudah didekonstruksi untuk permainan ini, yang mana itu merupakan format lawas yang sudah tergantikan oleh yang lain seperti NCA, NAX, XCI, atau NSP. Direktori ROM yang sudah didekonstruksi kekurangan ikon, metadata, dan dukungan pembaruan.<br><br>Untuk penjelasan berbagai format Switch yang didukung yuzu, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>periksa wiki kami</a>. Pesan ini tidak akan ditampilkan lagi. - - + + Error while loading ROM! Kesalahan ketika memuat ROM! - + The ROM format is not supported. Format ROM tak didukung. - + An error occurred initializing the video core. Terjadi kesalahan ketika menginisialisasi inti video. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu telah mengalami error saat menjalankan inti video. Ini biasanya disebabkan oleh pemicu piranti (driver) GPU yang usang, termasuk yang terintegrasi. Mohon lihat catatan untuk informasi lebih rinci. Untuk informasi cara mengakses catatan, mohon lihat halaman berikut: <a href='https://yuzu-emu.org/help/reference/log-files/'>Cara Mengupload Berkas Catatan</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. Terjadi kesalahan yang tak diketahui. Mohon lihat catatan untuk informasi lebih rinci. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Simpan Data - + Mod Data Mod Data - + Error Opening %1 Folder Gagal Membuka Folder %1 - - + + Folder does not exist! Folder tak ada! - + Error Opening Transferable Shader Cache Gagal Ketika Membuka Tembolok Shader yang Dapat Ditransfer - + Failed to create the shader cache directory for this title. - - Contents - Konten - - - - Update - Perbaharui - - - - DLC - DLC - - - - Remove Entry - Hapus Masukan - - - - Remove Installed Game %1? - - - - - - - - - Successfully Removed + Error Removing Contents - - Successfully removed the installed base game. - - - - - - - Error Removing %1 - - - - - The base game is not installed in the NAND and cannot be removed. - - - - - Successfully removed the installed update. + + Error Removing Update + Error Removing DLC + + + + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + + Remove Entry + Hapus Masukan + + + + + + + + + Successfully Removed + + + + + Successfully removed the installed base game. + + + + + The base game is not installed in the NAND and cannot be removed. + + + + + Successfully removed the installed update. + + + + There is no update installed for this title. - + There are no DLC installed for this title. Tidak ada DLC yang terinstall untuk judul ini. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File Hapus File - - + + Error Removing Transferable Shader Cache Kesalahan Menghapus Transferable Shader Cache - - + + A shader cache for this title does not exist. Cache shader bagi judul ini tidak ada - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration Kesalahan Menghapus Konfigurasi Buatan - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! Pengekstrakan RomFS Gagal! - + There was an error copying the RomFS files or the user cancelled the operation. Terjadi kesalahan ketika menyalin berkas RomFS atau dibatalkan oleh pengguna. - + Full Penuh - + Skeleton Skeleton - + Select RomFS Dump Mode Pilih Mode Dump RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Mohon pilih cara RomFS akan di-dump.<br>FPenuh akan menyalin seluruh berkas ke dalam direktori baru sementara <br>jerangkong hanya akan menciptakan struktur direktorinya saja. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Mengekstrak RomFS... - - + + Cancel Batal - + RomFS Extraction Succeeded! Pengekstrakan RomFS Berhasil! - + The operation completed successfully. Operasi selesai dengan sukses, - + Error Opening %1 Gagal membuka %1 - + Select Directory Pilih Direktori - + Properties Properti - + The game properties could not be loaded. Properti permainan tak dapat dimuat. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Eksekutabel Switch (%1);;Semua Berkas (*.*) - + Load File Muat Berkas - + Open Extracted ROM Directory Buka Direktori ROM Terekstrak - + Invalid Directory Selected Direktori Terpilih Tidak Sah - + The directory you have selected does not contain a 'main' file. Direktori yang Anda pilih tak memiliki berkas 'utama.' - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Install File - + %n file(s) remaining - + Installing file "%1"... Memasang berkas "%1"... - - + + Install Results Hasil Install - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed %n file(s) baru diinstall - + %n file(s) were overwritten %n file(s) telah ditimpa - + %n file(s) failed to install %n file(s) gagal di install - + System Application Aplikasi Sistem - + System Archive Arsip Sistem - + System Application Update Pembaruan Aplikasi Sistem - + Firmware Package (Type A) Paket Perangkat Tegar (Tipe A) - + Firmware Package (Type B) Paket Perangkat Tegar (Tipe B) - + Game Permainan - + Game Update Pembaruan Permainan - + Game DLC DLC Permainan - + Delta Title Judul Delta - + Select NCA Install Type... Pilih Tipe Pemasangan NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Mohon pilih jenis judul yang Anda ingin pasang sebagai NCA ini: (Dalam kebanyakan kasus, pilihan bawaan 'Permainan' tidak apa-apa`.) - + Failed to Install Gagal Memasang - + The title type you selected for the NCA is invalid. Jenis judul yang Anda pilih untuk NCA tidak sah. - + File not found Berkas tak ditemukan - + File "%1" not found Berkas "%1" tak ditemukan - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Akun yuzu Hilang - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Agar dapat mengirimkan berkas uju kompatibilitas permainan, Anda harus menautkan akun yuzu Anda.<br><br/>TUntuk mennautkan akun yuzu Anda, pergi ke Emulasi &gt; Konfigurasi &gt; Web. - + Error opening URL Kesalahan saat membuka URL - + Unable to open the URL "%1". Tidak dapat membuka URL "%1". - + TAS Recording Rekaman TAS - + Overwrite file of player 1? Timpa file pemain 1? - + Invalid config detected Konfigurasi tidak sah terdeteksi - + Handheld controller can't be used on docked mode. Pro controller will be selected. Kontroller jinjing tidak bisa digunakan dalam mode dock. Kontroller Pro akan dipilih - - - Error - - - - - - The current game is not looking for amiibos - - - - - + + Amiibo - - + + The current amiibo has been removed - + + Error + + + + + + The current game is not looking for amiibos + + + + Amiibo File (%1);; All Files (*.*) Berkas Amiibo (%1);; Semua Berkas (*.*) - + Load Amiibo Muat Amiibo - - Error opening Amiibo data file - Gagal membuka berkas data Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Tak dapat membuka berkas Amiibo "%1" untuk dibaca. - - - - Error reading Amiibo data file - Gagal membaca berkas data Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Tak dapat membaca data Amiibo sepenuhnya. Diperkirakan membaca %1 bita, namun hanya terbaca %2 bita. - - - + Error loading Amiibo data Gagal memuat data Amiibo - - Unable to load Amiibo data. - Tak dapat memuat data Amiibo + + The selected file is not a valid amiibo + - + + The selected file is already on use + + + + + An unknown error occurred + + + + Capture Screenshot Tangkapan Layar - + PNG Image (*.png) Berkas PNG (*.png) - + TAS state: Running %1/%2 Status TAS: Berjalan %1/%2 - + TAS state: Recording %1 Status TAS: Merekam %1 - + TAS state: Idle %1/%2 Status TAS: Diam %1/%2 - + TAS State: Invalid Status TAS: Tidak Valid - + &Stop Running &Matikan - + &Start &Mulai - + Stop R&ecording Berhenti Mer&ekam - + R&ecord R&ekam - + Building: %n shader(s) Membangun: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% Kecepatan: %1% / %2% - + Speed: %1% Kecepatan: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Permainan: %1 FPS - + Frame: %1 ms Frame: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU TINGGI - + GPU EXTREME GPU EKSTRIM - + GPU ERROR KESALAHAN GPU - + DOCKED - + HANDHELD - + NEAREST NEAREST - - + + BILINEAR BILINEAR - + BICUBIC BICUBIC - + GAUSSIAN GAUSSIAN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA TANPA AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + yuzu was unable to locate a Switch system archive. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 - + System Archive Not Found - + System Archive Missing - + yuzu was unable to locate the Switch shared fonts. %1 - + Shared Fonts Not Found - + Shared Font Missing - + Fatal Error Kesalahan Fatal - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. - + Fatal Error encountered - + Confirm Key Rederivation - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5046,76 +5212,76 @@ This will delete your autogenerated key files and re-run the key derivation modu - + Missing fuses - + - Missing BOOT0 - Kehilangan BOOT0 - + - Missing BCPKG2-1-Normal-Main - Kehilangan BCPKG2-1-Normal-Main - + - Missing PRODINFO - Kehilangan PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. - + Deriving Keys - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close yuzu? Apakah anda yakin ingin menutup yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5125,38 +5291,38 @@ Would you like to bypass this and exit anyway? GRenderWindow - + OpenGL not available! OpenGL tidak tersedia! - + yuzu has not been compiled with OpenGL support. - - + + Error while initializing OpenGL! Terjadi kesalahan menginisialisasi OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. VGA anda mungkin tidak mendukung OpenGL, atau anda tidak memiliki pemacu piranti (driver) grafis terbaharu. - + Error while initializing OpenGL 4.6! Terjadi kesalahan menginisialisasi OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 VGA anda mungkin tidak mendukung OpenGL 4.6, atau anda tidak memiliki pemacu piranti (driver) grafis terbaharu.<br><br>Pemuat GL:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 VGA anda mungkin tidak mendukung satu atau lebih ekstensi OpenGL. Mohon pastikan bahwa anda memiliki pemacu piranti (driver) grafis terbaharu.<br><br>Pemuat GL:<br>%1<br><br>Ekstensi yang tidak didukung:<br>%2 @@ -5164,153 +5330,153 @@ Would you like to bypass this and exit anyway? GameList - + Favorite Favorit - + Start Game - + Start Game without Custom Configuration - + Open Save Data Location Buka Lokasi Data Penyimpanan - + Open Mod Data Location Buka Lokasi Data Mod - + Open Transferable Pipeline Cache - + Remove Singkirkan - + Remove Installed Update - + Remove All Installed DLC - + Remove Custom Configuration - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents - + Dump RomFS - + Dump RomFS to SDMC - + Copy Title ID to Clipboard - + Navigate to GameDB entry - + Properties Properti - + Scan Subfolders - + Remove Game Directory - + ▲ Move Up - + ▼ Move Down - + Open Directory Location - + Clear Bersihkan - + Name Nama - + Compatibility Kompatibilitas - + Add-ons Pengaya (Add-On) - + File type - + Size Ukuran @@ -5319,76 +5485,61 @@ Would you like to bypass this and exit anyway? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Sempurna - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - - - - - Great - Bagus - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - - - Okay - OK - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. + Game can be played without issues. - Bad - Buruk - - - - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. + Playable - + + Game functions with minor graphical or audio glitches and is playable from start to finish. + + + + Intro/Menu Awal/Menu - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. + + Game loads, but is unable to progress past the Start Screen. - + Won't Boot Tidak Akan Berjalan - + The game crashes when attempting to startup. - + Not Tested - + The game has not yet been tested. @@ -5396,7 +5547,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list @@ -5409,12 +5560,12 @@ Screen. - + Filter: - + Enter pattern to filter @@ -5490,12 +5641,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5504,11 +5655,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5530,112 +5682,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Tangkapan Layar - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen - + Load File Muat Berkas - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5749,42 +5900,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Pemain - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5807,232 +5958,237 @@ Debug Message: - + &Emulation &Emulasi - + &View - + &Reset Window Size - + &Debugging - + Reset Window Size to &720p - + Reset Window Size to 720p - + Reset Window Size to &900p - + Reset Window Size to 900p - + Reset Window Size to &1080p - + Reset Window Size to 1080p - + + &Multiplayer + + + + &Tools - + &TAS - + &Help - + &Install Files to NAND... - + L&oad File... - + Load &Folder... - + E&xit - + &Pause &Jeda - + &Stop - + &Reinitialize keys... - + &About yuzu - + Single &Window Mode - + Con&figure... - + Display D&ock Widget Headers - + Show &Filter Bar - + Show &Status Bar - + Show Status Bar - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room + &Browse Public Game Lobby - - Direct Connect to Room + + &Create Room - - Show Current Room + + &Leave Room - F&ullscreen + &Direct Connect to Room - &Restart + &Show Current Room - Load/Remove &Amiibo... + F&ullscreen - &Report Compatibility + &Restart + Load/Remove &Amiibo... + + + + + &Report Compatibility + + + + Open &Mods Page - + Open &Quickstart Guide - + &FAQ - + Open &yuzu Folder - + &Capture Screenshot - + &Configure TAS... - + Configure C&urrent Game... - + &Start &Mulai - + &Reset - + R&ecord R&ekam @@ -6097,46 +6253,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Terhubung - - - - Not Connected - + + Connected + Terhubung + + + + New Messages Received + + + + Error - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6237,22 +6388,39 @@ They may have left the room. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6260,7 +6428,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6305,42 +6473,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 is playing %2 - + Not playing a game - + Installed SD Titles - + Installed NAND Titles - + System Titles - + Add New Game Directory - + Favorites @@ -6370,7 +6538,7 @@ p, li { white-space: pre-wrap; } - + [not set] [belum diatur] @@ -6385,10 +6553,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 @@ -6402,9 +6570,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] @@ -6569,15 +6737,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6585,35 +6753,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 %1%2Gerakan %3 - - + + %1%2Button %3 - + [unused] @@ -6654,11 +6822,124 @@ p, li { white-space: pre-wrap; } - + %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Nama + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6762,6 +7043,7 @@ p, li { white-space: pre-wrap; } + Handheld Jinjing @@ -6801,11 +7083,6 @@ p, li { white-space: pre-wrap; } Docked Terpasang - - - Undocked - Dilepas - Vibration diff --git a/dist/languages/it.ts b/dist/languages/it.ts index 76d639a..7eed301 100644 --- a/dist/languages/it.ts +++ b/dist/languages/it.ts @@ -95,78 +95,78 @@ p, li { white-space: pre-wrap; } Invia messaggio - + Members Membri - + %1 has joined %1 è entrato - + %1 has left %1 è uscito - + %1 has been kicked %1 è stato espulso - + %1 has been banned %1 è stato bannato - + %1 has been unbanned %1 non è più bannato - + View Profile Visualizza profilo - - + + Block Player Blocca giocatore - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Quando blocchi un giocatore, non riceverai più messaggi da quel giocatore.<br><br>Sei sicuro di voler bloccare %1? - + Kick Espelli - + Ban Banna - + Kick Player Espelli giocatore - + Are you sure you would like to <b>kick</b> %1? Sei sicuro di voler <b>espellere</b> %1? - + Ban Player Banna giocatore - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -212,8 +212,8 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - %1 (%2/%3 members) - connected - %1 (%2/%3 membri) - connesso + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 membri) - connesso @@ -226,6 +226,11 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. + + + + + Report Game Compatibility Segnala la compatibilità del gioco @@ -235,92 +240,127 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP.<html><head/><body><p><span style=" font-size:10pt;">Se dovessi scegliere di inviare una segnalazione alla </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">lista di compatibilità di yuzu</span></a><span style=" font-size:10pt;">, le seguenti informazioni saranno raccolte e visualizzate sul sito: </span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informazioni sull'hardware (CPU / GPU / sistema operativo)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Quale versione di yuzu stai utilizzando</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">L'account di yuzu connesso</li></ul></body></html> - - Perfect - Perfetto + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Il gioco funziona perfettamente senza alcun glitch audio o video.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Ottimo + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Il gioco presenta alcuni glitch audio o video minori ed è possibile giocare dall'inizio alla fine. Potrebbe richiedere l'utilizzo di alcuni espedienti.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Ok + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Il gioco presenta considerevoli glitch audio o video, ma è possibile giocare dall'inizio alla fine utilizzando degli espedienti.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Scadente + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Il gioco presenta considerevoli glitch audio o video. È impossibile progredire in alcune aree a causa della presenza di glitch anche utilizzando degli espedienti.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Intro/Menù + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Questo gioco è completamente ingiocabile a causa di gravi errori audio o video. E' impossibile proseguire oltre la schermata iniziale.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Non si avvia + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Il gioco si blocca quando viene avviato.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Indipendentemente da velocità o prestazioni, come ti è sembrato giocare questo gioco dall'inizio alla fine su questa versione di yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Grazie per la tua segnalazione! - + Submitting Invio in corso - + Communication error Errore di comunicazione - + An error occurred while sending the Testcase Si è verificato un errore durante l'invio della segnalazione - + Next Successivo @@ -418,7 +458,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP.Ripristina valori predefiniti - + Auto Auto @@ -526,7 +566,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - <div>Questa opzione migliora la velocità rimuovendo i controlli NaN. Questo ridurrà l'accuratezza di alcune istruzioni a virgola mobile.</div> + <div>Questa opzione migliora la velocità rimuovendo i controlli NaN. Ciò ridurrà l'accuratezza di alcune istruzioni a virgola mobile.</div> @@ -617,7 +657,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Enable block linking - Abilita il blocco ai collegamenti + Abilita il collegamento dei blocchi @@ -625,12 +665,13 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. -<div>Questa ottimizzazione evita la ricerca del mittente tenendo traccia dei potenziali indirizzi di ritorno delle istruzioni BL. Questo approssima cosa succede con il ritorno dello stack buffer di una vera CPU.</div> + <div>Questa ottimizzazione evita la ricerca del chiamante tenendo traccia dei potenziali indirizzi di ritorno delle istruzioni BL. Questo approssima ciò che succede con il return stack buffer di una vera CPU.</div> + Enable return stack buffer - Abilita il ritorno dello stack buffer + Abilita il return stack buffer @@ -638,7 +679,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - <div>Abilita un sistema di spedizione a due livelli. Un mittente più veloce scritto in assembly, ha una piccola cache MRU di destinazioni di salto è utilizzato per primo. Se ciò non riesce, l'invio ricade sul mittente in C++ più lento.</div> + <div>Abilita un sistema di dispatch a due livelli. Viene usato per primo un dispatcher più veloce scritto in assembly, che ha una piccola cache MRU di destinazioni di salto. Se questo fallisce, viene usato il dispatcher più lento scritto in C++.</div> @@ -652,7 +693,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - <div>Abilita un'ottimizzazione dell'IR che riduce gli accessi non necessari alla struttura di contestuale della CPU.</div> + <div>Abilita un'ottimizzazione dell'IR che riduce gli accessi non necessari alla struttura di contesto della CPU.</div> @@ -666,13 +707,13 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - <div>Abilita le ottimizzazioni dell'IR che comportano una propagazione costante.</div> + <div>Abilita le ottimizzazioni dell'IR che comportano la propagazione delle costanti.</div> Enable constant propagation - Abilita la propagazione costante + Abilita la propagazione delle costanti @@ -680,7 +721,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - <div>Abilita varie ottimizzazioni IR</div> + <div>Abilita varie ottimizzazioni dell'IR.</div> @@ -758,207 +799,242 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. ConfigureDebug - + Debugger Debugger - + Enable GDB Stub Abilita stub GDB - + Port: Porta: - + Logging Logging - + Global Log Filter Filtro log globale - + Show Log in Console Mostra i log nella console - + Open Log Location Apri cartella dei log - + When checked, the max size of the log increases from 100 MB to 1 GB - Quando selezionata, la dimensione massima del log aumenterà da 100 MB a 1 GB + Quando l'opzione è selezionata, la dimensione massima del log aumenterà da 100 MB a 1 GB - + Enable Extended Logging** Abilita il log esteso** - + Homebrew Homebrew - + Arguments String - Stringa degli Argomenti + Stringa degli argomenti - + Graphics Grafica - + When checked, the graphics API enters a slower debugging mode - Quando selezionata, l'API entra in modalità di debug lento + Quando l'opzione è selezionata, l'API grafica entra in una modalità di debug più lenta - + Enable Graphics Debugging - Abilita Debugging Grafica + Abilita il debug della grafica - + When checked, it enables Nsight Aftermath crash dumps - Se spuntato, abilita i crash dump di Nsight Aftermath + Quando l'opzione è selezionata, abilita i crash dump di Nsight Aftermath - + Enable Nsight Aftermath Abilita Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + Estrai shader del gioco - + When checked, it will dump all the macro programs of the GPU - + Quando l'opzione è selezionata, verranno estratti tutti i programmi macro della GPU - + Dump Maxwell Macros - + Estrai macro Maxwell - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - Quando selezionato, disabilita il macro-compilatore JIT. Abilitalo per rendere i giochi più lenti + Quando l'opzione è selezionata, disabilita il compilatore Just-In-Time delle macro. Abilitare questa opzione rende i giochi più lenti - + Disable Macro JIT - Disabilita Macro JIT + Disabilita JIT macro - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Debug - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + Abilita log di accesso al FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + Crea Minidump dopo un arresto anomalo + + + Advanced Avanzate - + Kiosk (Quest) Mode Modalità Kiosk (Quest) - + Enable CPU Debugging Abilita il debug della CPU - + Enable Debug Asserts Abilita le asserzioni di debug - + Enable Auto-Stub** - + Enable All Controller Types Abilita tutti i tipi di controller - + Disable Web Applet Disabilita l'applet web - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **L'opzione verrà automaticamente ripristinata alla chiusura di yuzu. + + + Restart Required + Riavvio richiesto + + + + yuzu is required to restart in order to apply this setting. + yuzu dev'essere riavviato affinché questa opzione venga applicata. + + + + Web applet not compiled + Applet web non compilato + + + + MiniDump creation not compiled + Creazione MiniDump non compilata + ConfigureDebugController Configure Debug Controller - Configura debug controller + Configura controller di debug @@ -1240,7 +1316,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Multicore CPU Emulation - Emulazione CPU multicore + Emulazione CPU multi-core @@ -1285,7 +1361,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. This reset all settings and remove all per-game configurations. This will not delete game directories, profiles, or input profiles. Proceed? - Questo resetta tutte le impostazioni e rimuove tutte le configurazioni per gioco. Questo, non cancellerà le cartelle di gioco, i profili o i profili di input. Vuoi Procedere ? + Tutte le impostazioni verranno ripristinate e tutte le configurazioni dei giochi verranno rimosse. Le cartelle di gioco, i profili e i profili di input non saranno cancellati. Vuoi procedere? @@ -1308,7 +1384,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Shader Backend: - + Backend degli shader: @@ -1321,193 +1397,218 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP.API: - + Graphics Settings Impostazioni grafiche - + Use disk pipeline cache Utilizza la cache delle pipeline su disco - + Use asynchronous GPU emulation Utilizza l'emulazione asincrona della GPU - + Accelerate ASTC texture decoding Accelera la decodifica delle texture ASTC - + NVDEC emulation: Emulazione NVDEC: - + No Video Output Nessun output video - + CPU Video Decoding Decodifica video CPU - + GPU Video Decoding (Default) Decodifica video GPU (predefinita) - + Fullscreen Mode: Modalità schermo intero: - + Borderless Windowed Finestra senza bordi - + Exclusive Fullscreen Esclusivamente a schermo intero - + Aspect Ratio: Rapporto d'aspetto: - + Default (16:9) Predefinito (16:9) - + Force 4:3 Forza 4:3 - + Force 21:9 Forza 21:9 - + + Force 16:10 + Forza 16:10 + + + Stretch to Window Allunga a finestra - + Resolution: Risoluzione: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [SPERIMENTALE] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [SPERIMENTALE] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: Filtro di adattamento alla finestra: - + Nearest Neighbor Nearest neighbor - + Bilinear Bilineare - + Bicubic Bicubico - + Gaussian Gaussiano - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (solo Vulkan) - + Anti-Aliasing Method: Metodo di anti-aliasing: - + None Nessuno - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Usa il colore di sfondo globale - + Set background color: Imposta colore dello sfondo: - + Background Color: Colore dello sfondo: @@ -1516,6 +1617,12 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP.GLASM (Assembly Shaders, NVIDIA Only) GLASM (shader assembly, solo NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1532,7 +1639,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Advanced Graphics Settings - Impostazioni Grafiche Avanzate + Impostazioni grafiche avanzate @@ -1570,37 +1677,47 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtro anisotropico: - + Automatic Automatico - + Default Predefinito - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -1673,7 +1790,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Invalid - + Non valido @@ -1770,7 +1887,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Docked - Docked + Dock @@ -1982,7 +2099,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Debug Controller - Debug Controller + Controller di debug @@ -1995,7 +2112,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Ring Controller - + Ring-Con @@ -2020,17 +2137,17 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Enable XInput 8 player support (disables web applet) - + Abilita il supporto a 8 giocatori con XInput (disabilita l'applet web) Enable UDP controllers (not needed for motion) - + Abilita controller UDP (non necessari per il movimento) Controller navigation - + Navigazione con il controller @@ -2068,7 +2185,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. Input Device - Dispositivo input + Dispositivo di input @@ -2092,7 +2209,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + Left Stick Levetta sinistra @@ -2186,14 +2303,14 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + L L - + ZL ZL @@ -2212,7 +2329,7 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + Plus Più @@ -2225,15 +2342,15 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + R R - + ZR ZR @@ -2290,231 +2407,236 @@ Questo bannerà sia il suo nome utente del forum che il suo indirizzo IP. - + Right Stick Levetta destra - - - - + + + + Clear Cancella - - - - - - [not set] - [non impostato] - - - - - Toggle button - Premi il bottone - - - - - Invert button - - - - - - Invert axis - Inverti assi - - - - + + + + + [not set] + [non impost.] + + + + + Invert button + Inverti pulsante + + + + + Toggle button + Premi il pulsante + + + + + Invert axis + Inverti asse + + + + + Set threshold Imposta soglia - - + + Choose a value between 0% and 100% Scegli un valore compreso tra 0% e 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Mappa la levetta analogica - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Dopo aver premuto OK, prima muovi la levetta orizzontalmente, e poi verticalmente. Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalmente. - + Center axis - + Centra asse - - + + Deadzone: %1% Zona morta: %1% - - + + Modifier Range: %1% Modifica raggio: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Due Joycon - + Left Joycon Joycon sinistro - + Right Joycon Joycon destro - + Handheld Portatile - + GameCube Controller Controller GameCube - + Poke Ball Plus Poké Ball Plus - + NES Controller Controller NES - + SNES Controller Controller SNES - + N64 Controller Controller N64 - + Sega Genesis Sega Genesis - + Start / Pause Avvia / Metti in pausa - + Z Z - + Control Stick Levetta di Controllo - + C-Stick Levetta C - + Shake! Scuoti! - + [waiting] [in attesa] - + New Profile Nuovo profilo - + Enter a profile name: Inserisci un nome profilo: - - + + Create Input Profile - Crea un profilo di Input + Crea un profilo di input - + The given profile name is not valid! - Il nome profilo dato non è valido! + Il nome profilo inserito non è valido! - + Failed to create the input profile "%1" Impossibile creare il profilo di input "%1" - + Delete Input Profile - Elimina un profilo di Input + Elimina un profilo di input - + Failed to delete the input profile "%1" Impossibile eliminare il profilo di input "%1" - + Load Input Profile Carica un profilo di input - + Failed to load the input profile "%1" Impossibile caricare il profilo di input "%1" - + Save Input Profile Salva un profilo di Input - + Failed to save the input profile "%1" Impossibile creare il profilo di input "%1" @@ -2524,7 +2646,7 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme Create Input Profile - Crea un profilo di Input + Crea un profilo di input @@ -2547,7 +2669,7 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme Touch - Touch + Tocco @@ -2569,7 +2691,7 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme Touch from button profile: - + Tocco dal profilo dei tasti: @@ -2718,7 +2840,7 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme None - Niente + Nessuna @@ -2769,42 +2891,42 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme Sviluppatore - + Add-Ons Add-on - + General Generale - + System Sistema - + CPU CPU - + Graphics Grafica - + Adv. Graphics - Grafiche Avanzate + Grafica - Avanzate - + Audio Audio - + Properties Proprietà @@ -2860,37 +2982,37 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme Utente in uso - + Username Nome utente - + Set Image - Imposta Immagine + Imposta immagine - + Add Aggiungi - + Rename Rinomina - + Remove Rimuovi - + Profile management is available only when game is not running. La gestione dei profili è disponibile solamente quando il gioco non è in esecuzione. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2898,102 +3020,112 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme %2 - + Enter Username Inserisci il nome utente - + Users Utenti - + Enter a username for the new user: Inserisci un nome utente per il nuovo utente: - + Enter a new username: Inserisci un nuovo nome utente: - - Confirm Delete - Conferma eliminazione - - - - You are about to delete user with name "%1". Are you sure? - Stai per cancellare l'utente chiamato "%1". Sei sicuro? - - - + Select User Image Seleziona immagine utente - + JPEG Images (*.jpg *.jpeg) Immagini JPEG (*.jpg *.jpeg) - + Error deleting image Impossibile eliminare l'immagine - + Error occurred attempting to overwrite previous image at: %1. Impossibile sovrascrivere l'immagine precedente in: %1. - + Error deleting file Impossibile eliminare il file - + Unable to delete existing file: %1. Impossibile eliminare il file già esistente: %1. - + Error creating user image directory Errore durante la creazione della cartella delle immagini dell'utente - + Unable to create directory %1 for storing user images. Impossibile creare la cartella %1 per archiviare le immagini dell'utente. - + Error copying user image Impossibile copiare l'immagine utente - + Unable to copy image from %1 to %2 Impossibile copiare l'immagine da %1 a %2 - + Error resizing user image Errore durante il ridimensionamento dell'immagine utente - + Unable to resize image Impossibile ridimensionare l'immagine + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + Eliminare questo utente? Tutti i suoi dati di salvataggio verranno rimossi. + + + + Confirm Delete + Conferma eliminazione + + + + Name: %1 +UUID: %2 + Nome: %1 +UUID: %2 + + ConfigureRingController Configure Ring Controller - + Configura Ring-Con @@ -3035,12 +3167,12 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme [not set] - [non impostato] + [non impost.] Invert axis - Inverti assi + Inverti asse @@ -3516,47 +3648,47 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme <html><head/><body><p>Gli script vengono letti seguendo lo stesso formato degli script di TAS-nx.<br/>Per saperne di più, puoi consultare <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">questa pagina</span></a> sul sito di yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + Per verificare quali scorciatoie controllano la riproduzione/registrazione, consulta le impostazioni delle scorciatoie (Configura -> Generale -> Scorciatoie). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. IMPORTANTE: questa funzione è ancora in fase sperimentale.<br/>Gli script NON verranno riprodotti perfettamente. - + Settings Impostazioni - + Enable TAS features Attiva le funzioni di TASing - + Loop script - + Pause execution during loads - + Script Directory Cartella degli script - + Path Percorso - + ... ... @@ -3566,12 +3698,12 @@ Per invertire gli assi, prima muovi la levetta verticalmente, e poi orizzontalme TAS Configuration - + Configurazione TAS Select TAS Load Directory... - + Seleziona la cartella di caricamento TAS... @@ -3633,7 +3765,7 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab New Profile - Nuovo Profilo + Nuovo profilo @@ -3643,17 +3775,17 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab Delete Profile - Elimina Profilo + Elimina profilo Delete profile %1? - Elimina profilo %1? + Eliminare il profilo %1? Rename Profile - Rinomina Profilo + Rinomina profilo @@ -3808,56 +3940,71 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab + Show Compatibility List + Mostra compatibilità + + + Show Add-Ons Column Mostra colonna Add-on - + + Show Size Column + Mostra colonna Dimensione + + + + Show File Types Column + Mostra colonna Tipo di file + + + Game Icon Size: Dimensione dell'icona del gioco: - + Folder Icon Size: Dimensione dell'icona delle cartelle: - + Row 1 Text: Testo riga 1: - + Row 2 Text: Testo riga 2: - + Screenshots Screenshot - + Ask Where To Save Screenshots (Windows Only) Chiedi dove salvare gli screenshot (solo Windows) - + Screenshots Path: Percorso degli screenshot: - + ... ... - + Select Screenshots Path... Seleziona il percorso degli screenshot... - + <System> <System> @@ -3966,7 +4113,7 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab - + Verify Verifica @@ -4028,7 +4175,7 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab Show Current Game in your Discord Status - Mostra il Gioco Attuale nel tuo Stato di Discord + Mostra il gioco in uso nel tuo stato di Discord @@ -4053,7 +4200,7 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab - + Unspecified Non specificato @@ -4068,17 +4215,36 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab Il token non è stato verificato. La modifica al token non è stata salvata. - + + Unverified, please click Verify before saving configuration + Tooltip + Non verificato, clicca su "Verifica" prima di salvare la configurazione + + + + Verifying... Verifica in corso... - + + Verified + Tooltip + Verificato + + + + Verification failed + Tooltip + Verifica fallita + + + Verification failed Verifica fallita - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verifica fallita. Controlla di aver inserito il token correttamente, e che la tua connessione a internet sia funzionante. @@ -4088,12 +4254,12 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab Controller P1 - Controller P1 + Controller G1 &Controller P1 - &Controller P1 + &Controller G1 @@ -4147,12 +4313,12 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab DirectConnectWindow - + Connecting Connessione in corso - + Connect Connetti @@ -4160,494 +4326,500 @@ Trascina i punti per cambiare posizione, oppure clicca due volte la cella in tab GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Vengono raccolti dati anonimi</a> per aiutarci a migliorare yuzu. <br/><br/>Desideri condividere i tuoi dati di utilizzo con noi? - + Telemetry Telemetria - + Broken Vulkan Installation Detected - + Rilevata installazione di Vulkan non funzionante - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + L'inizializzazione di Vulkan è fallita durante l'avvio.<br><br>Clicca <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>qui per istruzioni su come risolvere il problema</a>. - + Loading Web Applet... Caricamento dell'applet web... - - + + Disable Web Applet Disabilita l'applet web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Il numero di shaders al momento in costruzione - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocità corrente dell'emulazione. Valori più alti o più bassi di 100% indicano che l'emulazione sta funzionando più velocemente o lentamente rispetto a una Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - Quanti frame al secondo il gioco mostra attualmente. Questo varia da gioco a gioco e da situazione a situazione. + Il numero di fotogrammi al secondo che il gioco visualizza attualmente. Può variare in base al gioco e alla situazione. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tempo utilizzato per emulare un frame della Switch, non contando i limiti ai frame o il v-sync. Per un'emulazione alla massima velocità, il valore dev'essere al massimo 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Cancella i file recenti - + &Continue &Continua - + &Pause &Pausa - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format - Avviso Formato di Gioco Obsoleto + Formato del gioco obsoleto - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Stai usando una cartella con dentro una ROM decostruita come formato per avviare questo gioco, è un formato obsoleto ed è stato sostituito da altri come NCA, NAX, XCI o NSP. Le ROM decostruite non hanno icone, metadata e non supportano gli aggiornamenti. <br><br>Per una spiegazione sui vari formati di Switch che yuzu supporta, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>controlla la nostra wiki</a>. Questo messaggio non verrà più mostrato. - - + + Error while loading ROM! Errore nel caricamento della ROM! - + The ROM format is not supported. Il formato della ROM non è supportato. - + An error occurred initializing the video core. - E' stato riscontrato un errore nell'inizializzazione del core video. + È stato riscontrato un errore nell'inizializzazione del core video. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Errore nel caricamento della ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - %1<br>Per favore segui <a href='https://yuzu-emu.org/help/quickstart/'>la guida rapida di yuzu</a> per rifare il dump dei file.<br>Puoi fare riferimento alla wiki di yuzu</a> o al canale Discord di yuzu</a> per aiuto. + %1<br>Segui <a href='https://yuzu-emu.org/help/quickstart/'>la guida introduttiva di yuzu</a> per rifare il dump dei file.<br>Puoi fare riferimento alla wiki di yuzu</a> o al server Discord di yuzu</a> per assistenza. - + An unknown error occurred. Please see the log for more details. Si è verificato un errore sconosciuto. Visualizza il log per maggiori dettagli. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Dati di salvataggio - + Mod Data Dati delle mod - + Error Opening %1 Folder Errore nell'apertura della cartella %1 - - + + Folder does not exist! La cartella non esiste! - + Error Opening Transferable Shader Cache Errore nell'apertura della cache trasferibile degli shader - + Failed to create the shader cache directory for this title. Impossibile creare la cartella della cache degli shader per questo titolo. - - Contents - Contenuti + + Error Removing Contents + Errore nella rimozione del contentuto - - Update - Aggiornamento + + Error Removing Update + Errore nella rimozione dell'aggiornamento - - DLC - DLC + + Error Removing DLC + Errore nella rimozione del DLC - + + Remove Installed Game Contents? + Rimuovere il contenuto del gioco installato? + + + + Remove Installed Game Update? + Rimuovere l'aggiornamento installato? + + + + Remove Installed Game DLC? + Rimuovere il DLC installato? + + + Remove Entry Rimuovi voce - - Remove Installed Game %1? - Vuoi rimuovere i %1 installati del gioco? - - - - - - - - + + + + + + Successfully Removed - Rimosso con successo + Rimozione completata - + Successfully removed the installed base game. - Rimosso con successo il gioco base installato + Il gioco base installato è stato rimosso con successo. - - - - Error Removing %1 - Errore durante la rimozione %1 - - - + The base game is not installed in the NAND and cannot be removed. Il gioco base non è installato su NAND e non può essere rimosso. - + Successfully removed the installed update. - Aggiornamento rimosso on successo. + Aggiornamento rimosso con successo. - + There is no update installed for this title. Non c'è alcun aggiornamento installato per questo gioco. - + There are no DLC installed for this title. Non c'è alcun DLC installato per questo gioco. - + Successfully removed %1 installed DLC. - Rimossi con successo %1 DLC installati. + %1 DLC rimossi con successo. - + Delete OpenGL Transferable Shader Cache? Vuoi rimuovere la cache trasferibile degli shader OpenGL? - + Delete Vulkan Transferable Shader Cache? Vuoi rimuovere la cache trasferibile degli shader Vulkan? - + Delete All Transferable Shader Caches? Vuoi rimuovere tutte le cache trasferibili degli shader? - + Remove Custom Game Configuration? - Vuoi rimuovere la configurazione personalizzata del gioco? + Rimuovere la configurazione personalizzata del gioco? - + Remove File Rimuovi file - - + + Error Removing Transferable Shader Cache Errore nella rimozione della cache trasferibile degli shader - - + + A shader cache for this title does not exist. Per questo titolo non esiste una cache degli shader. - + Successfully removed the transferable shader cache. La cache trasferibile degli shader è stata rimossa con successo. - + Failed to remove the transferable shader cache. Impossibile rimuovere la cache trasferibile degli shader. - - + + Error Removing Transferable Shader Caches Errore nella rimozione delle cache trasferibili degli shader - + Successfully removed the transferable shader caches. Le cache trasferibili degli shader sono state rimosse con successo. - + Failed to remove the transferable shader cache directory. Impossibile rimuovere la cartella della cache trasferibile degli shader. - - + + Error Removing Custom Configuration Errore nella rimozione della configurazione personalizzata - + A custom configuration for this title does not exist. Non esiste una configurazione personalizzata per questo gioco. - + Successfully removed the custom game configuration. La configurazione personalizzata del gioco è stata rimossa con successo. - + Failed to remove the custom game configuration. Impossibile rimuovere la configurazione personalizzata del gioco. - - + + RomFS Extraction Failed! Estrazione RomFS fallita! - + There was an error copying the RomFS files or the user cancelled the operation. C'è stato un errore nella copia dei file del RomFS o l'operazione è stata annullata dall'utente. - + Full Completa - + Skeleton Cartelle - + Select RomFS Dump Mode Seleziona la modalità di estrazione della RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Seleziona come vorresti estrarre la RomFS. <br>La modalità Completa copierà tutti i file in una nuova cartella mentre<br>la modalità Cartelle creerà solamente le cartelle e le sottocartelle. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Estrazione RomFS in corso... - - + + Cancel Annulla - + RomFS Extraction Succeeded! Estrazione RomFS riuscita! - + The operation completed successfully. L'operazione è stata completata con successo. - + Error Opening %1 Errore nell'apertura di %1 - + Select Directory Seleziona cartella - + Properties Proprietà - + The game properties could not be loaded. Le proprietà del gioco non sono potute essere caricate. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Eseguibile Switch (%1);;Tutti i File (*.*) - + Load File Carica file - + Open Extracted ROM Directory Apri Cartella ROM Estratta - + Invalid Directory Selected Cartella selezionata non valida - + The directory you have selected does not contain a 'main' file. La cartella che hai selezionato non contiene un file "main". - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) File installabili Switch (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Installa file - + %n file(s) remaining %n file rimanente%n file rimanenti%n file rimanenti - + Installing file "%1"... Installazione del file "%1"... - - + + Install Results Risultati dell'installazione - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - Per evitare possibli conflitti, scoraggiamo gli utenti dall'installare giochi base su NAND. -Per favore, usare questa funzione solo per installare aggiornamenti e DLC. + Per evitare possibli conflitti, sconsigliamo di installare i giochi base su NAND. +Usa questa funzione solo per installare aggiornamenti e DLC. - + %n file(s) were newly installed - + %n nuovo file è stato installato +%n nuovi file sono stati installati +%n nuovi file sono stati installati + - + %n file(s) were overwritten %n file è stato sovrascritto @@ -4656,418 +4828,420 @@ Per favore, usare questa funzione solo per installare aggiornamenti e DLC. - + %n file(s) failed to install - + %n file non è stato installato a causa di errori +%n file non sono stati installati a causa di errori +%n file non sono stati installati a causa di errori + - + System Application Applicazione di sistema - + System Archive Archivio di sistema - + System Application Update Aggiornamento di un'applicazione di sistema - + Firmware Package (Type A) Pacchetto Firmware (Tipo A) - + Firmware Package (Type B) Pacchetto Firmware (Tipo B) - + Game Gioco - + Game Update Aggiornamento di gioco - + Game DLC DLC - + Delta Title Titolo Delta - + Select NCA Install Type... - Seleziona il Tipo di Installazione NCA + Seleziona il tipo di installazione NCA - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Seleziona il tipo del file NCA da installare: (Nella maggior parte dei casi, il predefinito 'Gioco' va bene.) - + Failed to Install Installazione fallita - + The title type you selected for the NCA is invalid. Il tipo che hai selezionato per l'NCA non è valido. - + File not found File non trovato - + File "%1" not found File "%1" non trovato - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Account di yuzu non trovato - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Per segnalare la compatibilità di un gioco, devi collegare il tuo account yuzu. <br><br/>Per collegare il tuo account yuzu, vai su Emulazione &gt; Configurazione &gt; Web. - + Error opening URL Errore aprendo l'URL - + Unable to open the URL "%1". Impossibile aprire l'URL "% 1". - + TAS Recording - + Overwrite file of player 1? Vuoi sovrascrivere il file del giocatore 1? - + Invalid config detected Trovata configurazione invalida - + Handheld controller can't be used on docked mode. Pro controller will be selected. - Il controller portatile non può essere utilizzato in modalità docked. Verrà selezionato il controller Pro. + Il controller portatile non può essere utilizzato in modalità dock. Verrà selezionato il controller Pro. - - - Error - Errore - - - - - The current game is not looking for amiibos - - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed L'Amiibo corrente è stato rimosso - - Amiibo File (%1);; All Files (*.*) - File Amiibo (%1);; Tutti I File (*.*) + + Error + Errore - + + + The current game is not looking for amiibos + + + + + Amiibo File (%1);; All Files (*.*) + File Amiibo (%1);; Tutti i file (*.*) + + + Load Amiibo Carica Amiibo - - Error opening Amiibo data file - Errore nell'apertura del file dati Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Impossibile aprire e leggere il file Amiibo "%1". - - - - Error reading Amiibo data file - Errore nella lettura dei dati del file Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Impossibile leggere tutti i dati dell'Amiibo. E' stato possibile leggere solamente %2 byte di %1. - - - + Error loading Amiibo data - Errore nel caricamento dei dati dell'Amiibo. + Errore nel caricamento dei dati dell'Amiibo - - Unable to load Amiibo data. - Impossibile caricare i dati dell'Amiibo. + + The selected file is not a valid amiibo + Il file selezionato non è un Amiibo valido - + + The selected file is already on use + Il file selezionato è già in uso + + + + An unknown error occurred + Si è verificato un errore sconosciuto + + + Capture Screenshot - Cattura Screenshot + Cattura screenshot - + PNG Image (*.png) Immagine PNG (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + &Interrompi - + &Start &Avvia - + Stop R&ecording Interrompi r&egistrazione - + R&ecord R&egistra - + Building: %n shader(s) - + Compilazione di %n shaderCompilazione di %n shaderCompilazione di %n shader - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Velocità: %1% / %2% - + Speed: %1% Velocità: %1% - + Game: %1 FPS (Unlocked) Gioco: %1 FPS (Sbloccati) - + Game: %1 FPS Gioco: %1 FPS - + Frame: %1 ms Frame: %1 ms - + GPU NORMAL GPU NORMALE - + GPU HIGH GPU ALTA - + GPU EXTREME GPU ESTREMA - + GPU ERROR ERRORE GPU - + DOCKED - + DOCK - + HANDHELD PORTATILE - + NEAREST NEAREST - - + + BILINEAR BILINEARE - + BICUBIC BICUBICO - + GAUSSIAN GAUSSIANO - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA NO AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Il gioco che stai provando a caricare richiede ulteriori file che devono essere estratti dalla tua Switch prima di poter giocare. <br/><br/>Per maggiori informazioni sull'estrazione di questi file, visualizza la seguente pagina della wiki: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Estrazione degli archivi di sistema e dei font condivisi da una console Switch</a>.<br/><br/>Vuoi uscire e tornare alla lista dei giochi? Proseguendo l'emulazione si potrebbero verificare arresti anomali, salvataggi corrotti o altri bug. - + yuzu was unable to locate a Switch system archive. %1 yuzu non ha potuto individuare un archivio di sistema della Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu non ha potuto individuare un archivio di sistema della Switch: %1. %2 - + System Archive Not Found Archivio di sistema non trovato - + System Archive Missing Archivio di sistema mancante - + yuzu was unable to locate the Switch shared fonts. %1 yuzu non ha potuto individuare i font condivisi della Switch. %1 - + Shared Fonts Not Found Font condivisi non trovati - + Shared Font Missing Font condivisi mancanti - + Fatal Error Errore Fatale - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu ha riscontrato un errore fatale, visualizza il log per maggiori dettagli. Per maggiori informazioni su come accedere al log, visualizza la seguente pagina: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Come caricare il file di log</a>.<br/><br/>Vuoi uscire e tornare alla lista dei giochi? Proseguendo l'emulazione si potrebbero verificare arresti anomali, salvataggi corrotti o altri bug. - + Fatal Error encountered Errore Fatale riscontrato - + Confirm Key Rederivation - Conferma Riderivazione Chiave + Conferma ri-derivazione chiavi - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5084,37 +5258,37 @@ e facoltativamente fai dei backup. Questo eliminerà i tuoi file di chiavi autogenerati e ri-avvierà il processo di derivazione delle chiavi. - + Missing fuses Fusi mancanti - + - Missing BOOT0 - BOOT0 mancante - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main mancante - + - Missing PRODINFO - PRODINFO mancante - + Derivation Components Missing Componenti di derivazione mancanti - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Chiavi di crittografia mancanti. <br>Segui <a href='https://yuzu-emu.org/help/quickstart/'>la guida introduttiva di yuzu</a> per ottenere tutte le tue chiavi, il tuo firmware e i tuoi giochi.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5123,39 +5297,39 @@ Questa operazione potrebbe durare fino a un minuto in base alle prestazioni del tuo sistema. - + Deriving Keys Derivazione chiavi - + Select RomFS Dump Target Seleziona Target dell'Estrazione del RomFS - + Please select which RomFS you would like to dump. Seleziona quale RomFS vorresti estrarre. - + Are you sure you want to close yuzu? Sei sicuro di voler chiudere yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Sei sicuro di voler arrestare l'emulazione? Tutti i progressi non salvati verranno perduti. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5167,38 +5341,38 @@ Desideri uscire comunque? GRenderWindow - + OpenGL not available! OpenGL non disponibile! - + yuzu has not been compiled with OpenGL support. yuzu non è stato compilato con il supporto OpenGL. - - + + Error while initializing OpenGL! Errore durante l'inizializzazione di OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. La tua GPU potrebbe non supportare OpenGL, o non hai installato l'ultima versione dei driver video. - + Error while initializing OpenGL 4.6! Errore durante l'inizializzazione di OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 La tua GPU potrebbe non supportare OpenGL 4.6, o non hai installato l'ultima versione dei driver video.<br><br>Renderer GL:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 La tua GPU potrebbe non supportare una o più estensioni OpenGL richieste. Assicurati di aver installato i driver video più recenti.<br><br>Renderer GL:<br>%1<br><br>Estensioni non supportate:<br>%2 @@ -5206,153 +5380,153 @@ Desideri uscire comunque? GameList - + Favorite Preferito - + Start Game Avvia gioco - + Start Game without Custom Configuration Avvia gioco senza la configurazione personalizzata - + Open Save Data Location Apri la cartella dei dati di salvataggio - + Open Mod Data Location Apri la cartella delle mod - + Open Transferable Pipeline Cache Apri la cartella della cache trasferibile delle pipeline - + Remove Rimuovi - + Remove Installed Update Rimuovi l'aggiornamento installato - + Remove All Installed DLC Rimuovi tutti i DLC installati - + Remove Custom Configuration Rimuovi la configurazione personalizzata - + Remove OpenGL Pipeline Cache Rimuovi la cache delle pipeline OpenGL - + Remove Vulkan Pipeline Cache Rimuovi la cache delle pipeline Vulkan - + Remove All Pipeline Caches Rimuovi tutte le cache delle pipeline - + Remove All Installed Contents Rimuovi tutti i contenuti installati - + Dump RomFS Estrai RomFS - + Dump RomFS to SDMC - + Estrai RomFS su SDMC - + Copy Title ID to Clipboard Copia il Title ID negli Appunti - + Navigate to GameDB entry Vai alla pagina di GameDB - + Properties Proprietà - + Scan Subfolders Scansiona le sottocartelle - + Remove Game Directory Rimuovi cartella dei giochi - + ▲ Move Up ▲ Sposta in alto - + ▼ Move Down ▼ Sposta in basso - + Open Directory Location Apri cartella - + Clear Cancella - + Name Nome - + Compatibility Compatibilità - + Add-ons Add-on - + File type Tipo di file - + Size Dimensione @@ -5361,81 +5535,61 @@ Desideri uscire comunque? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Perfetto - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - Il gioco funziona perfettamente senza alcun glitch audio o video, tutte le funzionalità testate -funzionano come dovrebbero senza la necessità di utilizzare alcun espediente. - - - - Great - Ottimo - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - Il gioco presenta alcuni glitch audio o video minori ed è possibile giocare dall'inizio alla fine. -Potrebbe richiedere l'utilizzo di alcuni espedienti. - - Okay - Ok - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - Il gioco presenta considerevoli glitch audio o video, ma è possibile giocare -dall'inizio alla fine utilizzando degli espedienti. + Game can be played without issues. + Il gioco funziona senza problemi. - Bad - Scadente + Playable + Giocabile - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Il gioco presenta considerevoli glitch audio o video. È impossibile progredire in alcune aree -a causa della presenza di glitch anche utilizzando degli espedienti. + Game functions with minor graphical or audio glitches and is playable from start to finish. + Il gioco presenta alcuni glitch audio o video minori ed è possibile giocare dall'inizio alla fine. - + Intro/Menu Intro/Menù - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Il gioco è completamente ingiocabile a causa di errori gravi audio o video. È impossibile proseguire oltre la Schermata -Iniziale. + + Game loads, but is unable to progress past the Start Screen. + Il gioco si avvia, ma è impossibile proseguire oltre la schermata iniziale. - + Won't Boot Non si avvia - + The game crashes when attempting to startup. Il gioco si blocca quando viene avviato. - + Not Tested Non testato - + The game has not yet been tested. Il gioco non è ancora stato testato. @@ -5443,7 +5597,7 @@ Iniziale. GameListPlaceholder - + Double-click to add a new folder to the game list Clicca due volte per aggiungere una nuova cartella alla lista dei giochi @@ -5453,15 +5607,15 @@ Iniziale. %1 of %n result(s) - + %1 di %n risultato%1 di %n risultati%1 di %n risultati - + Filter: Filtro: - + Enter pattern to filter Inserisci pattern per filtrare @@ -5537,12 +5691,12 @@ Iniziale. HostRoomWindow - + Error Errore - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Impossibile annunciare la stanza alla lobby pubblica. Per ospitare una stanza pubblicamente, devi avere un account yuzu valido configurato in Emulazione -> Configura -> Web. Se non desideri pubblicare una stanza nella lobby pubblica, seleziona Non in lista. @@ -5552,11 +5706,12 @@ Messaggio di debug: Hotkeys - + Audio Mute/Unmute - + Attiva/disattiva l'audio + @@ -5578,114 +5733,113 @@ Messaggio di debug: - Main Window Finestra principale - + Audio Volume Down - + Abbassa il volume dell'audio + + + + Audio Volume Up + Alza il volume dell'audio - Audio Volume Up - + Capture Screenshot + Cattura screenshot - Capture Screenshot - Cattura Screenshot + Change Adapting Filter + Cambia filtro di adattamento - Change Adapting Filter - + Change Docked Mode + Cambia modalità console - Change Docked Mode - + Change GPU Accuracy + Cambia accuratezza GPU - Change GPU Accuracy - + Continue/Pause Emulation + Continua/Metti in pausa l'emulazione - Continue/Pause Emulation - + Exit Fullscreen + Esci dalla modalità schermo intero - Exit Fullscreen - + Exit yuzu + Esci da yuzu - Exit yuzu - + Fullscreen + Schermo intero - Fullscreen - Schermo Intero - - - Load File Carica file - + Load/Remove Amiibo Carica/Rimuovi Amiibo - + Restart Emulation - + Riavvia l'emulazione - + Stop Emulation Arresta l'emulazione - + TAS Record - + Registra TAS + + + + TAS Reset + Reimposta TAS - TAS Reset - + TAS Start/Stop + Avvia/Interrompi TAS - TAS Start/Stop - + Toggle Filter Bar + Mostra/nascondi la barra del filtro - Toggle Filter Bar - + Toggle Framerate Limit + Attiva/disattiva il limite del framerate - Toggle Framerate Limit - - - - Toggle Mouse Panning - + Toggle Status Bar - + Mostra/nascondi la barra di stato @@ -5798,42 +5952,42 @@ Messaggio di debug: Aggiorna lobby - + Password Required to Join Password richiesta per entrare - + Password: Password: - - Room Name - Nome stanza - - - - Preferred Game - Gioco preferito - - - - Host - Host - - - + Players Giocatori - + + Room Name + Nome stanza + + + + Preferred Game + Gioco preferito + + + + Host + Host + + + Refreshing Aggiornamento in corso - + Refresh List Aggiorna lista @@ -5856,232 +6010,237 @@ Messaggio di debug: File &recenti - + &Emulation &Emulazione - + &View &Visualizza - + &Reset Window Size &Ripristina dimensioni della finestra - + &Debugging - Debugging + &Debug - + Reset Window Size to &720p Ripristina le dimensioni della finestra a &720p - + Reset Window Size to 720p Ripristina le dimensioni della finestra a 720p - + Reset Window Size to &900p Ripristina le dimensioni della finestra a &900p - + Reset Window Size to 900p Ripristina le dimensioni della finestra a 900p - + Reset Window Size to &1080p Ripristina le dimensioni della finestra a &1080p - + Reset Window Size to 1080p Ripristina le dimensioni della finestra a 1080p - + + &Multiplayer + &Multigiocatore + + + &Tools &Strumenti - + &TAS &TAS - + &Help &Aiuto - + &Install Files to NAND... &Installa file su NAND... - + L&oad File... Carica &file... - + Load &Folder... Carica &cartella... - + E&xit &Esci - + &Pause &Pausa - + &Stop Arre&sta - + &Reinitialize keys... &Reinizializza chiavi... - + &About yuzu &Informazioni su yuzu - + Single &Window Mode &Modalità finestra singola - + Con&figure... Configura... - + Display D&ock Widget Headers Visualizza le intestazioni del dock dei widget - + Show &Filter Bar Mostra barra del &filtro - + Show &Status Bar Mostra barra di &stato - + Show Status Bar Mostra barra di stato - - - Browse Public Game Lobby - Sfoglia lobby di gioco pubblica - - - - Create Room - Crea stanza - - Leave Room - Esci dalla stanza + &Browse Public Game Lobby + &Sfoglia lobby di gioco pubblica - - Direct Connect to Room - Collegamento diretto alla stanza + + &Create Room + &Crea stanza - - Show Current Room - Mostra stanza attuale + + &Leave Room + &Esci dalla stanza + &Direct Connect to Room + Collegamento &diretto alla stanza + + + + &Show Current Room + &Mostra stanza attuale + + + F&ullscreen Schermo intero - + &Restart &Riavvia - + Load/Remove &Amiibo... Carica/Rimuovi &Amiibo... - + &Report Compatibility &Segnala la compatibilità - + Open &Mods Page - Apri la pagina delle mod + Apri la pagina delle &mod - + Open &Quickstart Guide - Apri la guida rapida + Apri la &guida introduttiva - + &FAQ &Domande frequenti - + Open &yuzu Folder Apri la cartella di yuzu - + &Capture Screenshot Cattura schermo - + &Configure TAS... - + &Configura TAS... - + Configure C&urrent Game... Configura il gioco in uso... - + &Start &Avvia - + &Reset - + &Reimposta - + R&ecord R&egistra @@ -6146,47 +6305,42 @@ Messaggio di debug: MultiplayerState - - + Current connection status Stato connessione attuale - - + Not Connected. Click here to find a room! Non connesso. Clicca qui per trovare una stanza! - - - Connected - Connesso - - - - Not Connected Non connesso - + + Connected + Connesso + + + + New Messages Received + Nuovi messaggi ricevuti + + + Error Errore - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: Impossibile aggiornare le informazioni della stanza. Controlla la tua connessione a internet e prova a ospitare la stanza di nuovo. Messaggio di debug: - - - New Messages Received - Nuovi messaggi ricevuti - NetworkMessage @@ -6288,22 +6442,40 @@ They may have left the room. Potrebbe aver abbandonato la stanza. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + Nessuna interfaccia di rete valida selezionata. +Vai su Configura -> Sistema -> Rete e selezionane una. + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room Esci dalla stanza - + You are about to close the room. Any network connections will be closed. Stai per chiudere la stanza. Ogni connessione di rete verrà chiusa. - + Disconnect Disconnetti - + You are about to leave the room. Any network connections will be closed. Stai per uscire dalla stanza. Ogni connessione di rete verrà chiusa. @@ -6311,7 +6483,7 @@ Potrebbe aver abbandonato la stanza. NetworkMessage::ErrorManager - + Error Errore @@ -6360,42 +6532,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game %1 non sta giocando a un gioco - + %1 is playing %2 %1 sta giocando a %2 - + Not playing a game Non in gioco - + Installed SD Titles Titoli SD installati - + Installed NAND Titles Titoli NAND installati - + System Titles Titoli di sistema - + Add New Game Directory Aggiungi nuova cartella dei giochi - + Favorites Preferiti @@ -6425,9 +6597,9 @@ p, li { white-space: pre-wrap; } - + [not set] - [non impostato] + [non impost.] @@ -6440,10 +6612,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Asse %1%2 @@ -6457,9 +6629,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [sconosciuto] @@ -6624,15 +6796,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - + [non valido] - - + + %1%2Hat %3 @@ -6640,35 +6812,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Asse %3 - + %1%2Axis %3,%4,%5 - + %1%2Asse %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + %1%2Pulsante %3 - + [unused] [inutilizzato] @@ -6691,12 +6863,12 @@ p, li { white-space: pre-wrap; } Backward - + Indietro Forward - + Avanti @@ -6709,11 +6881,124 @@ p, li { white-space: pre-wrap; } - + %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + Tipo + + + + Name + Nome + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6739,7 +7024,7 @@ p, li { white-space: pre-wrap; } P4 - P4 + Giocatore 4 @@ -6765,7 +7050,7 @@ p, li { white-space: pre-wrap; } Dual Joycons - Doppi Joycon + Due Joycon @@ -6817,6 +7102,7 @@ p, li { white-space: pre-wrap; } + Handheld Portatile @@ -6854,12 +7140,7 @@ p, li { white-space: pre-wrap; } Docked - Docked - - - - Undocked - Undocked + Dock @@ -6890,7 +7171,7 @@ p, li { white-space: pre-wrap; } Controllers - Controllers + Controller @@ -6975,21 +7256,21 @@ p, li { white-space: pre-wrap; } Error Code: %1-%2 (0x%3) - Codice Errore: %1-%2 (0x%3) + Codice di errore: %1-%2 (0x%3) An error has occurred. Please try again or contact the developer of the software. - E' stato riscontrato un errore. -Per favore riprova o contatta gli sviluppatori del programma. + Si è verificato un errore. +Riprova o contatta gli sviluppatori del programma. An error occurred on %1 at %2. Please try again or contact the developer of the software. - E' stato riscontrato un errore su %1 a %2. -Per favore riprova o contatti gli sviluppatori. + Si è verificato un errore su %1 a %2. +Riprova o contatta gli sviluppatori del programma. @@ -6998,7 +7279,7 @@ Per favore riprova o contatti gli sviluppatori. %1 %2 - E' stato riscontrato un errore. + Si è verificato un errore. %1 @@ -7028,7 +7309,7 @@ Per favore riprova o contatti gli sviluppatori. Profile Selector - Selettore Profili + Selettore profili @@ -7036,7 +7317,7 @@ Per favore riprova o contatti gli sviluppatori. Software Keyboard - Tastiera Software + Tastiera software diff --git a/dist/languages/ja_JP.ts b/dist/languages/ja_JP.ts index 3c66d5d..1fe1ec9 100644 --- a/dist/languages/ja_JP.ts +++ b/dist/languages/ja_JP.ts @@ -82,7 +82,7 @@ p, li { white-space: pre-wrap; } Room Window - + ルームウインドウ @@ -95,78 +95,78 @@ p, li { white-space: pre-wrap; } メッセージを送る - + Members メンバー - + %1 has joined %1 が参加しました - + %1 has left %1 が退出しました - + %1 has been kicked %1 はキックされました - + %1 has been banned %1 はBanされました - + %1 has been unbanned %1 はBan解除されました - + View Profile プロフィールを見る - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? ブロックすると、そのプレイヤーからのチャットメッセージが届かなくなります。<br><br>%1を本当にブロックしますか? - + Kick キック - + Ban Ban - + Kick Player プレイヤーをキック - + Are you sure you would like to <b>kick</b> %1? %1 を<b>キック</b>しますがよろしいですか? - + Ban Player プレイヤーをBan - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -180,7 +180,7 @@ This would ban both their forum username and their IP address. Room Window - + ルームウインドウ @@ -212,7 +212,7 @@ This would ban both their forum username and their IP address. - %1 (%2/%3 members) - connected + %1 - %2 (%3/%4 members) - connected @@ -226,6 +226,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility ゲームの互換性を報告 @@ -235,92 +240,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">テストケースを</span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">yuzu互換性リスト</span></a><span style=" font-size:10pt;">に送信した場合、以下の情報が収集され、サイトに表示されます:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">ハードウェア情報(CPU/GPU/OS)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">実行中のyuzuバージョン</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">接続中のyuzuアカウント</li></ul></body></html> - - Perfect - 完璧 + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>サウンドやグラフィックの不具合なしに完全動作します。</p></body></html> + + Yes The game starts to output video or audio + - - Great - + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>グラフィックまたはサウンドに軽微な不具合がありますが、ゲームを最初から最後までプレイ可能です。いくつかの回避策が必要な場合があります。</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body>グラフィックまたはサウンドに重大な不具合がありますが、回避策を使うことで最初から最後までプレイ可能です。</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - 悪い + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>グラフィックまたはサウンドに重大な不具合があり、回避策を使用しても特定の場所から進めなくなります。</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - イントロ/メニュー + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>グラフィックまたはサウンドの重大な不具合のため、スタート画面から先に進むことが出来ません。</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - 起動不可 + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>起動時にクラッシュが発生します。</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>速度やパフォーマンスの問題を除き、このゲームは最初から最後までの間どの程度うまく実行できますか?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! ご協力ありがとうございます! - + Submitting 送信中 - + Communication error 通信エラー - + An error occurred while sending the Testcase テストケースの送信中にエラーが発生しました - + Next 次へ @@ -380,7 +420,7 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + 赤外線カメラを設定 @@ -418,7 +458,7 @@ This would ban both their forum username and their IP address. デフォルトに戻す - + Auto 自動 @@ -771,200 +811,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger デバッガ― - + Enable GDB Stub GDBスタブの有効化 - + Port: ポート: - + Logging ログ - + Global Log Filter グローバルログフィルター - + Show Log in Console コンソールにログを表示 - + Open Log Location ログ出力フォルダを開く - + When checked, the max size of the log increases from 100 MB to 1 GB チェックすると、ログの最大サイズが100MBから1GBに増加します。 - + Enable Extended Logging** 拡張ログの有効化** - + Homebrew Homebrew - + Arguments String 引数 - + Graphics グラフィック - + When checked, the graphics API enters a slower debugging mode チェックすると、 グラフィックAPIはより遅いデバッグモードになります。 - + Enable Graphics Debugging グラフィックデバッグの有効化 - + When checked, it enables Nsight Aftermath crash dumps チェックすると、Nsight Aftermathのクラッシュダンプが有効になります - + Enable Nsight Aftermath Nsight Aftermathの有効化 - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found チェックすると、ディスクシェーダーキャッシュまたはゲームからオリジナルのアセンブラーシェーダーをすべてダンプします - + Dump Game Shaders ゲームシェーダーをダンプ - + When checked, it will dump all the macro programs of the GPU チェックすると、GPUのすべてのマクロプログラムをダンプします - + Dump Maxwell Macros Maxwellマクロをダンプ - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower チェックすると、マクロのJust In Timeコンパイラが無効になります。有効にすると、ゲームの動作が遅くなります。 - + Disable Macro JIT Macro JITを無効化 - + When checked, yuzu will log statistics about the compiled pipeline cache チェックすると、コンパイルしたパイプラインキャッシュの統計情報をロギングします - + Enable Shader Feedback シェーダフィードバックの有効j化 - + When checked, it executes shaders without loop logic changes チェックすると、ループロジックを変更せずにシェーダーを実行します。 - + Disable Loop safety checks ループ安全性チェックの無効化 - + Debugging デバッグ - - Enable FS Access Log - FSアクセスログの有効化 - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - これを有効にすると、最新のオーディオコマンドリストがコンソールに出力されます。オーディオレンダラーを使用するゲームにのみ影響します。 - - - + Enable Verbose Reporting Services** 詳細なレポートサービスの有効化** - + + Enable FS Access Log + FSアクセスログの有効化 + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + これを有効にすると、最新のオーディオコマンドリストがコンソールに出力されます。オーディオレンダラーを使用するゲームにのみ影響します。 + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + クラッシュ時にミニダンプを生成 + + + Advanced 高度 - + Kiosk (Quest) Mode Kiosk (Quest) Mode - + Enable CPU Debugging CPUデバッグの有効化 - + Enable Debug Asserts デバッグアサートの有効化 - + Enable Auto-Stub** 自動スタブの有効化** - + Enable All Controller Types すべてのコントローラタイプを有効にする - + Disable Web Applet Webアプレットの無効化 - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. ** yuzuを終了したときに自動的にリセットされます。 + + + Restart Required + 再起動が必要 + + + + yuzu is required to restart in order to apply this setting. + この設定を適用するには yuzu を再起動する必要があります. + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1334,193 +1409,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings グラフィック設定 - + Use disk pipeline cache ディスクパイプラインキャッシュを使用 - + Use asynchronous GPU emulation 非同期GPUエミュレーションを使用する - + Accelerate ASTC texture decoding ASTCテクスチャデコーディングの高速化 - + NVDEC emulation: NVDEC エミュレーション: - + No Video Output ビデオ出力しない - + CPU Video Decoding ビデオをCPUでデコード - + GPU Video Decoding (Default) ビデオをGPUでデコード (デフォルト) - + Fullscreen Mode: 全画面モード: - + Borderless Windowed ボーダーレスウィンドウ - + Exclusive Fullscreen 排他的フルスクリーン - + Aspect Ratio: アスペクト比: - + Default (16:9) デフォルト (16:9) - + Force 4:3 強制的に 4:3 にする - + Force 21:9 強制的に 21:9 にする - + + Force 16:10 + + + + Stretch to Window ウィンドウに合わせる - + Resolution: 解像度: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [実験的] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [実験的] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: ウィンドウ アダプティング フィルター: - + Nearest Neighbor Nearest Neighbor - + Bilinear Bilinear - + Bicubic Bicubic - + Gaussian Gaussian - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (Vulkan のみ) - + Anti-Aliasing Method: アンチエイリアス方式: - + None なし - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color 共通設定を使用 - + Set background color: 背景色の設定: - + Background Color: 背景色: @@ -1529,6 +1629,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM (アセンブリシェーダ、NVIDIA のみ) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1583,37 +1689,47 @@ This would ban both their forum username and their IP address. 高速なGPUタイミングを有効化(ハック) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + 悲観的なバッファフラッシュを有効にします. このオプションは, 変更されていないバッファを強制的にフラッシュさせるので, パフォーマンスが低下する可能性があります. + + + + Use pessimistic buffer flushes (Hack) + 悲観的なバッファフラッシュを使用 (ハック) + + + Anisotropic Filtering: 異方性フィルタリング: - + Automatic 自動 - + Default デフォルト - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2013,7 +2129,7 @@ This would ban both their forum username and their IP address. Infrared Camera - + 赤外線カメラ @@ -2105,7 +2221,7 @@ This would ban both their forum username and their IP address. - + Left Stick Lスティック @@ -2199,14 +2315,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2225,7 +2341,7 @@ This would ban both their forum username and their IP address. - + Plus + @@ -2238,15 +2354,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2303,231 +2419,236 @@ This would ban both their forum username and their IP address. - + Right Stick Rスティック - - - - + + + + Clear クリア - - - - - + + + + + [not set] [未設定] - - - Toggle button - - - - - + + Invert button ボタンを反転 - - + + + Toggle button + + + + + Invert axis 軸を反転 - - - + + + Set threshold しきい値を設定 - - + + Choose a value between 0% and 100% 0%から100%の間の値を選択してください - + + Toggle axis + + + + Set gyro threshold ジャイロのしきい値を設定 - + Map Analog Stick アナログスティックをマップ - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. OKを押した後、スティックを水平方向に動かし、次に垂直方向に動かしてください。 軸を反転させる場合、 最初に垂直方向に動かし、次に水平方向に動かしてください。 - + Center axis - - + + Deadzone: %1% デッドゾーン:%1% - - + + Modifier Range: %1% 変更範囲:%1% - - + + Pro Controller Proコントローラ - + Dual Joycons Joy-Con(L/R) - + Left Joycon Joy-Con(L) - + Right Joycon Joy-Con(R) - + Handheld 携帯モード - + GameCube Controller ゲームキューブコントローラ - + Poke Ball Plus モンスターボールプラス - + NES Controller ファミコン・コントローラー - + SNES Controller スーパーファミコン・コントローラー - + N64 Controller ニンテンドウ64・コントローラー - + Sega Genesis メガドライブ - + Start / Pause スタート/ ポーズ - + Z Z - + Control Stick - + C-Stick Cスティック - + Shake! 振ってください - + [waiting] [待機中] - + New Profile 新規プロファイル - + Enter a profile name: プロファイル名を入力: - - + + Create Input Profile 入力プロファイルを作成 - + The given profile name is not valid! プロファイル名が無効です! - + Failed to create the input profile "%1" 入力プロファイル "%1" の作成に失敗しました - + Delete Input Profile 入力プロファイルを削除 - + Failed to delete the input profile "%1" 入力プロファイル "%1" の削除に失敗しました - + Load Input Profile 入力プロファイルをロード - + Failed to load the input profile "%1" 入力プロファイル "%1" のロードに失敗しました - + Save Input Profile 入力プロファイルをセーブ - + Failed to save the input profile "%1" 入力プロファイル "%1" のセーブに失敗しました @@ -2565,7 +2686,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< UDP Calibration: - + UDP補正: @@ -2782,42 +2903,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 開発元 - + Add-Ons アドオン - + General 全般 - + System システム - + CPU CPU - + Graphics グラフィック - + Adv. Graphics 高度なグラフィック - + Audio サウンド - + Properties プロパティ @@ -2873,37 +2994,37 @@ To invert the axes, first move your joystick vertically, and then horizontally.< アクティブなユーザー - + Username ユーザー名 - + Set Image ユーザー画像を設定 - + Add 追加 - + Rename 名前変更 - + Remove 削除 - + Profile management is available only when game is not running. プロファイル管理はゲーム未実行時にのみ行えます。 - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2911,96 +3032,105 @@ To invert the axes, first move your joystick vertically, and then horizontally.< %2 - + Enter Username ユーザ名 - + Users ユーザ - + Enter a username for the new user: 新しいユーザのユーザ名を入力: - + Enter a new username: 新しいユーザ名を入力: - - Confirm Delete - ユーザの削除 - - - - You are about to delete user with name "%1". Are you sure? - ユーザ ”%1” を削除しようとしています。続行しますか? - - - + Select User Image ユーザ画像を選択 - + JPEG Images (*.jpg *.jpeg) JPEG画像 (*.jpg *.jpeg) - + Error deleting image 画像削除エラー - + Error occurred attempting to overwrite previous image at: %1. 既存画像の上書き時にエラーが発生しました: %1 - + Error deleting file ファイル削除エラー - + Unable to delete existing file: %1. ファイルを削除できませんでした: %1 - + Error creating user image directory ユーザー画像ディレクトリ作成失敗 - + Unable to create directory %1 for storing user images. ユーザー画像保存ディレクトリ”%1”を作成できませんでした。 - + Error copying user image ユーザー画像コピーエラー - + Unable to copy image from %1 to %2 画像を”%1”から”%2”へコピー出来ませんでした。 - + Error resizing user image ユーザ画像のリサイズエラー - + Unable to resize image 画像をリサイズできません + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + ユーザの削除 + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3529,47 +3659,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< <html><head/><body><p>TAS-nxスクリプトと同じフォーマットでスクリプトからコントローラ入力を読み込みます。<br/>より詳細な説明は、yuzuホームページの<a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">ヘルプ</span></a>を参照してください。</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). どのホットキーで再生/録音を制御するかは、ホットキーの設定(設定>一般>ホットキー)を参照してください。 - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. 警告:実験的な機能です。<br/>現状では完全な再生や同期はできません。 - + Settings 設定 - + Enable TAS features TAS機能の有効化 - + Loop script スクリプトを繰り返し実行 - + Pause execution during loads ロード中は実行を一時停止 - + Script Directory スクリプトディレクトリ - + Path パス - + ... ... @@ -3821,56 +3951,71 @@ Drag points to change position, or double-click table cells to edit values. + Show Compatibility List + + + + Show Add-Ons Column アドオン列を表示 - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: ゲームアイコンサイズ: - + Folder Icon Size: フォルダアイコンサイズ: - + Row 1 Text: 1行目の表示内容: - + Row 2 Text: 2行目の表示内容: - + Screenshots スクリーンショット - + Ask Where To Save Screenshots (Windows Only) スクリーンショット時に保存先を確認する(Windowsのみ) - + Screenshots Path: スクリーンショットの保存先: - + ... ... - + Select Screenshots Path... スクリーンショットの保存先を選択... - + <System> <System> @@ -3979,7 +4124,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify 検証 @@ -4066,7 +4211,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified 未設定 @@ -4081,17 +4226,36 @@ Drag points to change position, or double-click table cells to edit values.トークンは検証されていません。トークンの変更はまだ保存されていません。 - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... 検証中... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + 検証に失敗 + + + Verification failed 検証に失敗 - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. 検証に失敗しました。トークンが正しく入力されていること、およびインターネット接続が機能していることを確認してください。 @@ -4114,7 +4278,7 @@ Drag points to change position, or double-click table cells to edit values. Direct Connect - + ダイレクト接続 @@ -4144,7 +4308,7 @@ Drag points to change position, or double-click table cells to edit values. Nickname - + ニックネーム @@ -4160,12 +4324,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting 接続中 - + Connect 接続 @@ -4173,913 +4337,915 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? yuzuを改善するための<a href='https://yuzu-emu.org/help/feature/telemetry/'>匿名データが収集されました</a>。<br/><br/>統計情報データを共有しますか? - + Telemetry テレメトリ - + Broken Vulkan Installation Detected 壊れたVulkanのインストールが検出されました。 - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. ブート時にVulkanの初期化に失敗しました。<br><br>この問題を解決するための手順は<a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>こちら</a>。 - + Loading Web Applet... Webアプレットをロード中... - - + + Disable Web Applet Webアプレットの無効化 - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Webアプレットを無効にすると、未定義の動作になる可能性があるため、スーパーマリオ3Dオールスターズでのみ使用するようにしてください。本当にWebアプレットを無効化しますか? (デバッグ設定で再度有効にすることができます)。 - + The amount of shaders currently being built ビルド中のシェーダー数 - + The current selected resolution scaling multiplier. 現在選択されている解像度の倍率。 - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. 現在のエミュレーション速度。値が100%より高いか低い場合、エミュレーション速度がSwitchより速いか遅いことを示します。 - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. ゲームが現在表示している1秒あたりのフレーム数。これはゲームごと、シーンごとに異なります。 - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Switchフレームをエミュレートするのにかかる時間で、フレームリミットやV-Syncは含まれません。フルスピードエミュレーションの場合、最大で16.67ミリ秒になります。 - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files 最近のファイルをクリア(&C) - + &Continue 再開(&C) - + &Pause 中断(&P) - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzuはゲームを起動しています - + Warning Outdated Game Format 古いゲームフォーマットの警告 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. このゲームでは、分解されたROMディレクトリフォーマットを使用しています。これは、NCA、NAX、XCI、またはNSPなどに取って代わられた古いフォーマットです。分解されたROMディレクトリには、アイコン、メタデータ、およびアップデートサポートがありません。<br><br>yuzuがサポートするSwitchフォーマットの説明については、<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>wikiをチェックしてください</a>。このメッセージは二度と表示されません。 - - + + Error while loading ROM! ROMロード中にエラーが発生しました! - + The ROM format is not supported. このROMフォーマットはサポートされていません。 - + An error occurred initializing the video core. ビデオコア初期化中にエラーが発生しました。 - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzuは、ビデオコアの実行中にエラーが発生しました。これは通常、内蔵GPUも含め、古いGPUドライバが原因です。詳しくはログをご覧ください。ログへのアクセス方法については、以下のページをご覧ください:<a href='https://yuzu-emu.org/help/reference/log-files/'>ログファイルのアップロード方法について</a>。 - + Error while loading ROM! %1 %1 signifies a numeric error code. ROMのロード中にエラー! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br><a href='https://yuzu-emu.org/help/quickstart/'>yuzuクイックスタートガイド</a>を参照してファイルを再ダンプしてください。<br>またはyuzu wiki及び</a>yuzu Discord</a>を参照するとよいでしょう。 - + An unknown error occurred. Please see the log for more details. 不明なエラーが発生しました。詳細はログを確認して下さい。 - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data データのセーブ - + Mod Data Modデータ - + Error Opening %1 Folder ”%1”フォルダを開けませんでした - - + + Folder does not exist! フォルダが存在しません! - + Error Opening Transferable Shader Cache シェーダキャッシュを開けませんでした - + Failed to create the shader cache directory for this title. このタイトル用のシェーダキャッシュディレクトリの作成に失敗しました - - Contents - コンテンツ + + Error Removing Contents + - - Update - アップデート + + Error Removing Update + - - DLC - DLC + + Error Removing DLC + - + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + Remove Entry エントリ削除 - - Remove Installed Game %1? - インストールされているゲーム%1を削除しますか? - - - - - - - - + + + + + + Successfully Removed 削除しました - + Successfully removed the installed base game. インストールされたゲームを正常に削除しました。 - - - - Error Removing %1 - %1削除エラー - - - + The base game is not installed in the NAND and cannot be removed. ゲームはNANDにインストールされていないため、削除できません。 - + Successfully removed the installed update. インストールされたアップデートを正常に削除しました。 - + There is no update installed for this title. このタイトルのアップデートはインストールされていません。 - + There are no DLC installed for this title. このタイトルにはDLCがインストールされていません。 - + Successfully removed %1 installed DLC. %1にインストールされたDLCを正常に削除しました。 - + Delete OpenGL Transferable Shader Cache? 転送可能なOpenGLシェーダキャッシュを削除しますか? - + Delete Vulkan Transferable Shader Cache? 転送可能なVulkanシェーダキャッシュを削除しますか? - + Delete All Transferable Shader Caches? 転送可能なすべてのシェーダキャッシュを削除しますか? - + Remove Custom Game Configuration? このタイトルのカスタム設定を削除しますか? - + Remove File ファイル削除 - - + + Error Removing Transferable Shader Cache 転送可能なシェーダーキャッシュの削除エラー - - + + A shader cache for this title does not exist. このタイトル用のシェーダキャッシュは存在しません。 - + Successfully removed the transferable shader cache. 転送可能なシェーダーキャッシュが正常に削除されました。 - + Failed to remove the transferable shader cache. 転送可能なシェーダーキャッシュを削除できませんでした。 - - + + Error Removing Transferable Shader Caches 転送可能なシェーダキャッシュの削除エラー - + Successfully removed the transferable shader caches. 転送可能なシェーダキャッシュを正常に削除しました。 - + Failed to remove the transferable shader cache directory. 転送可能なシェーダキャッシュディレクトリの削除に失敗しました。 - - + + Error Removing Custom Configuration カスタム設定の削除エラー - + A custom configuration for this title does not exist. このタイトルのカスタム設定は存在しません。 - + Successfully removed the custom game configuration. カスタム設定を正常に削除しました。 - + Failed to remove the custom game configuration. カスタム設定の削除に失敗しました。 - - + + RomFS Extraction Failed! RomFSの解析に失敗しました! - + There was an error copying the RomFS files or the user cancelled the operation. RomFSファイルをコピー中にエラーが発生したか、ユーザー操作によりキャンセルされました。 - + Full フル - + Skeleton スケルトン - + Select RomFS Dump Mode RomFSダンプモードの選択 - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. RomFSのダンプ方法を選択してください。<br>”完全”はすべてのファイルが新しいディレクトリにコピーされます。<br>”スケルトン”はディレクトリ構造を作成するだけです。 - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1 に RomFS を展開するための十分な空き領域がありません。Emulation > Configure > System > Filesystem > Dump Root で、空き容量を確保するか、別のダンプディレクトリを選択してください。 - + Extracting RomFS... RomFSを解析中... - - + + Cancel キャンセル - + RomFS Extraction Succeeded! RomFS解析成功! - + The operation completed successfully. 操作は成功しました。 - + Error Opening %1 ”%1”を開けませんでした - + Select Directory ディレクトリの選択 - + Properties プロパティ - + The game properties could not be loaded. ゲームプロパティをロード出来ませんでした。 - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch実行ファイル (%1);;すべてのファイル (*.*) - + Load File ファイルのロード - + Open Extracted ROM Directory 展開されているROMディレクトリを開く - + Invalid Directory Selected 無効なディレクトリが選択されました - + The directory you have selected does not contain a 'main' file. 選択されたディレクトリに”main”ファイルが見つかりませんでした。 - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) インストール可能なスイッチファイル (*.nca *.nsp *.xci);;任天堂コンテンツアーカイブ (*.nca);;任天堂サブミッションパッケージ (*.nsp);;NXカートリッジイメージ (*.xci) - + Install Files ファイルのインストール - + %n file(s) remaining 残り %n ファイル - + Installing file "%1"... "%1"ファイルをインストールしています・・・ - - + + Install Results インストール結果 - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. 競合を避けるため、NANDにゲーム本体をインストールすることはお勧めしません。 この機能は、アップデートやDLCのインストールにのみ使用してください。 - + %n file(s) were newly installed %n ファイルが新たにインストールされました - + %n file(s) were overwritten %n ファイルが上書きされました - + %n file(s) failed to install %n ファイルのインストールに失敗しました - + System Application システムアプリケーション - + System Archive システムアーカイブ - + System Application Update システムアプリケーションアップデート - + Firmware Package (Type A) ファームウェアパッケージ(Type A) - + Firmware Package (Type B) ファームウェアパッケージ(Type B) - + Game ゲーム - + Game Update ゲームアップデート - + Game DLC ゲームDLC - + Delta Title 差分タイトル - + Select NCA Install Type... NCAインストール種別を選択・・・ - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) インストールするNCAタイトル種別を選択して下さい: (ほとんどの場合、デフォルトの”ゲーム”で問題ありません。) - + Failed to Install インストール失敗 - + The title type you selected for the NCA is invalid. 選択されたNCAのタイトル種別が無効です。 - + File not found ファイルが存在しません - + File "%1" not found ファイル”%1”が存在しません - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account yuzuアカウントが存在しません - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. ゲームの互換性テストケースを送信するには、yuzuアカウントをリンクする必要があります。<br><br/>yuzuアカウントをリンクするには、エミュレーション > 設定 > Web から行います。 - + Error opening URL URLオープンエラー - + Unable to open the URL "%1". URL"%1"を開けません。 - + TAS Recording TAS 記録中 - + Overwrite file of player 1? プレイヤー1のファイルを上書きしますか? - + Invalid config detected 無効な設定を検出しました - + Handheld controller can't be used on docked mode. Pro controller will be selected. 携帯コントローラはドックモードで使用できないため、Proコントローラが選択されます。 - - - Error - エラー - - - - - The current game is not looking for amiibos - 現在のゲームはamiiboを要求しません - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed - + 現在の amiibo は削除されました - + + Error + エラー + + + + + The current game is not looking for amiibos + 現在のゲームはamiiboを要求しません + + + Amiibo File (%1);; All Files (*.*) amiiboファイル (%1);;すべてのファイル (*.*) - + Load Amiibo amiiboのロード - - Error opening Amiibo data file - amiiboデータファイルを開けませんでした - - - - Unable to open Amiibo file "%1" for reading. - amiiboデータファイル”%1”を読み込めませんでした。 - - - - Error reading Amiibo data file - amiiboデータファイルを読み込み中にエラーが発生した - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - amiiboデータを完全には読み取ることができませんでした。%1バイトを読み込もうとしましたが、%2バイトしか読み取れませんでした。 - - - + Error loading Amiibo data amiiboデータ読み込み中にエラーが発生しました - - Unable to load Amiibo data. - amiiboデータをロードできませんでした。 + + The selected file is not a valid amiibo + - + + The selected file is already on use + + + + + An unknown error occurred + + + + Capture Screenshot スクリーンショットのキャプチャ - + PNG Image (*.png) PNG画像 (*.png) - + TAS state: Running %1/%2 TAS 状態: 実行中 %1/%2 - + TAS state: Recording %1 TAS 状態: 記録中 %1 - + TAS state: Idle %1/%2 TAS 状態: アイドル %1/%2 - + TAS State: Invalid TAS 状態: 無効 - + &Stop Running 実行停止(&S) - + &Start 実行(&S) - + Stop R&ecording 記録停止(&R) - + R&ecord 記録(&R) - + Building: %n shader(s) 構築中: %n シェーダー - + Scale: %1x %1 is the resolution scaling factor 拡大率: %1x - + Speed: %1% / %2% 速度:%1% / %2% - + Speed: %1% 速度:%1% - + Game: %1 FPS (Unlocked) Game: %1 FPS(制限解除) - + Game: %1 FPS ゲーム:%1 FPS - + Frame: %1 ms フレーム:%1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU HIGH - + GPU EXTREME GPU EXTREME - + GPU ERROR GPU ERROR - + DOCKED DOCKED - + HANDHELD HANDHELD - + NEAREST NEAREST - - + + BILINEAR BILINEAR - + BICUBIC BICUBIC - + GAUSSIAN GAUSSIAN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA NO AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. ロードしようとしているゲームはプレイする前に、追加のファイルを必要とします。それはSwitchからダンプする必要があります。<br/><br/>これらのファイルのダンプの詳細については、次のWikiページを参照してください:<a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>スイッチコンソールからのシステムアーカイブと共有フォントをダンプする</a>。<br/><br/>ゲームリストに戻りますか?エミュレーションを続けると、クラッシュ、保存データの破損、またはその他のバグが発生する可能性があります。 - + yuzu was unable to locate a Switch system archive. %1 yuzuはSwitchのシステムアーカイブ "%1" を見つけられませんでした。 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzuはSwitchのシステムアーカイブ "%1" "%2" を見つけられませんでした。 - + System Archive Not Found システムアーカイブが見つかりません - + System Archive Missing システムアーカイブが見つかりません - + yuzu was unable to locate the Switch shared fonts. %1 yuzuはSwitchの共有フォント "%1" を見つけられませんでした。 - + Shared Fonts Not Found 共有フォントが存在しません - + Shared Font Missing 共有フォントが存在しません - + Fatal Error 致命的なエラー - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzuが致命的なエラーを検出しました。詳細については、ログを参照してください。ログへのアクセスの詳細については、次のページを参照してください。<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>ログファイルをアップロードする方法</a>。<br/><br/>ゲームリストに戻りますか?エミュレーションを続けると、クラッシュ、保存データの破損、またはその他のバグが発生する可能性があります。 - + Fatal Error encountered 致命的なエラー発生 - + Confirm Key Rederivation キーの再取得確認 - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5096,37 +5262,37 @@ This will delete your autogenerated key files and re-run the key derivation modu 実行すると、自動生成された鍵ファイルが削除され、鍵生成モジュールが再実行されます。 - + Missing fuses ヒューズがありません - + - Missing BOOT0 - BOOT0がありません - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Mainがありません - + - Missing PRODINFO - PRODINFOがありません - + Derivation Components Missing 派生コンポーネントがありません - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> 暗号化キーがありません。<br>キー、ファームウェア、ゲームを取得するには<a href='https://yuzu-emu.org/help/quickstart/'>yuzu クイックスタートガイド</a>を参照ください。<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5135,39 +5301,39 @@ on your system's performance. 1分以上かかります。 - + Deriving Keys 派生キー - + Select RomFS Dump Target RomFSダンプターゲットの選択 - + Please select which RomFS you would like to dump. ダンプしたいRomFSを選択して下さい。 - + Are you sure you want to close yuzu? yuzuを終了しますか? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. エミュレーションを停止しますか?セーブされていない進行状況は失われます。 - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5179,38 +5345,38 @@ Would you like to bypass this and exit anyway? GRenderWindow - + OpenGL not available! OpenGLは使用できません! - + yuzu has not been compiled with OpenGL support. yuzuはOpenGLサポート付きでコンパイルされていません。 - - + + Error while initializing OpenGL! OpenGL初期化エラー - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. GPUがOpenGLをサポートしていないか、グラフィックスドライバーが最新ではありません。 - + Error while initializing OpenGL 4.6! OpenGL4.6初期化エラー! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 GPUがOpenGL4.6をサポートしていないか、グラフィックスドライバーが最新ではありません。<br><br>GL レンダラ:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 GPUが1つ以上の必要なOpenGL拡張機能をサポートしていない可能性があります。最新のグラフィックドライバを使用していることを確認してください。<br><br>GL レンダラ:<br>%1<br><br>サポートされていない拡張機能:<br>%2 @@ -5218,153 +5384,153 @@ Would you like to bypass this and exit anyway? GameList - + Favorite お気に入り - + Start Game ゲームを開始 - + Start Game without Custom Configuration カスタム設定なしでゲームを開始 - + Open Save Data Location セーブデータディレクトリを開く - + Open Mod Data Location Modデータディレクトリを開く - + Open Transferable Pipeline Cache 転送可能なパイプラインキャッシュを開く - + Remove 削除 - + Remove Installed Update インストールされているアップデートを削除 - + Remove All Installed DLC 全てのインストールされているDLCを削除 - + Remove Custom Configuration カスタム設定を削除 - + Remove OpenGL Pipeline Cache OpenGLパイプラインキャッシュを削除 - + Remove Vulkan Pipeline Cache Vulkanパイプラインキャッシュを削除 - + Remove All Pipeline Caches すべてのパイプラインキャッシュを削除 - + Remove All Installed Contents 全てのインストールされているコンテンツを削除 - + Dump RomFS RomFSをダンプ - + Dump RomFS to SDMC RomFSをSDMCにダンプ - + Copy Title ID to Clipboard タイトルIDをクリップボードへコピー - + Navigate to GameDB entry GameDBエントリを表示 - + Properties プロパティ - + Scan Subfolders サブフォルダをスキャンする - + Remove Game Directory ゲームディレクトリを削除する - + ▲ Move Up ▲ 上へ移動 - + ▼ Move Down ▼ 下へ移動 - + Open Directory Location ディレクトリの場所を開く - + Clear クリア - + Name ゲーム名 - + Compatibility 互換性 - + Add-ons アドオン - + File type ファイル種別 - + Size ファイルサイズ @@ -5373,76 +5539,61 @@ Would you like to bypass this and exit anyway? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect カンペキ - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - サウンドまたはグラフィックの不具合は見られず、回避策なしで完全に動作します。 - - - - Great - バツグン - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - グラフィックまたはサウンドに軽微な不具合がありますが、ゲームを最初から最後までプレイ可能です。回避策が必要な場合があります - - Okay - ソコソコ - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - グラフィックまたはサウンドの重大な不具合がありますが、回避策を使うことで最初から最後までプレイ可能です。 + Game can be played without issues. + - Bad - ナンアリ + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - グラフィックまたはサウンドに重大な不具合があります。回避策を使用しても不具合のため特定の場所から進めなくなります。 + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu イントロ - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - グラフィックまたはサウンドの重大な不具合のため、プレイ不可能です。スタート画面から先にむことが出来ません。 + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot 起動不可 - + The game crashes when attempting to startup. ゲームは起動時にクラッシュしました。 - + Not Tested 未テスト - + The game has not yet been tested. このゲームはまだテストされていません。 @@ -5450,7 +5601,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list 新しいゲームリストフォルダを追加するにはダブルクリックしてください。 @@ -5463,12 +5614,12 @@ Screen. - + Filter: フィルター: - + Enter pattern to filter フィルターパターンを入力 @@ -5493,7 +5644,7 @@ Screen. Max Players - + 最大プレイヤー数 @@ -5538,18 +5689,18 @@ Screen. Host Room - + ホストルーム HostRoomWindow - + Error エラー - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: 公開ロビーへの部屋のアナウンスに失敗しました。部屋を公開するためには、Emulation -> Configure -> Web で有効なyuzuアカウントが設定されている必要があります。もし、部屋を公開ロビーに公開したくないのであれば、代わりに非公開を選択してください。 @@ -5559,11 +5710,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute 音声ミュート/解除 + @@ -5585,114 +5737,113 @@ Debug Message: - Main Window メイン画面 - + Audio Volume Down 音量を下げる - + Audio Volume Up 音量を上げる - + Capture Screenshot スクリーンショットを撮る - + Change Adapting Filter アダプティングフィルターの変更 - + Change Docked Mode ドックモードを変更 - + Change GPU Accuracy GPU精度を変更 - + Continue/Pause Emulation エミュレーションの一時停止/再開 - + Exit Fullscreen フルスクリーンをやめる - + Exit yuzu yuzuを終了 - + Fullscreen フルスクリーン - + Load File ファイルのロード - + Load/Remove Amiibo 読み込み/解除 Amiibo - + Restart Emulation エミュレーションをリスタート - + Stop Emulation エミュレーションをやめる - + TAS Record TAS 記録 - + TAS Reset TAS リセット - + TAS Start/Stop TAS 開始/停止 - + Toggle Filter Bar - + フィルタバー切り替え + + + + Toggle Framerate Limit + フレームレート制限切り替え - Toggle Framerate Limit - - - - Toggle Mouse Panning - + Toggle Status Bar - + ステータスバー切り替え @@ -5771,13 +5922,13 @@ Debug Message: Public Room Browser - + 公開ルームブラウザ Nickname - + ニックネーム @@ -5805,42 +5956,42 @@ Debug Message: ロビー更新 - + Password Required to Join 参加にはパスワードが必要です。 - + Password: パスワード: - - Room Name - ルーム名 - - - - Preferred Game - - - - - Host - ホスト - - - + Players プレイヤー - + + Room Name + ルーム名 + + + + Preferred Game + + + + + Host + ホスト + + + Refreshing 更新中 - + Refresh List リスト更新 @@ -5863,232 +6014,237 @@ Debug Message: 最近のファイル(&R) - + &Emulation エミュレーション(&E) - + &View 表示(&V) - + &Reset Window Size ウィンドウサイズのリセット(&R) - + &Debugging デバッグ(&D) - + Reset Window Size to &720p &720P - + Reset Window Size to 720p ウィンドウサイズを720Pにリセット - + Reset Window Size to &900p &900P - + Reset Window Size to 900p ウィンドウサイズを900Pにリセット - + Reset Window Size to &1080p &1080P - + Reset Window Size to 1080p ウィンドウサイズを1080Pにリセット - + + &Multiplayer + + + + &Tools ツール(&T) - + &TAS &TAS - + &Help ヘルプ(&H) - + &Install Files to NAND... ファイルをNANDにインストール...(&I) - + L&oad File... ファイルをロード...(&L) - + Load &Folder... フォルダをロード...(&F) - + E&xit 終了(&E) - + &Pause 中断(&P) - + &Stop 停止(&S) - + &Reinitialize keys... 鍵を再初期化...(&R) - + &About yuzu yuzuについて(&A) - + Single &Window Mode シングルウィンドウモード(&W) - + Con&figure... 設定...(&F) - + Display D&ock Widget Headers ドックウィジェットヘッダ(&O) - + Show &Filter Bar フィルタバー(&F) - + Show &Status Bar ステータスバー(&S) - + Show Status Bar ステータスバーの表示 - - - Browse Public Game Lobby - 公開ゲームロビーを開く - - - - Create Room - ルーム作成 - - Leave Room - ルームを離れる + &Browse Public Game Lobby + - - Direct Connect to Room - ルームに直接つなぐ + + &Create Room + - - Show Current Room - 現在のルームを表示 + + &Leave Room + + &Direct Connect to Room + + + + + &Show Current Room + + + + F&ullscreen 全画面表示(&F) - + &Restart 再実行(&R) - + Load/Remove &Amiibo... - + &Amiibo をロード/削除... - + &Report Compatibility 互換性を報告(&R) - + Open &Mods Page &Modページを開く - + Open &Quickstart Guide クイックスタートガイドを開く(&Q) - + &FAQ &FAQ - + Open &yuzu Folder &yuzuフォルダを開く - + &Capture Screenshot スクリーンショットをキャプチャ(&C) - + &Configure TAS... - TASを設定... (%C) + TASを設定... (&C) - + Configure C&urrent Game... 現在のゲームを設定...(&U) - + &Start 実行(&S) - + &Reset リセット(&R) - + R&ecord 記録(&R) @@ -6137,7 +6293,7 @@ Debug Message: Forum Username - + フォーラムのユーザ名 @@ -6153,47 +6309,42 @@ Debug Message: MultiplayerState - - + Current connection status 現在の接続状態 - - + Not Connected. Click here to find a room! 接続されていません。ここをクリックして部屋を見つけてください。 - - + Not Connected + 未接続 + + + Connected 接続の状態 - - - Not Connected - + + New Messages Received + 新たなメッセージを受信しました - + Error エラー - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: ルーム情報の更新に失敗しました。インターネット接続を確認し、再度ルームのホストをお試しください。 デバッグメッセージ: - - - New Messages Received - 新たなメッセージを受信しました - NetworkMessage @@ -6225,7 +6376,7 @@ Debug Message: You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list. - + ルームをホスティングするには, 優先ゲームを選択する必要があります. リストにまだゲームがない場合は, リストのプラスアイコンをクリックしてゲームフォルダを追加してください. @@ -6295,22 +6446,40 @@ They may have left the room. 退室した可能性があります。 - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + ゲーム進行中 + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + ゲーム進行中にルームに参加することはお勧めしません. ルーム機能が正しく動作しない原因になります. +とにかく実行しますか? + + + Leave Room ルームを離れる - + You are about to close the room. Any network connections will be closed. 部屋を閉じようとしています。ネットワーク接続がすべて終了します。 - + Disconnect 切断 - + You are about to leave the room. Any network connections will be closed. 部屋を退出しようとしています。ネットワーク接続はすべて終了します。 @@ -6318,7 +6487,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error エラー @@ -6367,42 +6536,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game %1はゲームのプレイ中ではありません - + %1 is playing %2 %1は%2をプレイ中です - + Not playing a game - + Installed SD Titles インストール済みSDタイトル - + Installed NAND Titles インストール済みNANDタイトル - + System Titles システムタイトル - + Add New Game Directory 新しいゲームディレクトリを追加する - + Favorites お気に入り @@ -6432,7 +6601,7 @@ p, li { white-space: pre-wrap; } - + [not set] [未設定] @@ -6447,10 +6616,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 軸 %1%2 @@ -6464,9 +6633,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [不明] @@ -6631,15 +6800,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [無効] - - + + %1%2Hat %3 @@ -6647,35 +6816,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 %1%2ボタン %3 - + [unused] [未使用] @@ -6693,7 +6862,7 @@ p, li { white-space: pre-wrap; } Wheel Indicates the mouse wheel - + ホイール @@ -6716,11 +6885,124 @@ p, li { white-space: pre-wrap; } - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + タイプ + + + + Name + ゲーム名 + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6824,6 +7106,7 @@ p, li { white-space: pre-wrap; } + Handheld 携帯モード @@ -6863,11 +7146,6 @@ p, li { white-space: pre-wrap; } Docked Docked - - - Undocked - Undocked - Vibration diff --git a/dist/languages/ko_KR.ts b/dist/languages/ko_KR.ts index 9dcae2b..2fc4969 100644 --- a/dist/languages/ko_KR.ts +++ b/dist/languages/ko_KR.ts @@ -36,7 +36,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">웹사이트</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">소스 코드</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">기여자</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">라이센스</span></a></p></body></html> @@ -82,95 +82,97 @@ p, li { white-space: pre-wrap; } Room Window - + 방의 창 Send Chat Message - + 채팅 메시지 보내기 Send Message - + 메시지 보내기 - + Members - + 멤버 - + %1 has joined - + %1이(가) 참여하였습니다 - + %1 has left - + %1이(가) 떠났습니다 - + %1 has been kicked - + %1이(가) 추방되었습니다 - + %1 has been banned - + %1이(가) 차단되었습니다 - + %1 has been unbanned - + %1이(가) 차단 해제되었습니다 - + View Profile - - - - - - Block Player - - - - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - - - - - Kick - - - - - Ban - + 프로필 보기 + - Kick Player - + Block Player + 차단된 플레이어 + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + 플레이어를 차단하면 더 이상 채팅 메시지를 받을 수 없습니다.<br><br>%1을(를) 차단하겠습니까? + + + + Kick + 추방 + + + + Ban + 차단 + + + + Kick Player + 추방된 플레이어 + + + Are you sure you would like to <b>kick</b> %1? - + 정말로 %1을 <b>추방</b>하겠습니까?? - + Ban Player - + 차단된 플레이어 - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - + %1을 <b>추방</b>하겠습니까? + +이렇게 하면 포럼 사용자 이름과 IP 주소가 모두 금지됩니다. @@ -178,22 +180,22 @@ This would ban both their forum username and their IP address. Room Window - + 방의 창 Room Description - + 방 설명 Moderation... - + 조정... Leave Room - + 방 나가기 @@ -206,12 +208,12 @@ This would ban both their forum username and their IP address. Disconnected - + 연결 끊김 - %1 (%2/%3 members) - connected - + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 멤버) - 연결됨 @@ -224,6 +226,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility 게임 호환성 보고 @@ -233,92 +240,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;"> yuzu 호환성 리스트에</span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">테스트 결과를 제출한 경우</span></a><span style=" font-size:10pt;"> 해당 정보가 수집되어 사이트에 게시됩니다:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">하드웨어 정보 (CPU / GPU / 운영체제)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">사용된 yuzu 버전</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">연결된 yuzu 계정</li></ul></body></html> - - Perfect - 완벽함 + + <html><head/><body><p>Does the game boot?</p></body></html> + <html><head/><body><p>게임이 부팅되나요?</p></body></html> - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>그래픽이나 오디오 문제 없이 게임의 모든 기능이 정상 작동합니다.</p></body></html> + + Yes The game starts to output video or audio + 예 게임이 비디오 또는 오디오 출력을 시작합니다 - - Great - 좋음 + + No The game doesn't get past the "Launching..." screen + 아니요 게임이 "실행 중..." 화면을 통과하지 않습니다 - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>그래픽이나 오디오 문제가 약간 있지만 게임을 끝까지 클리어할 수 있습니다. 별도의 해결책이 필요할 수 있습니다.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + 예 게임이 인트로/메뉴를 지나 게임 플레이로 들어갑니다 - - Okay - 평범함 + + No The game crashes or freezes while loading or using the menu + 아니요 메뉴를 로드하거나 사용하는 동안 게임이 충돌하거나 멈춥니다 - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>그래픽이나 오디오 문제가 다소 있지만 별도의 해결책을 사용하면 게임을 끝까지 클리어할 수는 있습니다.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + <html><head/><body><p>게임이 게임 플레이에 도달합니까?</p></body></html> - - Bad - 나쁨 + + Yes The game works without crashes + 예 충돌 없이 게임이 작동합니다 - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p> 게임은 작동하지만 그래픽이나 오디오 문제가 많으며 별도의 해결책을 사용해도 특정 구간에서 완전히 깨져서 진행이 불가합니다.</p></body></html> + + No The game crashes or freezes during gameplay + 아니요 게임 플레이 중에 게임이 충돌하거나 멈춥니다 - - Intro/Menu - 인트로/메뉴 + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + <html><head/><body><p>게임 플레이 중에 충돌, 정지 또는 잠김 없이 게임이 작동합니까?</p></body></html> - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>그래픽이나 오디오 문제가 심각해서 게임 플레이 자체가 불가하거나 시작 화면에서 넘어가지 않습니다.</p></body></html> + + Yes The game can be finished without any workarounds + 예 해결 방법 없이 게임을 종료할 수 있습니다 - - Won't Boot - 실행 불가 + + No The game can't progress past a certain area + 아니요 게임이 특정 영역을 지나서 진행할 수 없습니다 - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>게임 실행 시 크래시가 일어납니다.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + <html><head/><body><p>게임을 처음부터 끝까지 완벽하게 플레이할 수 있습니까?</p></body></html> - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p> 속도와 성능은 별개로, 얼마나 이 yuzu 버전에서 해당 게임이 처음부터 끝까지 잘 작동합니까?</p></body></html> + + Major The game has major graphical errors + 주요 게임에 주요 그래픽 오류가 있습니다 - + + Minor The game has minor graphical errors + 마이너 게임에 경미한 그래픽 오류가 있습니다 + + + + None Everything is rendered as it looks on the Nintendo Switch + 없음 닌텐도 스위치에서 보이는 대로 모든 것이 렌더링됩니다 + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + <html><head/><body><p>게임에 그래픽 결함이 있습니까?</p></body></html> + + + + Major The game has major audio errors + 메이저 게임에 주요 오디오 오류가 있습니다 + + + + Minor The game has minor audio errors + 마이너 게임에 사소한 오디오 오류가 있습니다 + + + + None Audio is played perfectly + 없음 오디오가 완벽하게 재생됩니다 + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + <html><head/><body><p>게임에 오디오 결함/효과 누락이 있습니까?</p></body></html> + + + Thank you for your submission! 제출 감사합니다! - + Submitting 제출중 - + Communication error 통신 에러 - + An error occurred while sending the Testcase 테스트 결과 전송 중 오류 발생 - + Next 다음 @@ -339,7 +381,7 @@ This would ban both their forum username and their IP address. Output Device - + 출력 장치 @@ -378,37 +420,37 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + 적외선 카메라 구성 Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + 에뮬레이트된 카메라의 이미지 출처를 선택합니다. 가상 카메라일 수도 있고 실제 카메라일 수도 있습니다. Camera Image Source: - + 카메라 이미지 출처: Input device: - + 입력 장치: Preview - + 미리보기 Resolution: 320*240 - + 해상도: 320*240 Click to preview - + 클릭하여 미리보기 @@ -416,7 +458,7 @@ This would ban both their forum username and their IP address. 기본값으로 초기화 - + Auto 자동 @@ -669,12 +711,14 @@ This would ban both their forum username and their IP address. <div>Enables IR optimizations that involve constant propagation.</div> - 상수 전파(constant propagation)를 포함하는 IR 최적화를 활성화합니다. + + <div>지속적인 전파를 포함하는 IR 최적화를 활성화합니다.</div> + Enable constant propagation - 상수 전파(constant propagation) 활성화 + 지속적인 전파 활성화 @@ -768,200 +812,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger 디버거 - + Enable GDB Stub GDB Stub 활성화 - + Port: 포트: - + Logging 로깅 - + Global Log Filter 전역 로그 필터 - + Show Log in Console 콘솔에 로그 표시 - + Open Log Location 로그 경로 열기 - + When checked, the max size of the log increases from 100 MB to 1 GB 이 옵션을 활성화 시, 로그 파일의 최대 용량이 100MB에서 1GB로 증가합니다. - + Enable Extended Logging** 확장된 로깅 활성화** - + Homebrew 홈브류 - + Arguments String 실행인수 - + Graphics 그래픽 - + When checked, the graphics API enters a slower debugging mode 이 옵션을 활성화 시, 그래픽 API가 디버깅 모드로 진입하여 속도가 느려집니다. - + Enable Graphics Debugging 그래픽 디버깅 활성화 - + When checked, it enables Nsight Aftermath crash dumps 선택하면 Nsight Aftermath 크래시 덤프가 활성화됩니다. - + Enable Nsight Aftermath Nsight Aftermath 활성화 - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found 선택하면 디스크 셰이더 캐시 또는 게임에서 찾은 모든 원본 어셈블러 셰이더를 덤프합니다. - + Dump Game Shaders 게임 셰이더 덤프 - + When checked, it will dump all the macro programs of the GPU 체크하면 GPU의 모든 매크로 프로그램을 덤프합니다. - + Dump Maxwell Macros Maxwell 매크로 덤프 - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower 이 옵션에 체크할 시, 매크로 JIT 컴파일러를 비활성화 합니다. 게임 속도가 느려지니 유의하십시오. - + Disable Macro JIT Macro JIT 비활성화 - + When checked, yuzu will log statistics about the compiled pipeline cache 선택하면 yuzu는 컴파일된 파이프라인 캐시에 대한 통계를 기록합니다. - + Enable Shader Feedback 셰이더 피드백 활성화 - + When checked, it executes shaders without loop logic changes 체크 시 루프 로직 변경 없이 셰이더 실행 - + Disable Loop safety checks 루프 안전 검사 비활성화 - + Debugging 디버깅 - - Enable FS Access Log - FS 액세스 로그 활성화 - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** 자세한 리포팅 서비스 활성화** - + + Enable FS Access Log + FS 액세스 로그 활성화 + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + 이 옵션을 활성화하면 가장 최근에 생성된 오디오 명령어 목록을 콘솔에 출력할 수 있습니다. 오디오 렌더러를 사용하는 게임에만 영향을 줍니다. + + + + Dump Audio Commands To Console** + 콘솔에 오디오 명령어 덤프 + + + + Create Minidump After Crash + 충돌후 미니덤프 생성 + + + Advanced 고급 - + Kiosk (Quest) Mode Kiosk (Quest) 모드 - + Enable CPU Debugging CPU 디버깅 활성화 - + Enable Debug Asserts 디버그 에러 검출 활성화 - + Enable Auto-Stub** 자동 스텁 활성화** - + Enable All Controller Types 모든 컨트롤러 유형 활성화 - + Disable Web Applet 웹 애플릿 비활성화 - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + 프로그램 시작시 yuzu가 Vulkan 환경을 확인할 수 있도록 합니다. 외부 프로그램에서 유자를 보는 데 문제가 있는 경우 이 기능을 비활성화합니다. + + + + Perform Startup Vulkan Check + 시작시 Vulkan 검사 수행 + + + **This will be reset automatically when yuzu closes. **Yuzu가 종료되면 자동으로 재설정됩니다. + + + Restart Required + 재시작 필요 + + + + yuzu is required to restart in order to apply this setting. + 이 설정을 적용하려면 yuzu를 다시 시작해야 합니다. + + + + Web applet not compiled + 웹 애플릿이 컴파일되지 않음 + + + + MiniDump creation not compiled + MiniDump 생성이 컴파일되지 않음 + ConfigureDebugController @@ -1229,7 +1308,7 @@ This would ban both their forum username and their IP address. Form - Form + 형태 @@ -1331,193 +1410,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings 그래픽 설정 - + Use disk pipeline cache 디스크 파이프라인 캐시 사용 - + Use asynchronous GPU emulation 비동기 GPU 에뮬레이션 사용 - + Accelerate ASTC texture decoding ASTC 텍스처 디코딩 가속화 - + NVDEC emulation: NVDEC 에뮬레이션: - + No Video Output 비디오 출력 없음 - + CPU Video Decoding CPU 비디오 디코딩 - + GPU Video Decoding (Default) GPU 비디오 디코딩(기본값) - + Fullscreen Mode: 전체 화면 모드: - + Borderless Windowed 경계 없는 창 모드 - + Exclusive Fullscreen 독점 전체화면 모드 - + Aspect Ratio: - 종횡비: + 화면비: - + Default (16:9) 기본 (16:9) - + Force 4:3 강제 4:3 - + Force 21:9 강제 21:9 - + + Force 16:10 + 강제 16:10 + + + Stretch to Window 창에 맞게 늘림 - + Resolution: 해상도: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [실험적] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [실험적] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: 윈도우 적응형 필터: - + Nearest Neighbor Nearest Neighbor - + Bilinear - Bilinear + 이중선형 - + Bicubic - Bicubic + 고등차수보간 - + Gaussian - Gaussian + 가우시안 - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) - AMD FidelityFX™️ 슈퍼 해상도(Vulkan 전용) + AMD FidelityFX™️ 슈퍼 해상도 (Vulkan 전용) - + Anti-Aliasing Method: 안티에일리어싱 방식: - + None 없음 - + FXAA FXAA - - + + Use global FSR Sharpness + 글로벌 FSR 선명도 사용 + + + + Set FSR Sharpness + FSR 선명도 설정 + + + + FSR Sharpness: + FSR 선명도: + + + + 100% + 100% + + + + Use global background color 전역 배경색 사용 - + Set background color: 배경색 설정: - + Background Color: 배경색: @@ -1526,6 +1630,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM(어셈블리 셰이더, NVIDIA 전용) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1557,7 +1667,7 @@ This would ban both their forum username and their IP address. Use VSync - + 수직 동기화 사용 @@ -1580,37 +1690,47 @@ This would ban both their forum username and their IP address. 빠른 GPU 시간 사용(Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + 비관적 버퍼 플러시를 활성화합니다. 이 옵션은 수정되지 않은 버퍼를 강제로 비우므로 성능이 저하될 수 있습니다. + + + + Use pessimistic buffer flushes (Hack) + 비관적 버퍼 플러시 사용(Hack) + + + Anisotropic Filtering: 비등방성 필터링: - + Automatic 자동 - + Default 기본값 - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2010,7 +2130,7 @@ This would ban both their forum username and their IP address. Infrared Camera - + 적외선 카메라 @@ -2102,7 +2222,7 @@ This would ban both their forum username and their IP address. - + Left Stick L 스틱 @@ -2196,14 +2316,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2222,7 +2342,7 @@ This would ban both their forum username and their IP address. - + Plus + @@ -2235,15 +2355,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2300,231 +2420,236 @@ This would ban both their forum username and their IP address. - + Right Stick R 스틱 - - - - + + + + Clear 초기화 - - - - - + + + + + [not set] [설정 안 됨] - - - Toggle button - 토글 버튼 - - - - + + Invert button 버튼 반전 - - + + + Toggle button + 토글 버튼 + + + + Invert axis 축 뒤집기 - - - + + + Set threshold 임계값 설정 - - + + Choose a value between 0% and 100% 0%에서 100% 안의 값을 고르세요 - + + Toggle axis + axis 토글 + + + Set gyro threshold 자이로 임계값 설정 - + Map Analog Stick 아날로그 스틱 맵핑 - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. OK 버튼을 누른 후에 먼저 조이스틱을 수평으로 움직이고, 그 다음 수직으로 움직이세요. 축을 뒤집으려면 수직으로 먼저 움직인 뒤에 수평으로 움직이세요. - + Center axis 중심축 - - + + Deadzone: %1% 데드존: %1% - - + + Modifier Range: %1% 수정자 범위: %1% - - + + Pro Controller 프로 컨트롤러 - + Dual Joycons 듀얼 조이콘 - + Left Joycon 왼쪽 조이콘 - + Right Joycon 오른쪽 조이콘 - + Handheld 휴대 모드 - + GameCube Controller GameCube 컨트롤러 - + Poke Ball Plus 몬스터볼 Plus - + NES Controller NES 컨트롤러 - + SNES Controller SNES 컨트롤러 - + N64 Controller N64 컨트롤러 - + Sega Genesis 세가 제네시스 - + Start / Pause 시작 / 일시중지 - + Z Z - + Control Stick 컨트롤 스틱 - + C-Stick C-Stick - + Shake! 흔드세요! - + [waiting] [대기중] - + New Profile 새 프로필 - + Enter a profile name: 프로필 이름을 입력하세요: - - + + Create Input Profile 입력 프로필 생성 - + The given profile name is not valid! 해당 프로필 이름은 사용할 수 없습니다! - + Failed to create the input profile "%1" "%1" 입력 프로필 생성 실패 - + Delete Input Profile 입력 프로필 삭제 - + Failed to delete the input profile "%1" "%1" 입력 프로필 삭제 실패 - + Load Input Profile 입력 프로필 불러오기 - + Failed to load the input profile "%1" "%1" 입력 프로필 불러오기 실패 - + Save Input Profile 입력 프로필 저장 - + Failed to save the input profile "%1" "%1" 입력 프로필 저장 실패 @@ -2779,42 +2904,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 개발자 - + Add-Ons 부가 기능 - + General 일반 - + System 시스템 - + CPU CPU - + Graphics 그래픽 - + Adv. Graphics 고급 그래픽 - + Audio 오디오 - + Properties 속성 @@ -2870,37 +2995,37 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 현재 유저 - + Username 유저 이름 - + Set Image 이미지 설정 - + Add 추가 - + Rename 이름 변경 - + Remove 제거 - + Profile management is available only when game is not running. 프로필 관리자는 게임이 작동 중이지 않을 때만 사용 가능합니다. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2908,96 +3033,106 @@ To invert the axes, first move your joystick vertically, and then horizontally.< %2 - + Enter Username 유저 이름을 입력하세요 - + Users 유저 - + Enter a username for the new user: 새로운 유저를 위한 유저 이름을 입력하세요: - + Enter a new username: 새로운 유저 이름을 입력하세요: - - Confirm Delete - 삭제 확인 - - - - You are about to delete user with name "%1". Are you sure? - 사용자 "%1"을(를) 삭제하려고 합니다. 계속하시겠습니까? - - - + Select User Image 유저 이미지 선택 - + JPEG Images (*.jpg *.jpeg) JPEG 이미지 (*.jpg *.jpeg) - + Error deleting image 이미지 삭제 오류 - + Error occurred attempting to overwrite previous image at: %1. %1에서 이전 이미지를 덮어쓰는 중 오류가 발생했습니다. - + Error deleting file 파일 삭제 오류 - + Unable to delete existing file: %1. 기존 파일을 삭제할 수 없음: %1. - + Error creating user image directory 사용자 이미지 디렉토리 생성 오류 - + Unable to create directory %1 for storing user images. 사용자 이미지를 저장하기 위한 %1 디렉토리를 만들 수 없습니다. - + Error copying user image 사용자 이미지 복사 오류 - + Unable to copy image from %1 to %2 이미지를 %1에서 %2로 복사할 수 없습니다 - + Error resizing user image 사용자 이미지 크기 조정 오류 - + Unable to resize image 이미지 크기를 조정할 수 없습니다 + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + 이 사용자를 삭제하시겠습니까? 사용자의 저장 데이터가 모두 삭제됩니다. + + + + Confirm Delete + 삭제 확인 + + + + Name: %1 +UUID: %2 + 이름: %1 +UUID: %2 + + ConfigureRingController @@ -3526,47 +3661,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< <html><head/><body><p>TAS-nx 스크립트와 동일한 형식의 스크립트에서 컨트롤러 입력을 읽습니다.<br/>더 자세한 설명은 yuzu 웹사이트에 있는 <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">help page</span></a>를 참조하세요.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). 재생/녹화를 제어하는 ​​단축키를 확인하려면 단축키 설정(설정 -> 일반 -> 단축키)을 참조하십시오. - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. 경고: 이것은 실험적인 기능입니다.<br/>현재의 불완전한 동기화 방법으로는 스크립트 프레임을 완벽하게 재생하지 않습니다. - + Settings 설정 - + Enable TAS features TAS 기능 활성화 - + Loop script 반복 스크립트 - + Pause execution during loads 로드 중 실행 일시중지 - + Script Directory 스크립트 주소 - + Path 주소 - + ... ... @@ -3648,7 +3783,7 @@ Drag points to change position, or double-click table cells to edit values. Enter the name for the new profile. - 새 프로필의 이름을 적으시오. + 새 프로필의 이름을 입력하세요. @@ -3818,56 +3953,71 @@ Drag points to change position, or double-click table cells to edit values. + Show Compatibility List + 환성 목록 표시 + + + Show Add-Ons Column 추가 기능 열 표시 - + + Show Size Column + 크기 열 표시 + + + + Show File Types Column + 파일 형식 열 표시 + + + Game Icon Size: 게임 아이콘 크기: - + Folder Icon Size: 폴더 아이콘 크기: - + Row 1 Text: 1번째 행 텍스트: - + Row 2 Text: 2번째 행 텍스트: - + Screenshots 스크린샷 - + Ask Where To Save Screenshots (Windows Only) 스크린샷 저장 위치 물어보기 (Windows 전용) - + Screenshots Path: 스크린샷 경로 : - + ... ... - + Select Screenshots Path... 스크린샷 경로 선택... - + <System> <System> @@ -3976,7 +4126,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify 인증 @@ -4003,7 +4153,7 @@ Drag points to change position, or double-click table cells to edit values. Web Service configuration can only be changed when a public room isn't being hosted. - + 웹 서비스 구성은 공개 방이 호스팅되지 않을 때만 변경할 수 있습니다. @@ -4063,7 +4213,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified 불특정 @@ -4078,17 +4228,36 @@ Drag points to change position, or double-click table cells to edit values.토큰이 확인되지 않았습니다. 토큰 변경 사항이 저장되지 않을 것입니다. - + + Unverified, please click Verify before saving configuration + Tooltip + 인증되지 않음, 구성을 저장하기 전에 인증을 클릭하십시오. + + + + Verifying... 인증 중... - + + Verified + Tooltip + 인증됨 + + + + Verification failed + Tooltip + 인증 실패 + + + Verification failed 인증 실패 - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. 인증 실패. 토큰을 올바르게 입력했는지, 그리고 인터넷이 연결되어 있는지 확인하십시오. @@ -4111,972 +4280,974 @@ Drag points to change position, or double-click table cells to edit values. Direct Connect - + 직접 연결 IP Address - + IP 주소 IP - + IP <html><head/><body><p>IPv4 address of the host</p></body></html> - + <html><head/><body><p>호스트의 IPv4 주소</p></body></html> Port - + 포트 <html><head/><body><p>Port number the host is listening on</p></body></html> - + <html><head/><body><p>호스트가 수신 대기 중인 포트 번호</p></body></html> Nickname - + 별명 Password - + 비밀번호 Connect - + 연결 DirectConnectWindow - + Connecting - + 연결중 - + Connect - + 연결 GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? yuzu를 개선하기 위해 <a href='https://yuzu-emu.org/help/feature/telemetry/'>익명 데이터가 수집됩니다.</a> <br/><br/>사용 데이터를 공유하시겠습니까? - + Telemetry 원격 측정 - + Broken Vulkan Installation Detected 망가진 Vulkan 설치 감지됨 - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + 부팅하는 동안 Vulkan 초기화에 실패했습니다.<br><br>문제 해결 지침을 보려면 <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>여기</a>를 클릭하세요. - + Loading Web Applet... 웹 애플릿을 로드하는 중... - - + + Disable Web Applet 웹 애플릿 비활성화 - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) 웹 애플릿을 비활성화하면 정의되지 않은 동작이 발생할 수 있으며 Super Mario 3D All-Stars에서만 사용해야 합니다. 웹 애플릿을 비활성화하시겠습니까? (디버그 설정에서 다시 활성화할 수 있습니다.) - + The amount of shaders currently being built 현재 생성중인 셰이더의 양 - + The current selected resolution scaling multiplier. 현재 선택된 해상도 배율입니다. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. 현재 에뮬레이션 속도. 100%보다 높거나 낮은 값은 에뮬레이션이 Switch보다 빠르거나 느린 것을 나타냅니다. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 게임이 현재 표시하고 있는 초당 프레임 수입니다. 이것은 게임마다 다르고 장면마다 다릅니다. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 프레임 제한이나 수직 동기화를 계산하지 않고 Switch 프레임을 에뮬레이션 하는 데 걸린 시간. 최대 속도로 에뮬레이트 중일 때에는 대부분 16.67 ms 근처입니다. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files Clear Recent Files(&C) - + &Continue 재개(&C) - + &Pause 일시중지(&P) - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu가 게임을 실행중입니다 - + Warning Outdated Game Format 오래된 게임 포맷 경고 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. 이 게임 파일은 '분해된 ROM 디렉토리'라는 오래된 포맷을 사용하고 있습니다. 해당 포맷은 NCA, NAX, XCI 또는 NSP와 같은 다른 포맷으로 대체되었으며 분해된 ROM 디렉토리에는 아이콘, 메타 데이터 및 업데이트가 지원되지 않습니다.<br><br>yuzu가 지원하는 다양한 Switch 포맷에 대한 설명은 <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>위키를 확인하세요.</a> 이 메시지는 다시 표시되지 않습니다. - - + + Error while loading ROM! ROM 로드 중 오류 발생! - + The ROM format is not supported. 지원되지 않는 롬 포맷입니다. - + An error occurred initializing the video core. 비디오 코어를 초기화하는 동안 오류가 발생했습니다. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. 비디오 코어를 실행하는 동안 yuzu에 오류가 발생했습니다. 이것은 일반적으로 통합 드라이버를 포함하여 오래된 GPU 드라이버로 인해 발생합니다. 자세한 내용은 로그를 참조하십시오. 로그 액세스에 대한 자세한 내용은 <a href='https://yuzu-emu.org/help/reference/log-files/'>로그 파일 업로드 방법</a> 페이지를 참조하세요. - + Error while loading ROM! %1 %1 signifies a numeric error code. ROM 불러오는 중 오류 발생! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>파일들을 다시 덤프하기 위해<a href='https://yuzu-emu.org/help/quickstart/'>yuzu 빠른 시작 가이드</a> 를 따라주세요.<br>도움이 필요할 시 yuzu 위키</a> 를 참고하거나 yuzu 디스코드</a> 를 이용해보세요. - + An unknown error occurred. Please see the log for more details. 알 수 없는 오류가 발생했습니다. 자세한 내용은 로그를 참고하십시오. - + (64-bit) (64비트) - + (32-bit) (32비트) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data 세이브 데이터 - + Mod Data 모드 데이터 - + Error Opening %1 Folder %1 폴더 열기 오류 - - + + Folder does not exist! 폴더가 존재하지 않습니다! - + Error Opening Transferable Shader Cache 전송 가능한 셰이더 캐시 열기 오류 - + Failed to create the shader cache directory for this title. 이 타이틀에 대한 셰이더 캐시 디렉토리를 생성하지 못했습니다. - - Contents - 컨텐츠 + + Error Removing Contents + 콘텐츠 제거 중 오류 발생 - - Update - 업데이트 + + Error Removing Update + 업데이트 제거 오류 - - DLC - DLC + + Error Removing DLC + DLC 제거 오류 - + + Remove Installed Game Contents? + 설치된 게임 콘텐츠를 제거하겠습니까? + + + + Remove Installed Game Update? + 설치된 게임 업데이트를 제거하겠습니까? + + + + Remove Installed Game DLC? + 설치된 게임 DLC를 제거하겠습니까? + + + Remove Entry 항목 제거 - - Remove Installed Game %1? - 설치된 게임을 삭제 %1? - - - - - - - - + + + + + + Successfully Removed 삭제 완료 - + Successfully removed the installed base game. 설치된 기본 게임을 성공적으로 제거했습니다. - - - - Error Removing %1 - 삭제 중 오류 발생 %1 - - - + The base game is not installed in the NAND and cannot be removed. 기본 게임은 NAND에 설치되어 있지 않으며 제거 할 수 없습니다. - + Successfully removed the installed update. 설치된 업데이트를 성공적으로 제거했습니다. - + There is no update installed for this title. 이 타이틀에 대해 설치된 업데이트가 없습니다. - + There are no DLC installed for this title. 이 타이틀에 설치된 DLC가 없습니다. - + Successfully removed %1 installed DLC. 설치된 %1 DLC를 성공적으로 제거했습니다. - + Delete OpenGL Transferable Shader Cache? OpenGL 전송 가능한 셰이더 캐시를 삭제하시겠습니까? - + Delete Vulkan Transferable Shader Cache? Vulkan 전송 가능한 셰이더 캐시를 삭제하시겠습니까? - + Delete All Transferable Shader Caches? 모든 전송 가능한 셰이더 캐시를 삭제하시겠습니까? - + Remove Custom Game Configuration? 사용자 지정 게임 구성을 제거 하시겠습니까? - + Remove File 파일 제거 - - + + Error Removing Transferable Shader Cache 전송 가능한 셰이더 캐시 제거 오류 - - + + A shader cache for this title does not exist. 이 타이틀에 대한 셰이더 캐시가 존재하지 않습니다. - + Successfully removed the transferable shader cache. 전송 가능한 셰이더 캐시를 성공적으로 제거했습니다. - + Failed to remove the transferable shader cache. 전송 가능한 셰이더 캐시를 제거하지 못했습니다. - - + + Error Removing Transferable Shader Caches 전송 가능한 셰이더 캐시 제거 오류 - + Successfully removed the transferable shader caches. 전송 가능한 셰이더 캐시를 성공적으로 제거했습니다. - + Failed to remove the transferable shader cache directory. 전송 가능한 셰이더 캐시 디렉토리를 제거하지 못했습니다. - - + + Error Removing Custom Configuration 사용자 지정 구성 제거 오류 - + A custom configuration for this title does not exist. 이 타이틀에 대한 사용자 지정 구성이 존재하지 않습니다. - + Successfully removed the custom game configuration. 사용자 지정 게임 구성을 성공적으로 제거했습니다. - + Failed to remove the custom game configuration. 사용자 지정 게임 구성을 제거하지 못했습니다. - - + + RomFS Extraction Failed! RomFS 추출 실패! - + There was an error copying the RomFS files or the user cancelled the operation. RomFS 파일을 복사하는 중에 오류가 발생했거나 사용자가 작업을 취소했습니다. - + Full 전체 - + Skeleton 뼈대 - + Select RomFS Dump Mode RomFS 덤프 모드 선택 - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. RomFS 덤프 방법을 선택하십시오.<br>전체는 모든 파일을 새 디렉토리에 복사하고<br>뼈대는 디렉토리 구조 만 생성합니다. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1에 RomFS를 추출하기에 충분한 여유 공간이 없습니다. 공간을 확보하거나 에뮬레이견 > 설정 > 시스템 > 파일시스템 > 덤프 경로에서 다른 덤프 디렉토리를 선택하십시오. - + Extracting RomFS... RomFS 추출 중... - - + + Cancel 취소 - + RomFS Extraction Succeeded! RomFS 추출이 성공했습니다! - + The operation completed successfully. 작업이 성공적으로 완료되었습니다. - + Error Opening %1 %1 열기 오류 - + Select Directory 경로 선택 - + Properties 속성 - + The game properties could not be loaded. 게임 속성을 로드 할 수 없습니다. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch 실행파일 (%1);;모든 파일 (*.*) - + Load File 파일 로드 - + Open Extracted ROM Directory 추출된 ROM 디렉토리 열기 - + Invalid Directory Selected 잘못된 디렉토리 선택 - + The directory you have selected does not contain a 'main' file. 선택한 디렉토리에 'main'파일이 없습니다. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) 설치 가능한 Switch 파일 (*.nca *.nsp *.xci);;Nintendo 컨텐츠 아카이브 (*.nca);;Nintendo 서브미션 패키지 (*.nsp);;NX 카트리지 이미지 (*.xci) - + Install Files 파일 설치 - + %n file(s) remaining %n개의 파일이 남음 - + Installing file "%1"... 파일 "%1" 설치 중... - - + + Install Results 설치 결과 - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. 충돌을 피하기 위해, 낸드에 베이스 게임을 설치하는 것을 권장하지 않습니다. 이 기능은 업데이트나 DLC를 설치할 때에만 사용해주세요. - + %n file(s) were newly installed %n개의 파일이 새로 설치되었습니다. - + %n file(s) were overwritten %n개의 파일을 덮어썼습니다. - + %n file(s) failed to install %n개의 파일을 설치하지 못했습니다. - + System Application 시스템 애플리케이션 - + System Archive 시스템 아카이브 - + System Application Update 시스템 애플리케이션 업데이트 - + Firmware Package (Type A) 펌웨어 패키지 (A타입) - + Firmware Package (Type B) 펌웨어 패키지 (B타입) - + Game 게임 - + Game Update 게임 업데이트 - + Game DLC 게임 DLC - + Delta Title 델타 타이틀 - + Select NCA Install Type... NCA 설치 유형 선택... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) 이 NCA를 설치할 타이틀 유형을 선택하세요: (대부분의 경우 기본값인 '게임'이 괜찮습니다.) - + Failed to Install 설치 실패 - + The title type you selected for the NCA is invalid. NCA 타이틀 유형이 유효하지 않습니다. - + File not found 파일을 찾을 수 없음 - + File "%1" not found 파일 "%1"을 찾을 수 없습니다 - + OK OK - + + Hardware requirements not met + 하드웨어 요구 사항이 충족되지 않음 + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + 시스템이 권장 하드웨어 요구 사항을 충족하지 않습니다. 호환성 보고가 비활성화되었습니다. + + + Missing yuzu Account yuzu 계정 누락 - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. 게임 호환성 테스트 결과를 제출하려면 yuzu 계정을 연결해야합니다.<br><br/>yuzu 계정을 연결하려면 에뮬레이션 &gt; 설정 &gt; 웹으로 가세요. - + Error opening URL URL 열기 오류 - + Unable to open the URL "%1". URL "%1"을 열 수 없습니다. - + TAS Recording TAS 레코딩 - + Overwrite file of player 1? 플레이어 1의 파일을 덮어쓰시겠습니까? - + Invalid config detected 유효하지 않은 설정 감지 - + Handheld controller can't be used on docked mode. Pro controller will be selected. 휴대 모드용 컨트롤러는 거치 모드에서 사용할 수 없습니다. 프로 컨트롤러로 대신 선택됩니다. - - - Error - 오류 - - - - - The current game is not looking for amiibos - 현재 게임은 amiibo를 찾고 있지 않습니다 - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed 현재 amiibo가 제거되었습니다. - + + Error + 오류 + + + + + The current game is not looking for amiibos + 현재 게임은 amiibo를 찾고 있지 않습니다 + + + Amiibo File (%1);; All Files (*.*) Amiibo 파일 (%1);; 모든 파일 (*.*) - + Load Amiibo Amiibo 로드 - - Error opening Amiibo data file - Amiibo 데이터 파일 열기 오류 - - - - Unable to open Amiibo file "%1" for reading. - Amiibo 파일 "%1"을(를) 읽을 수 없습니다. - - - - Error reading Amiibo data file - Amiibo 데이터 파일 읽기 오류 - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Amiibo 데이터를 완전히 읽을 수 없습니다. %1 바이트를 읽으려고 했지만 %2 바이트만 읽을 수 있었습니다. - - - + Error loading Amiibo data Amiibo 데이터 로드 오류 - - Unable to load Amiibo data. - Amiibo 데이터를 로드할 수 없습니다. + + The selected file is not a valid amiibo + 선택한 파일은 유효한 amiibo가 아닙니다 - + + The selected file is already on use + 선택한 파일은 이미 사용 중입니다 + + + + An unknown error occurred + 알수없는 오류가 발생했습니다 + + + Capture Screenshot 스크린샷 캡처 - + PNG Image (*.png) PNG 이미지 (*.png) - + TAS state: Running %1/%2 TAS 상태: %1/%2 실행 중 - + TAS state: Recording %1 TAS 상태: 레코딩 %1 - + TAS state: Idle %1/%2 TAS 상태: 유휴 %1/%2 - + TAS State: Invalid TAS 상태: 유효하지 않음 - + &Stop Running 실행 중지(&S) - + &Start 시작(&S) - + Stop R&ecording 레코딩 중지(&e) - + R&ecord 레코드(&R) - + Building: %n shader(s) 빌드중: %n개 셰이더 - + Scale: %1x %1 is the resolution scaling factor 스케일: %1x - + Speed: %1% / %2% 속도: %1% / %2% - + Speed: %1% 속도: %1% - + Game: %1 FPS (Unlocked) 게임: %1 FPS (제한없음) - + Game: %1 FPS 게임: %1 FPS - + Frame: %1 ms 프레임: %1 ms - + GPU NORMAL GPU 보통 - + GPU HIGH GPU 높음 - + GPU EXTREME GPU 굉장함 - + GPU ERROR GPU 오류 - + DOCKED 거치 모드 - + HANDHELD 휴대 모드 - + NEAREST NEAREST - - + + BILINEAR BILINEAR - + BICUBIC BICUBIC - + GAUSSIAN GAUSSIAN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA AA 없음 - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. 해당 게임은 플레이하기 전에 Switch 기기에서 추가 파일을 덤프해야합니다.<br/><br/>이러한 파일 덤프에 대한 자세한 내용은 다음 위키 페이지를 참조하십시오: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Switch 콘솔에서 시스템 아카이브 및 공유 글꼴 덤프</a>.<br/><br/>게임 목록으로 돌아가시겠습니까? 이를 무시하고 에뮬레이션을 계속하면 충돌, 저장 데이터 손상 또는 기타 버그가 발생할 수 있습니다. - + yuzu was unable to locate a Switch system archive. %1 yuzu가 Switch 시스템 아카이브를 찾을 수 없습니다. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu가 Switch 시스템 아카이브를 찾을 수 없습니다: %1. %2 - + System Archive Not Found 시스템 아카이브를 찾을 수 없음 - + System Archive Missing 시스템 아카이브 누락 - + yuzu was unable to locate the Switch shared fonts. %1 yuzu가 Switch 공유 글꼴을 찾을 수 없습니다. %1 - + Shared Fonts Not Found 공유 글꼴을 찾을 수 없음 - + Shared Font Missing 공유 글꼴 누락 - + Fatal Error 치명적인 오류 - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. 치명적인 오류가 발생했습니다. 자세한 내용은 로그를 확인하십시오. 로그 액세스에 대한 자세한 내용은 다음 페이지를 참조하십시오: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>로그 파일을 업로드하는 방법</a>.<br/><br/>게임 목록으로 돌아가시겠습니까? 이를 무시하고 에뮬레이션을 계속하면 충돌, 저장 데이터 손상 또는 기타 버그가 발생할 수 있습니다. - + Fatal Error encountered 치명적인 오류 발생 - + Confirm Key Rederivation 키 재생성 확인 - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5093,37 +5264,37 @@ This will delete your autogenerated key files and re-run the key derivation modu 자동 생성되었던 키 파일들이 삭제되고 키 생성 모듈이 다시 실행됩니다. - + Missing fuses fuses 누락 - + - Missing BOOT0 - BOOT0 누락 - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main 누락 - + - Missing PRODINFO - PRODINFO 누락 - + Derivation Components Missing 파생 구성 요소 누락 - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> 암호화 키가 없습니다. <br>모든 키, 펌웨어 및 게임을 얻으려면 <a href='https://yuzu-emu.org/help/quickstart/'>yuzu 빠른 시작 가이드</a>를 따르세요.<br><br> <small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5132,39 +5303,39 @@ on your system's performance. 소요될 수 있습니다. - + Deriving Keys 파생 키 - + Select RomFS Dump Target RomFS 덤프 대상 선택 - + Please select which RomFS you would like to dump. 덤프할 RomFS를 선택하십시오. - + Are you sure you want to close yuzu? yuzu를 닫으시겠습니까? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. 에뮬레이션을 중지하시겠습니까? 모든 저장되지 않은 진행 상황은 사라집니다. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5176,38 +5347,38 @@ Would you like to bypass this and exit anyway? GRenderWindow - + OpenGL not available! OpenGL을 사용할 수 없습니다! - + yuzu has not been compiled with OpenGL support. yuzu는 OpenGL 지원으로 컴파일되지 않았습니다. - - + + Error while initializing OpenGL! OpenGL을 초기화하는 동안 오류가 발생했습니다! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. 사용하시는 GPU가 OpenGL을 지원하지 않거나, 최신 그래픽 드라이버가 설치되어 있지 않습니다. - + Error while initializing OpenGL 4.6! OpenGL 4.6 초기화 중 오류 발생! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 사용하시는 GPU가 OpenGL 4.6을 지원하지 않거나 최신 그래픽 드라이버가 설치되어 있지 않습니다. <br><br>GL 렌더링 장치:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 사용하시는 GPU가 1개 이상의 OpenGL 확장 기능을 지원하지 않습니다. 최신 그래픽 드라이버가 설치되어 있는지 확인하세요. <br><br>GL 렌더링 장치:<br>%1<br><br>지원하지 않는 확장 기능:<br>%2 @@ -5215,153 +5386,153 @@ Would you like to bypass this and exit anyway? GameList - + Favorite 선호하는 게임 - + Start Game 게임 시작 - + Start Game without Custom Configuration 맞춤 설정 없이 게임 시작 - + Open Save Data Location 세이브 데이터 경로 열기 - + Open Mod Data Location MOD 데이터 경로 열기 - + Open Transferable Pipeline Cache 전송 가능한 파이프라인 캐시 열기 - + Remove 제거 - + Remove Installed Update 설치된 업데이트 삭제 - + Remove All Installed DLC 설치된 모든 DLC 삭제 - + Remove Custom Configuration 사용자 지정 구성 제거 - + Remove OpenGL Pipeline Cache OpenGL 파이프라인 캐시 제거 - + Remove Vulkan Pipeline Cache Vulkan 파이프라인 캐시 제거 - + Remove All Pipeline Caches 모든 파이프라인 캐시 제거 - + Remove All Installed Contents 설치된 모든 컨텐츠 제거 - + Dump RomFS RomFS를 덤프 - + Dump RomFS to SDMC RomFS를 SDMC로 덤프 - + Copy Title ID to Clipboard 클립보드에 타이틀 ID 복사 - + Navigate to GameDB entry GameDB 항목으로 이동 - + Properties 속성 - + Scan Subfolders 하위 폴더 스캔 - + Remove Game Directory 게임 디렉토리 제거 - + ▲ Move Up ▲ 위로 이동 - + ▼ Move Down ▼ 아래로 이동 - + Open Directory Location 디렉토리 위치 열기 - + Clear 초기화 - + Name 이름 - + Compatibility 호환성 - + Add-ons 부가 기능 - + File type 파일 형식 - + Size 크기 @@ -5370,81 +5541,61 @@ Would you like to bypass this and exit anyway? GameListItemCompat + Ingame + 게임 내 + + + + Game starts, but crashes or major glitches prevent it from being completed. + 게임이 시작되지만, 충돌이나 주요 결함으로 인해 게임이 완료되지 않습니다. + + + Perfect 완벽함 - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - 그래픽이나 오디오 문제 없이 게임의 모든 기능이 정상 작동합니다. -별도의 해결책이 필요하지 않습니다. - - - - Great - 좋음 - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - 그래픽이나 오디오 문제가 약간 있지만 게임을 끝까지 클리어할 수 있습니다. -별도의 해결책이 필요할 수 있습니다. - - Okay - 괜찮음 - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - 그래픽이나 오디오 문제가 다소 있지만 게임을 끝까지 클리어할 수는 있습니다. -별도의 해결책이 필요합니다. + Game can be played without issues. + 문제 없이 게임 플레이가 가능합니다. - Bad - 나쁨 + Playable + 재생 가능 - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - 게임은 작동하지만 그래픽이나 오디오 문제가 많으며 특정 구간에서는 완전히 깨져서 진행이 불가합니다. -별도의 해결책을 써도 마찬가지입니다. + Game functions with minor graphical or audio glitches and is playable from start to finish. + 약간의 그래픽 또는 오디오 결함이 있는 게임 기능이 있으며 처음부터 끝까지 플레이할 수 있습니다. - + Intro/Menu 인트로/메뉴 - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - 그래픽이나 오디오 문제가 심각해서 게임 플레이 자체가 불가하거나 시작 화면에서 -넘어가지 않습니다. + + Game loads, but is unable to progress past the Start Screen. + 게임이 로드되지만 시작 화면을 지나서 진행할 수 없습니다. - + Won't Boot 실행 불가 - + The game crashes when attempting to startup. 게임 실행 시 크래시가 일어납니다. - + Not Tested 테스트되지 않음 - + The game has not yet been tested. 이 게임은 아직 테스트되지 않았습니다. @@ -5452,7 +5603,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list 더블 클릭하여 게임 목록에 새 폴더 추가 @@ -5465,12 +5616,12 @@ Screen. %1 중의 %n 결과 - + Filter: 필터: - + Enter pattern to filter 검색 필터 입력 @@ -5480,22 +5631,22 @@ Screen. Create Room - + 방 만들기 Room Name - + 방 이름 Preferred Game - + 선호하는 게임 Max Players - + 최대 플레이어 @@ -5505,66 +5656,68 @@ Screen. (Leave blank for open game) - + (열린 게임은 공백으로 둠) Password - + 비밀번호 Port - + 포트 Room Description - + 방 설명 Load Previous Ban List - + 이전 차단 목록 불러오기 Public - + 공개 Unlisted - + 비공개 Host Room - + 호스트 방 HostRoomWindow - + Error 오류 - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - + 공개 로비에 방을 알리지 못했습니다. 방을 공개적으로 호스트하려면 에뮬레이션 -> 구성 -> 웹에서 유효한 yuzu 계정이 구성되어 있어야 합니다. 공개 로비에 방을 게시하지 않으려면 대신 목록에 없음을 선택하세요. +디버그 메시지: Hotkeys - + Audio Mute/Unmute 오디오 음소거/음소거 해제 + @@ -5586,112 +5739,111 @@ Debug Message: - Main Window 메인 윈도우 - + Audio Volume Down 오디오 볼륨 낮추기 - + Audio Volume Up 오디오 볼륨 키우기 - + Capture Screenshot 스크린샷 캡처 - + Change Adapting Filter 적응형 필터 변경 - + Change Docked Mode 독 모드 변경 - + Change GPU Accuracy GPU 정확성 변경 - + Continue/Pause Emulation 재개/에뮬레이션 일시중지 - + Exit Fullscreen 전체화면 종료 - + Exit yuzu yuzu 종료 - + Fullscreen 전체화면 - + Load File 파일 로드 - + Load/Remove Amiibo Amiibo 로드/제거 - + Restart Emulation 에뮬레이션 재시작 - + Stop Emulation 에뮬레이션 중단 - + TAS Record TAS 기록 - + TAS Reset TAS 리셋 - + TAS Start/Stop TAS 시작/멈춤 - + Toggle Filter Bar 상태 표시줄 전환 - + Toggle Framerate Limit 프레임속도 제한 토글 - + Toggle Mouse Panning 마우스 패닝 활성화 - + Toggle Status Bar 상태 표시줄 전환 @@ -5772,78 +5924,78 @@ Debug Message: Public Room Browser - + 공개 방 찾아보기 Nickname - + 별명 Filters - + 필터 Search - + 검색 Games I Own - + 내가 소유한 게임 Hide Full Rooms - + 전체 방 숨기기 Refresh Lobby - + 로비 새로 고침 - + Password Required to Join - + 입장시 비밀번호가 필요합니다 - + Password: - + 비밀번호: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players - + 플레이어 - + + Room Name + 방 이름 + + + + Preferred Game + 선호하는 게임 + + + + Host + 호스트 + + + Refreshing - + 새로 고치는 중 - + Refresh List - + 새로 고침 목록 @@ -5864,232 +6016,237 @@ Debug Message: 최근 파일(&R) - + &Emulation 에뮬레이션(&E) - + &View 보기(&V) - + &Reset Window Size 창 크기 초기화 (&R) - + &Debugging 디버깅(&D) - + Reset Window Size to &720p 창 크기를 720p로 맞추기(&7) - + Reset Window Size to 720p 창 크기를 720p로 맞추기 - + Reset Window Size to &900p 창 크기를 900p로 맞추기(&9) - + Reset Window Size to 900p 창 크기를 900p로 맞추기 - + Reset Window Size to &1080p 창 크기를 1080p로 맞추기(&1) - + Reset Window Size to 1080p 창 크기를 1080p로 맞추기 - + + &Multiplayer + 멀티플레이어(&M) + + + &Tools 도구(&T) - + &TAS TAS(&T) - + &Help 도움말(&H) - + &Install Files to NAND... 낸드에 파일 설치(&I) - + L&oad File... 파일 불러오기...(&L) - + Load &Folder... 폴더 불러오기...(&F) - + E&xit 종료(&X) - + &Pause 일시중지(&P) - + &Stop 정지(&S) - + &Reinitialize keys... 키 재설정...(&R) - + &About yuzu yuzu 정보(&A) - + Single &Window Mode 싱글 창 모드(&W) - + Con&figure... 설정(&f) - + Display D&ock Widget Headers 독 위젯 헤더 표시(&o) - + Show &Filter Bar 필터링 바 표시(&F) - + Show &Status Bar 상태 표시줄 보이기(&S) - + Show Status Bar 상태 표시줄 보이기 - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room - + &Browse Public Game Lobby + 공개 게임 로비 찾아보기(&B) - - Direct Connect to Room - + + &Create Room + 방 만들기(&C) - - Show Current Room - + + &Leave Room + 방에서 나가기(&L) + &Direct Connect to Room + 방에 직접 연결(&D) + + + + &Show Current Room + 현재 방 표시(&S) + + + F&ullscreen 전체 화면(&u) - + &Restart 재시작(&R) - + Load/Remove &Amiibo... Amiibo 로드/제거(&A)... - + &Report Compatibility 호환성 보고(&R) - + Open &Mods Page 게임 모드 페이지 열기(&M) - + Open &Quickstart Guide 빠른 시작 가이드 열기(&Q) - + &FAQ FAQ(&F) - + Open &yuzu Folder yuzu 폴더 열기(&y) - + &Capture Screenshot 스크린샷 찍기(&C) - + &Configure TAS... TAS설정...(&C) - + Configure C&urrent Game... 실행중인 게임 맞춤 설정...(&u) - + &Start 시작(&S) - + &Reset 리셋(&R) - + R&ecord 레코드(&e) @@ -6107,92 +6264,88 @@ Debug Message: Moderation - + 조정 Ban List - + 차단 목록 Refreshing - + 새로 고치는 중 Unban - + 차단 해재 Subject - + 주제 Type - + 유형 Forum Username - + 포럼 사용자이름 IP Address - + IP 주소 Refresh - + 새로 고침 MultiplayerState - - + Current connection status - + 현재 연결 상태 - - + Not Connected. Click here to find a room! - + 연결되지 않았습니다. 방을 찾으려면 여기를 클릭하세요! - - + Not Connected + 연결되지 않음 + + + Connected 연결됨 - - - Not Connected - + + New Messages Received + 수신된 새 메시지 - + Error 오류 - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - - - New Messages Received - + 방 정보를 업데이트하지 못했습니다. 인터넷 연결을 확인하고 방을 다시 호스팅해 보세요. +디버그 메시지: @@ -6200,124 +6353,144 @@ Debug Message: Username is not valid. Must be 4 to 20 alphanumeric characters. - + 사용자 이름이 유효하지 않습니다. 4~20자의 영숫자여야 합니다. Room name is not valid. Must be 4 to 20 alphanumeric characters. - + 방 이름이 유효하지 않습니다. 4~20자의 영숫자여야 합니다. Username is already in use or not valid. Please choose another. - + 사용자 이름은 이미 사용 중이거나 유효하지 않습니다. 다른 것을 선택하세요. IP is not a valid IPv4 address. - + IP는 유효한 IPv4 주소가 아닙니다. Port must be a number between 0 to 65535. - + 포트는 0에서 65535 사이의 숫자여야 합니다. You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list. - + 방을 호스팅하려면 선호하는 게임을 선택해야 합니다. 아직 게임 목록에 게임이 없으면 게임 목록에서 더하기 아이콘을 클릭하여 게임 폴더를 추가하세요. Unable to find an internet connection. Check your internet settings. - + 인터넷 연결을 찾을 수 없습니다. 인터넷 설정을 확인하세요. Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded. - + 호스트에 연결할 수 없습니다. 연결 설정이 올바른지 확인하세요. 여전히 연결할 수 없으면 방 호스트에게 연락하여 호스트가 전달된 외부 포트로 올바르게 구성되었는지 확인하세요. Unable to connect to the room because it is already full. - + 이미 꽉 찼기 때문에 방에 연결할 수 없습니다. Creating a room failed. Please retry. Restarting yuzu might be necessary. - + 방을 만들지 못했습니다. 다시 시도하세요. yuzu를 다시 시작해야 할 수도 있습니다. The host of the room has banned you. Speak with the host to unban you or try a different room. - + 방 호스트가 당신을 차단했습니다. 호스트와 대화하여 차단을 해제하거나 다른 방을 사용해 보세요. Version mismatch! Please update to the latest version of yuzu. If the problem persists, contact the room host and ask them to update the server. - + 버전이 불일치합니다! 최신 버전의 yuzu로 업데이트하세요. 문제가 지속되면 방 주인에게 연락하여 서버 업데이트를 요청하세요. Incorrect password. - + 잘못된 비밀번호입니다. An unknown error occurred. If this error continues to occur, please open an issue - + 알 수없는 오류가 발생했습니다. 이 오류가 계속 발생하면 문제를 알려주세요. Connection to room lost. Try to reconnect. - + 방과의 연결이 끊어졌습니다. 다시 연결해 보세요. You have been kicked by the room host. - + 방 호스트에게 추방당했습니다. IP address is already in use. Please choose another. - + IP 주소는 이미 사용 중입니다. 다른 것을 선택하세요. You do not have enough permission to perform this action. - + 이 작업을 수행할 수 있는 권한이 없습니다. The user you are trying to kick/ban could not be found. They may have left the room. - + 추방/금지하려는 사용자를 찾을 수 없습니다. +방을 나갔을 수 있습니다. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + 유효한 네트워크 인터페이스가 선택되지 않았습니다. +구성 -> 시스템 -> 네트워크로 이동하여 인터페이스를 선택하세요. + + + + Game already running + 게임이 이미 실행 중입니다 + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + 게임이 이미 실행 중일 때 방에 참여하는 것은 권장되지 않으며 방 기능이 제대로 작동하지 않을 수 있습니다. +그래도 진행하시겠습니까? + + + Leave Room - + 방 나가기 - + You are about to close the room. Any network connections will be closed. - + 방을 닫으려고 합니다. 모든 네트워크 연결이 닫힙니다. - + Disconnect - + 연결 해제 - + You are about to leave the room. Any network connections will be closed. - + 방을 떠나려고 합니다. 모든 네트워크 연결이 닫힙니다. NetworkMessage::ErrorManager - + Error 오류 @@ -6366,42 +6539,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1은(는) 게임을 하고 있지 않습니다 - + %1 is playing %2 - + %1이(가) %2을(를) 플레이 중입니다 - + Not playing a game - + 게임을 하지 않음 - + Installed SD Titles 설치된 SD 타이틀 - + Installed NAND Titles 설치된 NAND 타이틀 - + System Titles 시스템 타이틀 - + Add New Game Directory 새 게임 디렉토리 추가 - + Favorites 선호하는 게임 @@ -6431,7 +6604,7 @@ p, li { white-space: pre-wrap; } - + [not set] [설정 안 됨] @@ -6446,10 +6619,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 축 %1%2 @@ -6463,9 +6636,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [알 수 없음] @@ -6630,15 +6803,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [유효하지않음] - - + + %1%2Hat %3 %1%2방향키 %3 @@ -6646,35 +6819,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Axis %3 - + %1%2Axis %3,%4,%5 %1%2Axis %3,%4,%5 - + %1%2Motion %3 %1%2모션 %3 - - + + %1%2Button %3 %1%2버튼 %3 - + [unused] [미사용] @@ -6715,11 +6888,124 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + Amiibo 설정 + + + + Amiibo Info + Amiibo 정보 + + + + Series + 시리즈 + + + + Type + 유형 + + + + Name + 이름 + + + + Amiibo Data + Amiibo 데이터 + + + + Custom Name + 커스텀 이름 + + + + Owner + 소유자 + + + + Creation Date + 생성 날짜 + + + + dd/MM/yyyy + 일일/월월/년년년년 + + + + Modification Date + 수정 일자 + + + + dd/MM/yyyy + 일일/월월/년년년년 + + + + Game Data + 게임 데이터 + + + + Game Id + 게임 Id + + + + Mount Amiibo + Amiibo 마운트 + + + + ... + ... + + + + File Path + 파일 경로 + + + + No game data present + 게임 데이터 없음 + + + + The following amiibo data will be formatted: + 다음 amiibo 데이터의 형식이 지정됩니다: + + + + The following game data will removed: + 다음 게임 데이터가 제거됩니다: + + + + Set nickname and owner: + 닉네임 및 소유자 설정: + + + + Do you wish to restore this amiibo? + 이 amiibo를 복원하겠습니까? + + QtControllerSelectorDialog @@ -6823,6 +7109,7 @@ p, li { white-space: pre-wrap; } + Handheld 휴대 모드 @@ -6862,11 +7149,6 @@ p, li { white-space: pre-wrap; } Docked 거치 모드 - - - Undocked - 휴대 모드 - Vibration diff --git a/dist/languages/nb.ts b/dist/languages/nb.ts index a5e68ce..19b4d2d 100644 --- a/dist/languages/nb.ts +++ b/dist/languages/nb.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -204,7 +204,7 @@ This would ban both their forum username and their IP address. - %1 (%2/%3 members) - connected + %1 - %2 (%3/%4 members) - connected @@ -218,6 +218,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Rapporter Spillkompabilitet @@ -227,92 +232,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Om du velger å sende inn et testtilfelle til </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">yuzu Kompatibilitetslisten</span></a><span style=" font-size:10pt;">, Vil den følgende informasjonen bli samlet og vist på siden:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Maskinvareinformasjon (CPU / GPU / Operativsystem)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hvilken versjon av yuzu du bruker</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Den tilkoblede yuzu kontoen</li></ul></body></html> - - Perfect - Perfekt + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Spiller fungerer helt perfekt med ingen lyd- eller grafikkproblemer.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Bra + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Spillet fungerer med små grafiske- eller lyd-problemer og er spillbart fra start til slutt. Kan trenge noen justeringer.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - OK + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Spillet fungerer med store grafiske- eller lyd-problemer, men er spillbart fra start til slutt med justeringer.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Dårlig + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Spillet fungerer, men med store grafiske- eller lyd-problemer. Umulig å gjøre framgang i spesifikke områder på grunn av problemer selv med justeringer.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Intro/Meny + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Spillet er helt umulig å spille på grunn av store grafiske- eller lydproblemer. Umulig å komme forbi startskjermen.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Vil ikke starte + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Spillet krasjer ved oppstart.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Uavhengig av hastighet og ytelse, hvor bra fungerer dette spillet fra start til slutt på denne versjonen av Yuzu? + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Tusen takk for ditt bidrag! - + Submitting Sender inn - + Communication error Kommunikasjonsfeil - + An error occurred while sending the Testcase En feil oppstod under sending av testtilfellet - + Next Neste @@ -410,7 +450,7 @@ This would ban both their forum username and their IP address. Gjenopprett Standardverdier - + Auto Auto @@ -746,200 +786,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub Aktiver GDB Stub - + Port: Port: - + Logging Loggføring - + Global Log Filter Global Loggfilter - + Show Log in Console Vis logg i konsollen - + Open Log Location Åpne Logg-Plassering - + When checked, the max size of the log increases from 100 MB to 1 GB Når dette er på øker maksstørrelsen til loggen fra 100 MB til 1 GB - + Enable Extended Logging** Slå på utvidet loggføring** - + Homebrew Homebrew - + Arguments String Argument streng - + Graphics Grafikk - + When checked, the graphics API enters a slower debugging mode Når dette er på går grafikk–API-et inn i en tregere feilsøkingsmodus - + Enable Graphics Debugging Slå på Grafikkfeilsøking - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - + Disable Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback Slå på shader-tilbakemelding - + When checked, it executes shaders without loop logic changes Når dette er på kjører shader-e uten endring i løkkelogikk - + Disable Loop safety checks - + Debugging Feilsøking - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avansert - + Kiosk (Quest) Mode - + Enable CPU Debugging Slå på prosessorfeilsøking - + Enable Debug Asserts - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet Slå av web-applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Dette blir automatisk tilbakestilt når yuzu lukkes. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1309,193 +1384,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings Grafikkinnstillinger - + Use disk pipeline cache - + Use asynchronous GPU emulation Bruk asynkron GPU-emulering - + Accelerate ASTC texture decoding Akselerer ASTC-teksturdekoding - + NVDEC emulation: NVDEC-emulering: - + No Video Output Ingen videoutdata - + CPU Video Decoding Prosessorvideodekoding - + GPU Video Decoding (Default) GPU-videodekoding (standard) - + Fullscreen Mode: Fullskjermmodus: - + Borderless Windowed Rammeløst vindu - + Exclusive Fullscreen Eksklusiv fullskjerm - + Aspect Ratio: Størrelsesforhold: - + Default (16:9) Standard (16:9) - + Force 4:3 Tving 4:3 - + Force 21:9 Tving 21:9 - + + Force 16:10 + + + + Stretch to Window Strekk til Vindu - + Resolution: Oppløsning: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EKSPERIMENTELL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EKSPERIMENTELL] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: - + Nearest Neighbor Nærmeste nabo - + Bilinear Bilineær - + Bicubic Bikubisk - + Gaussian Gaussisk - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (kun med Vulkan) - + Anti-Aliasing Method: Anti-aliasing–metode: - + None Ingen - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Bruk global bakgrunnsfarge - + Set background color: Velg bakgrunnsfarge: - + Background Color: Bakgrunnsfarge: @@ -1504,6 +1604,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM (assembly-shader-e, kun med NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1558,37 +1664,47 @@ This would ban both their forum username and their IP address. - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotropisk filtrering: - + Automatic Automatisk - + Default Standard - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2080,7 +2196,7 @@ This would ban both their forum username and their IP address. - + Left Stick Venstre Pinne @@ -2174,14 +2290,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2200,7 +2316,7 @@ This would ban both their forum username and their IP address. - + Plus Pluss @@ -2213,15 +2329,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2278,231 +2394,236 @@ This would ban both their forum username and their IP address. - + Right Stick Høyre Pinne - - - - + + + + Clear Fjern - - - - - + + + + + [not set] [ikke satt] - - - Toggle button - Veksle knapp - - - - + + Invert button Inverter knapp - - + + + Toggle button + Veksle knapp + + + + Invert axis Inverter akse - - - + + + Set threshold Set grense - - + + Choose a value between 0% and 100% Velg en verdi mellom 0% og 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Etter du har trykker på OK, flytt først stikken horisontalt, og så vertikalt. For å invertere aksene, flytt først stikken vertikalt, og så horistonalt. - + Center axis Senterakse - - + + Deadzone: %1% Dødsone: %1% - - + + Modifier Range: %1% Modifikatorområde: %1% - - + + Pro Controller Pro-Kontroller - + Dual Joycons Doble Joycons - + Left Joycon Venstre Joycon - + Right Joycon Høyre Joycon - + Handheld Håndholdt - + GameCube Controller GameCube-kontroller - + Poke Ball Plus Poke Ball Plus - + NES Controller NES-kontroller - + SNES Controller SNES-kontroller - + N64 Controller N64-kontroller - + Sega Genesis Sega Genesis - + Start / Pause Start / paus - + Z Z - + Control Stick Kontrollstikke - + C-Stick C-stikke - + Shake! Rist! - + [waiting] [venter] - + New Profile Ny Profil - + Enter a profile name: Skriv inn et profilnavn: - - + + Create Input Profile Lag inndataprofil - + The given profile name is not valid! Det oppgitte profilenavnet er ugyldig! - + Failed to create the input profile "%1" Klarte ikke lage inndataprofil "%1" - + Delete Input Profile Slett inndataprofil - + Failed to delete the input profile "%1" Klarte ikke slette inndataprofil "%1" - + Load Input Profile Last inn inndataprofil - + Failed to load the input profile "%1" Klarte ikke laste inn inndataprofil "%1" - + Save Input Profile Lagre inndataprofil - + Failed to save the input profile "%1" Klarte ikke lagre inndataprofil "%1" @@ -2757,42 +2878,42 @@ For å invertere aksene, flytt først stikken vertikalt, og så horistonalt.Utvikler - + Add-Ons Tillegg - + General Generelt - + System System - + CPU CPU - + Graphics Grafikk - + Adv. Graphics Avn. Grafikk - + Audio Lyd - + Properties Egenskaper @@ -2848,37 +2969,37 @@ For å invertere aksene, flytt først stikken vertikalt, og så horistonalt.Nåværende Bruker - + Username Brukernavn - + Set Image Sett Bilde - + Add Legg til - + Rename Gi nytt navn - + Remove Fjern - + Profile management is available only when game is not running. Profil-administrering er bare tilgjengelig når ingen spill kjører. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2886,96 +3007,105 @@ For å invertere aksene, flytt først stikken vertikalt, og så horistonalt. - + Enter Username Skriv inn Brukernavn - + Users Brukere - + Enter a username for the new user: Tast inn et brukernavn for den nye brukeren: - + Enter a new username: Skriv inn et nytt brukernavn - - Confirm Delete - Bekreft Sletting - - - - You are about to delete user with name "%1". Are you sure? - Du er i ferd med å slette brukeren med navnet "%1". Er du sikker? - - - + Select User Image Sett Bruker Bilde - + JPEG Images (*.jpg *.jpeg) JPEG Bilder (*.jpg *.jpeg) - + Error deleting image Feil ved sletting av bilde - + Error occurred attempting to overwrite previous image at: %1. En feil oppstod under overskrivelse av det forrige bildet på: %1. - + Error deleting file Feil ved sletting av fil - + Unable to delete existing file: %1. Kunne ikke slette eksisterende fil: %1. - + Error creating user image directory Feil under opprettelse av profilbildemappe - + Unable to create directory %1 for storing user images. Kunne ikke opprette mappe %1 for å lagre profilbilder. - + Error copying user image Feil under kopiering av profilbilde - + Unable to copy image from %1 to %2 Kunne ikke kopiere bilde fra %1 til %2 - + Error resizing user image Feil under endring av størrelse på brukerbilde - + Unable to resize image Klarte ikke endre bildestørrelse + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Bekreft Sletting + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3504,47 +3634,47 @@ For å invertere aksene, flytt først stikken vertikalt, og så horistonalt.<html><head/><body><p>Leser kontrollerinndata fra script i samme format som TAS-nx–script.<br/>For en mer detaljert beskrivelse, se <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">hjelpesiden</span></a> på yuzus hjemmeside.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). For å sjekke hvilke hurtigtaster som styrer avspilling/innspilling, se hurtigtastinnstillingene (Konfigurer -> Generelt -> Hurtigtaster). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ADVARSEL: Dette er eksperimentell funksjonalitet.<br/>Script spilles ikke tilbake med perfekt bildetiming med den nåværende, ikke-perfekte synkemetoden. - + Settings Innstillinger - + Enable TAS features Slå på TAS-funksjonalitet - + Loop script - + Pause execution during loads Sett kjøring på vent under lasting - + Script Directory Skriptmappe - + Path Sti - + ... ... @@ -3796,56 +3926,71 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re + Show Compatibility List + + + + Show Add-Ons Column Vis tilleggskolonnen - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: Spillikonstørrelse: - + Folder Icon Size: Mappeikonstørrelse: - + Row 1 Text: Rad 1 Tekst: - + Row 2 Text: Rad 2 Tekst: - + Screenshots Skjermbilder - + Ask Where To Save Screenshots (Windows Only) Spør om hvor skjermbilder skal lagres (kun for Windows) - + Screenshots Path: Skjermbildebane: - + ... ... - + Select Screenshots Path... Velg Skermbildebane... - + <System> <System> @@ -3954,7 +4099,7 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re - + Verify Verifiser @@ -4041,7 +4186,7 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re - + Unspecified Uspesifisert @@ -4056,17 +4201,36 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Verifiserer... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verifisering feilet + + + Verification failed Verifisering feilet - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. @@ -4135,12 +4299,12 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re DirectConnectWindow - + Connecting - + Connect @@ -4148,486 +4312,489 @@ Dra punkter for å endre posisjon, eller dobbelttrykk på tabellfelter for å re GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonym data blir samlet inn</a>for å hjelpe til med å forbedre yuzu.<br/><br/>Vil du dele din bruksdata med oss? - + Telemetry Telemetri - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Laster web-applet... - - + + Disable Web Applet Slå av web-applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Antall shader-e som bygges for øyeblikket - + The current selected resolution scaling multiplier. Den valgte oppløsningsskaleringsfaktoren. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Nåværende emuleringshastighet. Verdier høyere eller lavere en 100% indikerer at emuleringen kjører raskere eller tregere enn en Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Hvor mange bilder per sekund spiller viser. Dette vil variere fra spill til spill og scene til scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tid det tar for å emulere et Switch bilde. Teller ikke med bildebegrensing eller v-sync. For full-hastighet emulering burde dette være 16.67 ms. på det høyeste. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files - + &Continue - + &Pause &Paus - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Et spill kjører i yuzu - + Warning Outdated Game Format Advarsel: Utdatert Spillformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Du bruker en dekonstruert ROM-mappe for dette spillet, som er et utdatert format som har blitt erstattet av andre formater som NCA, NAX, XCI, eller NSP. Dekonstruerte ROM-mapper mangler ikoner, metadata, og oppdateringsstøtte.<br><br>For en forklaring på diverse Switch-formater som yuzu støtter,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>sjekk vår wiki</a>. Denne meldingen vil ikke bli vist igjen. - - + + Error while loading ROM! Feil under innlasting av ROM! - + The ROM format is not supported. Dette ROM-formatet er ikke støttet. - + An error occurred initializing the video core. En feil oppstod under initialisering av videokjernen. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu har oppdaget en feil under kjøring av videokjernen. Dette er vanligvis forårsaket av utdaterte GPU-drivere, inkludert for integrert grafikk. Vennligst sjekk loggen for flere detaljer. For mer informasjon om å finne loggen, besøk følgende side: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Uploadd the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Feil under lasting av ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. En ukjent feil oppstod. Se loggen for flere detaljer. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Lagre Data - + Mod Data Mod Data - + Error Opening %1 Folder Feil Under Åpning av %1 Mappen - - + + Folder does not exist! Mappen eksisterer ikke! - + Error Opening Transferable Shader Cache - + Failed to create the shader cache directory for this title. - - Contents - Innhold + + Error Removing Contents + - - Update - Oppdatering + + Error Removing Update + - - DLC - DLC + + Error Removing DLC + - + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + Remove Entry Fjern oppføring - - Remove Installed Game %1? - Fjern Installert Spill %1? - - - - - - - - + + + + + + Successfully Removed Fjerning lykkes - + Successfully removed the installed base game. - - - - Error Removing %1 - Feil Under Fjerning av %1 - - - + The base game is not installed in the NAND and cannot be removed. Grunnspillet er ikke installert i NAND og kan ikke bli fjernet. - + Successfully removed the installed update. Fjernet vellykket den installerte oppdateringen. - + There is no update installed for this title. Det er ingen oppdatering installert for denne tittelen. - + There are no DLC installed for this title. Det er ingen DLC installert for denne tittelen. - + Successfully removed %1 installed DLC. Fjernet vellykket %1 installerte DLC-er. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? Fjern Tilpasset Spillkonfigurasjon? - + Remove File Fjern Fil - - + + Error Removing Transferable Shader Cache Feil under fjerning av overførbar shader cache - - + + A shader cache for this title does not exist. - + Successfully removed the transferable shader cache. Lykkes i å fjerne den overførbare shader cachen. - + Failed to remove the transferable shader cache. Feil under fjerning av den overførbare shader cachen. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration Feil Under Fjerning Av Tilpasset Konfigurasjon - + A custom configuration for this title does not exist. En tilpasset konfigurasjon for denne tittelen finnes ikke. - + Successfully removed the custom game configuration. Fjernet vellykket den tilpassede spillkonfigurasjonen. - + Failed to remove the custom game configuration. Feil under fjerning av den tilpassede spillkonfigurasjonen. - - + + RomFS Extraction Failed! Utvinning av RomFS Feilet! - + There was an error copying the RomFS files or the user cancelled the operation. Det oppstod en feil under kopiering av RomFS filene eller så kansellerte brukeren operasjonen. - + Full Fullstendig - + Skeleton Skjelett - + Select RomFS Dump Mode Velg RomFS Dump Modus - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Velg hvordan du vil dumpe RomFS.<br>Fullstendig vil kopiere alle filene til en ny mappe mens <br>skjelett vil bare skape mappestrukturen. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Utvinner RomFS... - - + + Cancel Avbryt - + RomFS Extraction Succeeded! RomFS Utpakking lyktes! - + The operation completed successfully. Operasjonen fullført vellykket. - + Error Opening %1 Feil ved åpning av %1 - + Select Directory Velg Mappe - + Properties Egenskaper - + The game properties could not be loaded. Spillets egenskaper kunne ikke bli lastet inn. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch Kjørbar Fil (%1);;Alle Filer (*.*) - + Load File Last inn Fil - + Open Extracted ROM Directory Åpne Utpakket ROM Mappe - + Invalid Directory Selected Ugyldig Mappe Valgt - + The directory you have selected does not contain a 'main' file. Mappen du valgte inneholder ikke en 'main' fil. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Installerbar Switch-Fil (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xcI) - + Install Files Installer Filer - + %n file(s) remaining %n fil gjenstår%n filer gjenstår - + Installing file "%1"... Installerer fil "%1"... - - + + Install Results Insallasjonsresultater - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed %n fil ble nylig installert @@ -4635,7 +4802,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) were overwritten %n fil ble overskrevet @@ -4643,7 +4810,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) failed to install %n fil ble ikke installert @@ -4651,411 +4818,410 @@ Please, only use this feature to install updates and DLC. - + System Application Systemapplikasjon - + System Archive Systemarkiv - + System Application Update Systemapplikasjonsoppdatering - + Firmware Package (Type A) Firmware Pakke (Type A) - + Firmware Package (Type B) Firmware-Pakke (Type B) - + Game Spill - + Game Update Spilloppdatering - + Game DLC Spill tilleggspakke - + Delta Title Delta Tittel - + Select NCA Install Type... Velg NCA Installasjonstype... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Vennligst velg typen tittel du vil installere denne NCA-en som: (I de fleste tilfellene, standarden 'Spill' fungerer.) - + Failed to Install Feil under Installasjon - + The title type you selected for the NCA is invalid. Titteltypen du valgte for NCA-en er ugyldig. - + File not found Fil ikke funnet - + File "%1" not found Filen "%1" ikke funnet - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Mangler yuzu Bruker - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. For å sende inn et testtilfelle for spillkompatibilitet, må du linke yuzu-brukeren din.<br><br/>For å linke yuzu-brukeren din, gå til Emulasjon &gt; Konfigurasjon &gt; Nett. - + Error opening URL Feil under åpning av URL - + Unable to open the URL "%1". Kunne ikke åpne URL "%1". - + TAS Recording TAS-innspilling - + Overwrite file of player 1? Overskriv filen til spiller 1? - + Invalid config detected Ugyldig konfigurasjon oppdaget - + Handheld controller can't be used on docked mode. Pro controller will be selected. Håndholdt kontroller kan ikke brukes i dokket modus. Pro-kontroller vil bli valgt. - - - Error - Feil - - - - - The current game is not looking for amiibos - Det kjørende spillet sjekker ikke for amiibo-er - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed Den valgte amiibo-en har blitt fjernet - + + Error + Feil + + + + + The current game is not looking for amiibos + Det kjørende spillet sjekker ikke for amiibo-er + + + Amiibo File (%1);; All Files (*.*) Amiibo-Fil (%1);; Alle Filer (*.*) - + Load Amiibo Last inn Amiibo - - Error opening Amiibo data file - Feil ved Åpning av Amiibo data fil - - - - Unable to open Amiibo file "%1" for reading. - Kunne ikke åpne Amiibo-fil "%1" for lesing. - - - - Error reading Amiibo data file - Feil under lesing av Amiibo datafil. - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Kunne ikke lese all Amiibo-data. Forventet å lese minst %1 bytes, men kunne bare lese %2 bytes. - - - + Error loading Amiibo data Feil ved lasting av Amiibo data - - Unable to load Amiibo data. - Kunne ikke laste Amiibo-data. + + The selected file is not a valid amiibo + - + + The selected file is already on use + + + + + An unknown error occurred + + + + Capture Screenshot Ta Skjermbilde - + PNG Image (*.png) PNG Bilde (*.png) - + TAS state: Running %1/%2 TAS-tilstand: Kjører %1/%2 - + TAS state: Recording %1 TAS-tilstand: Spiller inn %1 - + TAS state: Idle %1/%2 TAS-tilstand: Venter %1%2 - + TAS State: Invalid TAS-tilstand: Ugyldig - + &Stop Running &Stopp kjøring - + &Start &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) Bygger: %n shaderBygger: %n shader-e - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% Hastighet: %1% / %2% - + Speed: %1% Hastighet: %1% - + Game: %1 FPS (Unlocked) Spill: %1 FPS (ubegrenset) - + Game: %1 FPS Spill: %1 FPS - + Frame: %1 ms Ramme: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU HØY - + GPU EXTREME GPU EKSTREM - + GPU ERROR GPU FEIL - + DOCKED - + HANDHELD - + NEAREST NÆRMESTE - - + + BILINEAR BILINEÆR - + BICUBIC BIKUBISK - + GAUSSIAN GAUSSISK - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA INGEN AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Spillet du prøver å laste krever at ekstra filer fra din Switch blir dumpet før du spiller.<br/><br/>For mer informasjon om dumping av disse filene, vennligst se den følgende wiki-siden: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping av System-Arkiv og Shared Fonts fra en Switch-Konsoll</a>.<br/><br/>Vil du gå tilbake til spillisten? Fortsetting av emulasjon kan føre til krasjing, ødelagt lagringsdata, eller andre feil. - + yuzu was unable to locate a Switch system archive. %1 yuzu kunne ikke finne et Switch system-arkiv. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu kunne ikke finne et Switch system-arkiv: %1. %2 - + System Archive Not Found System Arkiv Ikke Funnet - + System Archive Missing System Arkiv Mangler - + yuzu was unable to locate the Switch shared fonts. %1 yuzu kunne ikke finne Switch shared fonts. %1 - + Shared Fonts Not Found Shared Fonts Ikke Funnet - + Shared Font Missing Shared Font Mangler - + Fatal Error Fatal Feil - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu har oppdaget en fatal feil, vennligst se loggen for flere detaljer. For mer informasjon om å finne loggen, vennligst se den følgende siden: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Hvordan å Laste Opp Log-Filen</a>.<br/><br/>Vil du gå tilbake til spillisten? Fortsetting av emulasjon kan føre til krasjing, ødelagt lagringsdata, eller andre feil. - + Fatal Error encountered Fatal Feil oppstått - + Confirm Key Rederivation Bekreft Nøkkel-Redirevasjon - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5072,37 +5238,37 @@ og eventuelt lag backups. Dette vil slette dine autogenererte nøkkel-filer og kjøre nøkkel-derivasjonsmodulen på nytt. - + Missing fuses Mangler fuses - + - Missing BOOT0 - Mangler BOOT0 - + - Missing BCPKG2-1-Normal-Main - Mangler BCPKG2-1-Normal-Main - + - Missing PRODINFO - Mangler PRODINFO - + Derivation Components Missing Derivasjonskomponenter Mangler - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Krypteringsnøkler mangler. <br>Vennligst følg <a href='https://yuzu-emu.org/help/quickstart/'>yuzus oppstartsguide</a> for å få alle nøklene, fastvaren og spillene dine.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5111,39 +5277,39 @@ Dette kan ta opp til et minutt avhengig av systemytelsen din. - + Deriving Keys Deriverer Nøkler - + Select RomFS Dump Target Velg RomFS Dump-Mål - + Please select which RomFS you would like to dump. Vennligst velg hvilken RomFS du vil dumpe. - + Are you sure you want to close yuzu? Er du sikker på at du vil lukke yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Er du sikker på at du vil stoppe emulasjonen? All ulagret fremgang vil bli tapt. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5155,38 +5321,38 @@ Vil du overstyre dette og lukke likevel? GRenderWindow - + OpenGL not available! OpenGL ikke tilgjengelig! - + yuzu has not been compiled with OpenGL support. yuzu har ikke blitt kompilert med OpenGL-støtte. - - + + Error while initializing OpenGL! Feil under initialisering av OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Det kan hende at GPU-en din ikke støtter OpenGL, eller at du ikke har den nyeste grafikkdriveren. - + Error while initializing OpenGL 4.6! Feil under initialisering av OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Det kan hende at GPU-en din ikke støtter OpenGL 4.6, eller at du ikke har den nyeste grafikkdriveren.<br><br>GL-renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Det kan hende at GPU-en din ikke støtter én eller flere nødvendige OpenGL-utvidelser. Vennligst sørg for at du har den nyeste grafikkdriveren.<br><br>GL-renderer: <br>%1<br><br>Ikke-støttede utvidelser:<br>%2 @@ -5194,153 +5360,153 @@ Vil du overstyre dette og lukke likevel? GameList - + Favorite Legg til som favoritt - + Start Game - + Start Game without Custom Configuration - + Open Save Data Location Åpne Lagret Data plassering - + Open Mod Data Location Åpne Mod Data plassering - + Open Transferable Pipeline Cache - + Remove Fjern - + Remove Installed Update Fjern Installert Oppdatering - + Remove All Installed DLC Fjern All Installert DLC - + Remove Custom Configuration Fjern Tilpasset Konfigurasjon - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents Fjern All Installert Innhold - + Dump RomFS Dump RomFS - + Dump RomFS to SDMC - + Copy Title ID to Clipboard Kopier Tittel-ID til Utklippstavle - + Navigate to GameDB entry Naviger til GameDB-oppføring - + Properties Egenskaper - + Scan Subfolders Skann Undermapper - + Remove Game Directory Fjern Spillmappe - + ▲ Move Up ▲ Flytt Opp - + ▼ Move Down ▼ Flytt Ned - + Open Directory Location Åpne Spillmappe - + Clear Fjern - + Name Navn - + Compatibility Kompatibilitet - + Add-ons Tilleggsprogrammer - + File type Fil Type - + Size Størrelse @@ -5349,76 +5515,61 @@ Vil du overstyre dette og lukke likevel? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Perfekt - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - - - - - Great - Bra - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - - - Okay - Ok - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. + Game can be played without issues. - Bad - Dårlig - - - - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. + Playable - + + Game functions with minor graphical or audio glitches and is playable from start to finish. + + + + Intro/Menu Intro/Meny - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Spillet er fullstendig uspillbart på grunn av store grafikk- eller lydfeil. Kan ikke komme fordi startskjermen. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Vil ikke starte - + The game crashes when attempting to startup. Spillet krasjer under oppstart. - + Not Tested Ikke testet - + The game has not yet been tested. Spillet har ikke blitt testet ennå. @@ -5426,7 +5577,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Dobbeltrykk for å legge til en ny mappe i spillisten @@ -5439,12 +5590,12 @@ Screen. %1 of %n resultat%1 of %n resultater - + Filter: Filter: - + Enter pattern to filter @@ -5520,12 +5671,12 @@ Screen. HostRoomWindow - + Error Feil - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5534,11 +5685,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5560,112 +5712,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Ta Skjermbilde - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Fullskjerm - + Load File Last inn Fil - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5780,42 +5931,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Spillere - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5838,232 +5989,237 @@ Debug Message: Nylige file&r - + &Emulation &Emulering - + &View &Vis - + &Reset Window Size Nullstill vindusstø&rrelse - + &Debugging - + Reset Window Size to &720p Tilbakestill vindusstørrelse til &720p - + Reset Window Size to 720p Tilbakestill vindusstørrelse til 720p - + Reset Window Size to &900p Tilbakestill vindusstørrelse til &900p - + Reset Window Size to 900p Tilbakestill vindusstørrelse til 900p - + Reset Window Size to &1080p Tilbakestill vindusstørrelse til &1080p - + Reset Window Size to 1080p Tilbakestill vindusstørrelse til 1080p - + + &Multiplayer + + + + &Tools Verk&tøy - + &TAS &TAS - + &Help &Hjelp - + &Install Files to NAND... &Installer filer til NAND... - + L&oad File... - + Load &Folder... - + E&xit &Avslutt - + &Pause &Paus - + &Stop &Stop - + &Reinitialize keys... &Reinitialiser nøkler... - + &About yuzu - + Single &Window Mode - + Con&figure... Kon&figurer... - + Display D&ock Widget Headers - + Show &Filter Bar Vis &filterlinje - + Show &Status Bar Vis &statuslinje - + Show Status Bar Vis statuslinje - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room + &Browse Public Game Lobby - - Direct Connect to Room + + &Create Room - - Show Current Room + + &Leave Room - F&ullscreen - F&ullskjerm + &Direct Connect to Room + - &Restart + &Show Current Room - Load/Remove &Amiibo... - + F&ullscreen + F&ullskjerm - &Report Compatibility + &Restart + Load/Remove &Amiibo... + + + + + &Report Compatibility + + + + Open &Mods Page - + Open &Quickstart Guide - + &FAQ - + Open &yuzu Folder - + &Capture Screenshot - + &Configure TAS... - + Configure C&urrent Game... - + &Start &Start - + &Reset - + R&ecord @@ -6128,46 +6284,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Tilkoblet - - - - Not Connected - + + Connected + Tilkoblet + + + + New Messages Received + + + + Error Feil - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6268,22 +6419,39 @@ They may have left the room. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6291,7 +6459,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Feil @@ -6340,42 +6508,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 is playing %2 - + Not playing a game - + Installed SD Titles Installerte SD-titler - + Installed NAND Titles Installerte NAND-titler - + System Titles System Titler - + Add New Game Directory Legg til ny spillmappe - + Favorites Favoritter @@ -6405,7 +6573,7 @@ p, li { white-space: pre-wrap; } - + [not set] [ikke satt] @@ -6420,10 +6588,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Akse %1%2 @@ -6437,9 +6605,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [ukjent] @@ -6604,15 +6772,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [ugyldig] - - + + %1%2Hat %3 @@ -6620,35 +6788,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Akse %3 - + %1%2Axis %3,%4,%5 %1%2Akse %3,%4,%5 - + %1%2Motion %3 %1%2Bevegelse %3 - - + + %1%2Button %3 %1%2Knapp %3 - + [unused] [ubrukt] @@ -6689,11 +6857,124 @@ p, li { white-space: pre-wrap; } Ekstra - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Navn + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6797,6 +7078,7 @@ p, li { white-space: pre-wrap; } + Handheld Håndholdt @@ -6836,11 +7118,6 @@ p, li { white-space: pre-wrap; } Docked Dokket - - - Undocked - Udokket - Vibration diff --git a/dist/languages/nl.ts b/dist/languages/nl.ts index e42dbb5..2465874 100644 --- a/dist/languages/nl.ts +++ b/dist/languages/nl.ts @@ -30,7 +30,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Broncode</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Bijdragers</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Licentie</span></a></p></body></html> @@ -81,86 +81,86 @@ p, li { white-space: pre-wrap; } Send Chat Message - + Stuur Chatbericht Send Message - + Stuur Bericht - + Members - + Leden - + %1 has joined - + %1 heeft deelgenomen - + %1 has left - + %1 is weggegaan - + %1 has been kicked - + %1 is verwijderd - + %1 has been banned - + %1 is verbannen - + %1 has been unbanned - + %1's ban is ongedaan gemaakt - + View Profile - + Profiel Bekijken - - + + Block Player - + Speler Blokkeren - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Verwijderen - + Ban - + Verwijderen - + Kick Player - + Speler verwijderen - + Are you sure you would like to <b>kick</b> %1? - + Weet je zeker dat je %1 wil <b>verwijderen</b>? - + Ban Player - + Speler Verbannen - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -177,12 +177,12 @@ This would ban both their forum username and their IP address. Room Description - + Kamer Beschrijving Moderation... - + Moderatie... @@ -204,7 +204,7 @@ This would ban both their forum username and their IP address. - %1 (%2/%3 members) - connected + %1 - %2 (%3/%4 members) - connected @@ -218,6 +218,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Rapporteer Game Compatibiliteit @@ -227,92 +232,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Als je kiest een test case op te sturen naar de </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">yuzu compatibiliteitslijst</span></a><span style=" font-size:10pt;">, zal de volgende informatie worden verzameld en getoond op de site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware Informatie (CPU / GPU / Besturingssysteem)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Welke versie van yuzu je draait</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Het verbonden yuzu account</li></ul></body></html> - - Perfect - Perfect + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>De game werkt foutloos, zonder geluid of grafische problemen.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Goed + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Game werkt met kleine grafische of geluid problemen en is speelbaar van start tot eind. Kan enkele tijdelijke oplossingen nodig hebben.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - OK + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Game werkt met grote grafische of geluid problemen, maar is speelbaar van start tot eind met tijdelijke oplossingen.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Slecht + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Game werkt, maar met grote grafische of geluid problemen. Specifieke gebieden kunnen niet worden uitgespeeld vanwege problemen, zelfs met tijdelijke oplossingen. </p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Intro/Menu + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Game is compleet onspeelbaar dankzij grote grafische of geluid problemen. Onmogelijk voorbij het startscherm te komen.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Start Niet + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>De game crasht wanneer je het probeert op te starten.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Onafhankelijk van snelheid of prestaties, Hoe goed speelt de game van start tot eind op deze versie van yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Bedankt voor je inzending! - + Submitting Inzenden - + Communication error Communicatiefout - + An error occurred while sending the Testcase Er is een fout gebeurd tijdens het versturen van de Testcase - + Next Volgende @@ -410,7 +450,7 @@ This would ban both their forum username and their IP address. Standaard Herstellen - + Auto Auto @@ -743,200 +783,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub GDB Stub Aanzetten - + Port: Poort: - + Logging Loggen - + Global Log Filter Globale Log Filter - + Show Log in Console Laat Log Venster Zien - + Open Log Location Open Log Locatie - + When checked, the max size of the log increases from 100 MB to 1 GB Indien aangevinkt, neemt de maximale grootte van de log toe van 100 MB tot 1 GB - + Enable Extended Logging** Activeer Uitgebreid Loggen** - + Homebrew Homebrew - + Arguments String Argumenten Rij - + Graphics Graphics - + When checked, the graphics API enters a slower debugging mode Indien aangevinkt, gaat de grafische API naar een langzamere foutopsporingsmodus - + Enable Graphics Debugging Grafische foutopsporing inschakelen - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Indien aangevinkt, wordt de macro Just In Time-compiler uitgeschakeld. Als u dit inschakelt, worden games langzamer - + Disable Macro JIT Schakel Macro JIT uit - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Debugging - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Geavanceerd - + Kiosk (Quest) Mode Kiosk (Quest) Modus - + Enable CPU Debugging - + Enable Debug Asserts Schakel Debug asserties in - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Deze optie wordt automatisch gereset wanneer yuzu is gesloten. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1307,193 +1382,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings Grafische Instellingen - + Use disk pipeline cache - + Use asynchronous GPU emulation Gebruik asynchroon GPU emulatie - + Accelerate ASTC texture decoding - + NVDEC emulation: - + No Video Output - + CPU Video Decoding - + GPU Video Decoding (Default) - + Fullscreen Mode: Volledig scherm modus: - + Borderless Windowed BoordLoos Venster - + Exclusive Fullscreen Exclusief Volledig Scherm - + Aspect Ratio: Aspect Ratio: - + Default (16:9) Standaart (16:9) - + Force 4:3 Forceer 4:3 - + Force 21:9 Forceer 21:9 - + + Force 16:10 + + + + Stretch to Window Rek naar Venster - + Resolution: - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) - + 2X (1440p/2160p) - + 3X (2160p/3240p) - + 4X (2880p/4320p) - + 5X (3600p/5400p) - + 6X (4320p/6480p) - + Window Adapting Filter: - + Nearest Neighbor - + Bilinear - + Bicubic - + Gaussian - + ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) - + Anti-Aliasing Method: - + None Geen - + FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Gebruik globale achtergrondkleur - + Set background color: Gebruik achtergrondkleur: - + Background Color: Achtergrondkleur: @@ -1502,6 +1602,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1556,37 +1662,47 @@ This would ban both their forum username and their IP address. - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotrope Filtering: - + Automatic - + Default Standaard - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2078,7 +2194,7 @@ This would ban both their forum username and their IP address. - + Left Stick Linker Stick @@ -2172,14 +2288,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2198,7 +2314,7 @@ This would ban both their forum username and their IP address. - + Plus Plus: @@ -2211,15 +2327,15 @@ This would ban both their forum username and their IP address. - + R R: - + ZR ZR @@ -2276,231 +2392,236 @@ This would ban both their forum username and their IP address. - + Right Stick Rechter Stick - - - - + + + + Clear Verwijder - - - - - + + + + + [not set] [niet ingesteld] - - - Toggle button - Shakel Knop - - - - + + Invert button - - + + + Toggle button + Shakel Knop + + + + Invert axis Spiegel As - - - + + + Set threshold - - + + Choose a value between 0% and 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Zet Analoge Stick - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Na OK in te drukken, beweeg je joystick eerst horizontaal en dan verticaal. Om de assen te spiegelen, beweek je joystick eerst verticaal en dan horizontaal. - + Center axis - - + + Deadzone: %1% Deadzone: %1% - - + + Modifier Range: %1% Bewerk Range: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Twee Joycons - + Left Joycon Linker Joycon - + Right Joycon Rechter Joycon - + Handheld Mobiel - + GameCube Controller GameCube Controller - + Poke Ball Plus - + NES Controller - + SNES Controller - + N64 Controller - + Sega Genesis - + Start / Pause Start / Pauze - + Z Z - + Control Stick Control Stick - + C-Stick C-Stick - + Shake! Shudden! - + [waiting] [aan het wachten] - + New Profile Nieuw Profiel - + Enter a profile name: Voer nieuwe gebruikersnaam in: - - + + Create Input Profile Creëer een nieuw Invoer Profiel - + The given profile name is not valid! De ingevoerde Profiel naam is niet geldig - + Failed to create the input profile "%1" Het is mislukt om Invoer Profiel "%1 te Creëer - + Delete Input Profile Verwijder invoer profiel - + Failed to delete the input profile "%1" Het is mislukt om Invoer Profiel "%1 te Verwijderen - + Load Input Profile Laad invoer profiel - + Failed to load the input profile "%1" Het is mislukt om Invoer Profiel "%1 te Laden - + Save Input Profile Sla Invoer profiel op - + Failed to save the input profile "%1" Het is mislukt om Invoer Profiel "%1 Op te slaan @@ -2755,42 +2876,42 @@ Om de assen te spiegelen, beweek je joystick eerst verticaal en dan horizontaal. Ontwikkelaar - + Add-Ons Add-Ons - + General Algemeen - + System Systeem - + CPU CPU - + Graphics Grafisch - + Adv. Graphics Adv. Grafisch - + Audio Geluid - + Properties Eigenschappen @@ -2846,37 +2967,37 @@ Om de assen te spiegelen, beweek je joystick eerst verticaal en dan horizontaal. Huidige Gebruiker - + Username Gebruikersnaam - + Set Image Selecteer Afbeelding - + Add Voeg Toe - + Rename Hernoem - + Remove Verwijder - + Profile management is available only when game is not running. Profiel beheer is alleen beschikbaar wanneer het spel niet bezig is. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2884,96 +3005,105 @@ Om de assen te spiegelen, beweek je joystick eerst verticaal en dan horizontaal. %2 - + Enter Username Voer een Gebruikersnaam in - + Users Gebruikers - + Enter a username for the new user: Voer een gebruikersnaam in voor de nieuwe gebruiker: - + Enter a new username: Voer nieuwe gebruikersnaam in: - - Confirm Delete - Bevestig Verwijdering - - - - You are about to delete user with name "%1". Are you sure? - Je staat op het punt een gebruiker met de naam "%1" te verwijderen. Weet je het zeker? - - - + Select User Image Selecteer gebruiker's foto - + JPEG Images (*.jpg *.jpeg) JPEG foto's (*.jpg *.jpeg) - + Error deleting image Fout tijdens verwijderen afbeelding - + Error occurred attempting to overwrite previous image at: %1. Er is een fout opgetreden bij het overschrijven van de vorige afbeelding in: %1. - + Error deleting file Fout tijdens verwijderen bestand - + Unable to delete existing file: %1. Kan bestaand bestand niet verwijderen: %1. - + Error creating user image directory Fout tijdens het maken van de map met afbeeldingen van de gebruiker - + Unable to create directory %1 for storing user images. Fout tijdens het maken van map %1 om gebruikersafbeeldingen in te bewaren. - + Error copying user image Fout tijdens het kopiëren van de gebruiker afbeelding - + Unable to copy image from %1 to %2 Kan afbeelding niet kopiëren van %1 naar %2 - + Error resizing user image - + Unable to resize image + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Bevestig Verwijdering + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3502,47 +3632,47 @@ Om de assen te spiegelen, beweek je joystick eerst verticaal en dan horizontaal. - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Pad - + ... ... @@ -3794,56 +3924,71 @@ Sleep punten om positie te veranderen, of dubbel klik één van de tabel cellen + Show Compatibility List + + + + Show Add-Ons Column Toon Add-Ons Kolom - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: - + Folder Icon Size: - + Row 1 Text: Rij 1 Text: - + Row 2 Text: Rij 2 Text: - + Screenshots Schermafbeelding - + Ask Where To Save Screenshots (Windows Only) - + Screenshots Path: - + ... ... - + Select Screenshots Path... - + <System> <System> @@ -3952,7 +4097,7 @@ Sleep punten om positie te veranderen, of dubbel klik één van de tabel cellen - + Verify Verifieer @@ -4039,7 +4184,7 @@ Sleep punten om positie te veranderen, of dubbel klik één van de tabel cellen - + Unspecified Niet gespecificeerd @@ -4054,17 +4199,36 @@ Sleep punten om positie te veranderen, of dubbel klik één van de tabel cellen Token is niet geverifieerd. De verandering aan uw token zijn niet opgeslagen. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Verifiëren... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verificatie mislukt + + + Verification failed Verificatie mislukt - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verificatie mislukt. Check dat uw token correct is en dat uw internet werkt. @@ -4133,12 +4297,12 @@ Sleep punten om positie te veranderen, of dubbel klik één van de tabel cellen DirectConnectWindow - + Connecting - + Connect @@ -4146,908 +4310,910 @@ Sleep punten om positie te veranderen, of dubbel klik één van de tabel cellen GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Annonieme gegevens worden verzameld</a> om yuzu te helpen verbeteren. <br/><br/> Zou je jouw gebruiksgegevens met ons willen delen? - + Telemetry Telemetrie - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Web Applet Laden... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Huidige emulatie snelheid. Waardes hoger of lager dan 100% betekent dat de emulatie sneller of langzamer loopt dan de Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Hoeveel frames per seconde de game op dit moment weergeeft. Dit zal veranderen van game naar game en van scène naar scène. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tijd gebruikt om een frame van de Switch te emuleren, waarbij framelimiteren of v-sync niet wordt meegerekend. Voor emulatie op volledige snelheid zou dit maximaal 16.67 ms zijn. - + VULKAN - + OPENGL - + &Clear Recent Files - + &Continue - + &Pause &Pauzeren - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Waarschuwing Verouderd Spel Formaat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Je gebruikt gedeconstrueerd ROM map formaat voor dit Spel, dit is een verouderd formaat en is vervangen door formaten zoals NCA, NAX, XCI of NSP. Gedeconstrueerd ROM map heeft geen iconen, metadata en update understeuning.<br><br>Voor een uitleg over welke Switch formaten yuzu ondersteund, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>kijk op onze wiki</a>. Dit bericht word niet nog een keer weergegeven. - - + + Error while loading ROM! Fout tijdens het laden van een ROM! - + The ROM format is not supported. Het formaat van de ROM is niet ondersteunt. - + An error occurred initializing the video core. Er is een fout opgetreden tijdens het initialiseren van de videokern. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. Een onbekende fout heeft plaatsgevonden. Kijk in de log voor meer details. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Save Data Save Data - + Mod Data Mod Data - + Error Opening %1 Folder Fout tijdens het openen van %1 folder - - + + Folder does not exist! Folder bestaat niet! - + Error Opening Transferable Shader Cache Fout Bij Het Openen Van Overdraagbare Shader Cache - + Failed to create the shader cache directory for this title. - - Contents - - - - - Update - - - - - DLC - - - - - Remove Entry - - - - - Remove Installed Game %1? - - - - - - - - - Successfully Removed + Error Removing Contents - - Successfully removed the installed base game. - - - - - - - Error Removing %1 - - - - - The base game is not installed in the NAND and cannot be removed. - - - - - Successfully removed the installed update. + + Error Removing Update + Error Removing DLC + + + + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + + Remove Entry + + + + + + + + + + Successfully Removed + + + + + Successfully removed the installed base game. + + + + + The base game is not installed in the NAND and cannot be removed. + + + + + Successfully removed the installed update. + + + + There is no update installed for this title. - + There are no DLC installed for this title. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File - - + + Error Removing Transferable Shader Cache - - + + A shader cache for this title does not exist. Er bestaat geen shader cache voor deze game - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! RomFS Extractie Mislukt! - + There was an error copying the RomFS files or the user cancelled the operation. Er was een fout tijdens het kopiëren van de RomFS bestanden of de gebruiker heeft de operatie geannuleerd. - + Full Vol - + Skeleton Skelet - + Select RomFS Dump Mode Selecteer RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Selecteer alstublieft hoe je de RomFS wilt dumpen.<br>Volledig kopieërd alle bestanden in een map terwijl <br> skelet maakt alleen het map structuur. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... RomFS uitpakken... - - + + Cancel Annuleren - + RomFS Extraction Succeeded! RomFS Extractie Geslaagd! - + The operation completed successfully. De operatie is succesvol voltooid. - + Error Opening %1 Fout bij openen %1 - + Select Directory Selecteer Map - + Properties Eigenschappen - + The game properties could not be loaded. De eigenschappen van de game kunnen niet geladen worden. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch Executable (%1);;Alle bestanden (*.*) - + Load File Laad Bestand - + Open Extracted ROM Directory Open Gedecomprimeerd ROM Map - + Invalid Directory Selected Ongeldige Map Geselecteerd - + The directory you have selected does not contain a 'main' file. De map die je hebt geselecteerd bevat geen 'main' bestand. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... Bestand "%1" Installeren... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systeem Applicatie - + System Archive Systeem Archief - + System Application Update Systeem Applicatie Update - + Firmware Package (Type A) Filmware Pakket (Type A) - + Firmware Package (Type B) Filmware Pakket (Type B) - + Game Game - + Game Update Game Update - + Game DLC Game DLC - + Delta Title Delta Titel - + Select NCA Install Type... Selecteer NCA Installatie Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Selecteer het type titel hoe je wilt dat deze NCA installeerd: (In de meeste gevallen is de standaard 'Game' juist.) - + Failed to Install Installatie Mislukt - + The title type you selected for the NCA is invalid. Het type title dat je hebt geselecteerd voor de NCA is ongeldig. - + File not found Bestand niet gevonden - + File "%1" not found Bestand "%1" niet gevonden - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Je yuzu account mist - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Om game campatibiliteit te raporteren, moet je je yuzu account koppelen.<br><br/> Om je yuzu account te koppelen, ga naar Emulatie &gt; Configuratie &gt; Web. - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - - Error - - - - - - The current game is not looking for amiibos - - - - - + + Amiibo - - + + The current amiibo has been removed - + + Error + + + + + + The current game is not looking for amiibos + + + + Amiibo File (%1);; All Files (*.*) Amiibo Bestand (%1);; Alle Bestanden (*.*) - + Load Amiibo Laad Amiibo - - Error opening Amiibo data file - Fout tijdens het openen van het Amiibo gegevens bestand - - - - Unable to open Amiibo file "%1" for reading. - Kan Amiibo bestand "%1" niet lezen. - - - - Error reading Amiibo data file - Fout tijdens het lezen van het Amiibo gegevens bestand - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Kan de volledige Amiibo gegevens niet lezen. Verwacht om %1 bytes te lezen, maar het is alleen mogelijk om %2 bytes te lezen. - - - + Error loading Amiibo data Fout tijdens het laden van de Amiibo data - - Unable to load Amiibo data. - Kan de Amiibo gegevens niet laden. - - - - Capture Screenshot - Screenshot Vastleggen - - - - PNG Image (*.png) - PNG afbeelding (*.png) - - - - TAS state: Running %1/%2 + + The selected file is not a valid amiibo - - TAS state: Recording %1 + + The selected file is already on use - - TAS state: Idle %1/%2 + + An unknown error occurred + Capture Screenshot + Screenshot Vastleggen + + + + PNG Image (*.png) + PNG afbeelding (*.png) + + + + TAS state: Running %1/%2 + + + + + TAS state: Recording %1 + + + + + TAS state: Idle %1/%2 + + + + TAS State: Invalid - + &Stop Running - + &Start &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Snelheid: %1% / %2% - + Speed: %1% Snelheid: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Game: %1 FPS - + Frame: %1 ms Frame: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. De game die je probeert te laden heeft extra bestanden nodig van je Switch voordat je het kan spelen. <br/><br/>Voor meer informatie over het dumpen van deze bestanden, volg alsjeblieft onze wiki pagina: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Het dumpen van Systeem Archieven en de Gedeelde Lettertypen van een Switch console </a>. <br/><br/>Wil je terug gaan naar de game lijst? Verdergaan met de emulatie zal misschien gevolgen hebben als vastlopen, beschadigde opslag data, of andere problemen. - + yuzu was unable to locate a Switch system archive. %1 yuzu was niet in staat om de Switch systeem archieven te vinden. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu was niet in staat om de Switch systeem archieven te vinden. %1. %2 - + System Archive Not Found Systeem Archief Niet Gevonden - + System Archive Missing Systeem Archief Mist - + yuzu was unable to locate the Switch shared fonts. %1 yuzu was niet in staat om de Switch shared fonts te vinden. %1 - + Shared Fonts Not Found Shared Fonts Niet Gevonden - + Shared Font Missing Gedeelde Lettertypes Niet Gevonden - + Fatal Error Fatale Fout - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu is een fatale fout tegengekomen, zie de log voor meer details. Voor meer informatie over toegang krijgen tot de log, zie de volgende pagina: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Hoe upload je een log bestand</a>.<br/><br/>Zou je terug willen naar de game lijst? Doorgaan met emulatie kan resulteren in vastlapen, corrupte save gegevens, of andere problemen. - + Fatal Error encountered Fatale Fout opgetreden - + Confirm Key Rederivation Bevestig Sleutel Herafleiding - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5064,37 +5230,37 @@ en optioneel maak backups. Dit zal je automatisch gegenereerde sleutel bestanden verwijderen en de sleutel verkrijger module opnieuw starten - + Missing fuses - + - Missing BOOT0 - + - Missing BCPKG2-1-Normal-Main - + - Missing PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5102,39 +5268,39 @@ on your system's performance. op je systeem's performatie. - + Deriving Keys Sleutels afleiden - + Select RomFS Dump Target Selecteer RomFS Dump Doel - + Please select which RomFS you would like to dump. Selecteer welke RomFS je zou willen dumpen. - + Are you sure you want to close yuzu? Weet je zeker dat je yuzu wilt sluiten? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Weet je zeker dat je de emulatie wilt stoppen? Alle onopgeslagen voortgang will verloren gaan. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5146,38 +5312,38 @@ Wilt u dit omzeilen en toch afsluiten? GRenderWindow - + OpenGL not available! - + yuzu has not been compiled with OpenGL support. - - + + Error while initializing OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. - + Error while initializing OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 @@ -5185,153 +5351,153 @@ Wilt u dit omzeilen en toch afsluiten? GameList - + Favorite - + Start Game - + Start Game without Custom Configuration - + Open Save Data Location Open Locatie Van Save Gegevens - + Open Mod Data Location Open Mod Data Locatie - + Open Transferable Pipeline Cache - + Remove Verwijder - + Remove Installed Update - + Remove All Installed DLC - + Remove Custom Configuration - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents - + Dump RomFS Dump RomFS - + Dump RomFS to SDMC - + Copy Title ID to Clipboard Kopieer Titel ID naar Klembord - + Navigate to GameDB entry Navigeer naar GameDB inzending - + Properties Eigenschappen - + Scan Subfolders Scan Subfolders - + Remove Game Directory Verwijder Game Directory - + ▲ Move Up - + ▼ Move Down - + Open Directory Location Open Directory Locatie - + Clear Verwijder - + Name Naam - + Compatibility Compatibiliteit - + Add-ons Toevoegingen - + File type Bestands type - + Size Grootte @@ -5340,76 +5506,61 @@ Wilt u dit omzeilen en toch afsluiten? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Perfect - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - Game functioneerd foutloos, zonder auditieve of grafische glitches. Alle geteste functionaliteit werkt zoals bedoeld zonder dat er tijdelijke oplossingen nodig zijn. - - - - Great - Goed - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - Game werkt met kleine grafische of auditieve glitches en is speelbaar van start tot eind. Kan enkele workarounds nodig hebben. - - Okay - Okay - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - Game werkt met grove grafische of auditieve glitches, maar is speelbaar van start tot eind met workarounds + Game can be played without issues. + - Bad - Slecht + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Game werkt, maar met grove grafische of auditieve glitches. Specifieke gebieden kunnen niet worden uitgespeeld vanwege glitches, zelfs met workarounds. + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu Intro/Menu - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Game is compleet onspeelbaar dankzij grove grafische of auditieve glitches. Onmogelijk voorbij het startscherm te gaan. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Start Niet - + The game crashes when attempting to startup. De Game crasht wanneer hij probeert op te starten. - + Not Tested Niet Getest - + The game has not yet been tested. Deze Game is nog niet getest. @@ -5417,7 +5568,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Dubbel-klik om een ​​nieuwe map toe te voegen aan de lijst met games @@ -5430,12 +5581,12 @@ Screen. - + Filter: Filter: - + Enter pattern to filter Voer patroon in om te filteren: @@ -5485,7 +5636,7 @@ Screen. Room Description - + Kamer Beschrijving @@ -5511,12 +5662,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5525,11 +5676,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5551,112 +5703,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Screenshot Vastleggen - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Volledig Scherm - + Load File Laad Bestand - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5770,42 +5921,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Spelers - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5828,232 +5979,237 @@ Debug Message: - + &Emulation &Emulatie - + &View &Weergeven - + &Reset Window Size - + &Debugging - + Reset Window Size to &720p - + Reset Window Size to 720p - + Reset Window Size to &900p - + Reset Window Size to 900p - + Reset Window Size to &1080p - + Reset Window Size to 1080p - + + &Multiplayer + + + + &Tools - + &TAS - + &Help &Help - + &Install Files to NAND... - + L&oad File... - + Load &Folder... - + E&xit A&fsluiten - + &Pause &Pauzeren - + &Stop &Stop - + &Reinitialize keys... - + &About yuzu - + Single &Window Mode - + Con&figure... - + Display D&ock Widget Headers - + Show &Filter Bar - + Show &Status Bar - + Show Status Bar Laat status balk zien - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room + &Browse Public Game Lobby - - Direct Connect to Room + + &Create Room - - Show Current Room + + &Leave Room - F&ullscreen + &Direct Connect to Room - &Restart + &Show Current Room - Load/Remove &Amiibo... + F&ullscreen - &Report Compatibility + &Restart + Load/Remove &Amiibo... + + + + + &Report Compatibility + + + + Open &Mods Page - + Open &Quickstart Guide - + &FAQ - + Open &yuzu Folder - + &Capture Screenshot - + &Configure TAS... - + Configure C&urrent Game... - + &Start &Start - + &Reset - + R&ecord @@ -6118,46 +6274,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Verbonden - - - - Not Connected - + + Connected + Verbonden + + + + New Messages Received + + + + Error - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6258,22 +6409,39 @@ They may have left the room. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6281,7 +6449,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6326,42 +6494,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 is playing %2 - + Not playing a game - + Installed SD Titles Geïnstalleerde SD Titels - + Installed NAND Titles Geïnstalleerde NAND Titels - + System Titles Systeem Titels - + Add New Game Directory Voeg Nieuwe Game Map Toe - + Favorites @@ -6391,7 +6559,7 @@ p, li { white-space: pre-wrap; } - + [not set] [niet aangegeven] @@ -6406,10 +6574,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Axis %1%2 @@ -6423,9 +6591,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [onbekend] @@ -6590,15 +6758,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6606,35 +6774,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [ongebruikt] @@ -6675,11 +6843,124 @@ p, li { white-space: pre-wrap; } - + %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Naam + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6783,6 +7064,7 @@ p, li { white-space: pre-wrap; } + Handheld Mobiel @@ -6822,11 +7104,6 @@ p, li { white-space: pre-wrap; } Docked GeDocked - - - Undocked - Niet-Gedocked - Vibration diff --git a/dist/languages/pl.ts b/dist/languages/pl.ts index 83b4d2b..3c5d18c 100644 --- a/dist/languages/pl.ts +++ b/dist/languages/pl.ts @@ -95,78 +95,78 @@ p, li { white-space: pre-wrap; } Wyślij Wiadomość - + Members Członkowie - + %1 has joined %1 dołączył/a - + %1 has left %1 wyszedł/ła - + %1 has been kicked %1 został/a wyrzucony/a - + %1 has been banned %1 został/a zbanowany/a - + %1 has been unbanned %1 został/a odbanowany/a - + View Profile Wyświetl Profil - - + + Block Player Zablokuj Gracza - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Po zablokowaniu gracza nie będziesz otrzymywał od niego/jej wiadomości. <br><br>Czy na pewno chcesz zablokować %1? - + Kick Wyrzuć - + Ban Zbanuj - + Kick Player Wyrzuć Gracza - + Are you sure you would like to <b>kick</b> %1? Na pewno chcesz <b>wyrzucić</b> %1? - + Ban Player Zbanuj Gracza - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -212,8 +212,8 @@ To zbanuje jego/jej nick na forum, oraz jego/jej adres IP. - %1 (%2/%3 members) - connected - %1 (%2/%3 uczestników) - połączono + %1 - %2 (%3/%4 members) - connected + @@ -226,6 +226,11 @@ To zbanuje jego/jej nick na forum, oraz jego/jej adres IP. + + + + + Report Game Compatibility Zgłoś kompatybilność gry @@ -235,92 +240,127 @@ To zbanuje jego/jej nick na forum, oraz jego/jej adres IP. <html><head/><body><p><span style=" font-size:10pt;">Jeśli postanowisz wysłać wyniki testu na </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">listę kompatybilności yuzu</span></a><span style=" font-size:10pt;">, następujące informacja zostaną zebrane i wyświetlone na stronie:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informacja o sprzęcie komputerowym (procesor / karta graficzna / system operacyjny)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wersja yuzu, której używasz</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Połączone konto yuzu</li></ul></body></html> - - Perfect - Idealna + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Gra działa idealnie - bez żadnych glitchy audio czy graficznych.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Świetnie + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Gra działa z pomniejszymi błędami audio lub graficznymi, jest grywalna od początku do końca. Potrzebne mogą być "obejścia".</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - W porządku + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Gra działa, ale występują znaczące glitche graficzne, lub glitche audio. Przez glitche nie można przejść specyficznych sekcji gry, nawet używając "obejść".</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Zła + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Gra działa, ale występują znaczące glitche graficzne, lub glitche audio. Przez glitche nie można przejść specyficznych sekcji gry, nawet używając "obejść".</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Intro/Menu + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Gra jest totalnie niegrywalna przez znaczące glitche graficzne lub audio. Nie można przejść przez ekran startowy.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Nie uruchamia się + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Gra zawiesza się podczas próby uruchomienia się.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Pomijając prędkość gry, i jej wydajność, czy można spokojnie przejść ją od początku do końca na tej wersji yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Dziękujemy za Twoją opinię! - + Submitting Wysyłanie - + Communication error Błąd komunikacyjny - + An error occurred while sending the Testcase Wystąpił błąd podczas wysyłania Testcase'u - + Next Następny @@ -418,7 +458,7 @@ To zbanuje jego/jej nick na forum, oraz jego/jej adres IP. Przywróć domyślne - + Auto Automatyczny @@ -758,200 +798,235 @@ Gdy ta opcja jest włączona, niedopasowanie jest uruchamiane tylko wtedy, gdy d ConfigureDebug - + Debugger - + Enable GDB Stub Włącz namiastkę GDB - + Port: Port - + Logging Logowanie - + Global Log Filter Globalny filtr rejestrów - + Show Log in Console Pokaż Log w konsoli - + Open Log Location Otwórz miejsce rejestrów - + When checked, the max size of the log increases from 100 MB to 1 GB Kiedy zaznaczony, maksymalny rozmiar logu wzrasta ze 100 MB do 1 GB - + Enable Extended Logging** Włącz Przedłużony Logging** - + Homebrew Homebrew - + Arguments String Linijka argumentu - + Graphics Grafika - + When checked, the graphics API enters a slower debugging mode Gdy zaznaczone, API grafiki przechodzi w wolniejszy tryb debugowania - + Enable Graphics Debugging Włącz debugowanie grafiki - + When checked, it enables Nsight Aftermath crash dumps Gdy zaznaczone, włącza zrzucanie awarii Nsight Aftermath - + Enable Nsight Aftermath Włącz Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Po zaznaczeniu, zrzuci wszystkie oryginalne shadery asemblera z pamięci podręcznej dysku shaderów albo gry, jeśli zostaną znalezione - + Dump Game Shaders Zrzuć Shadery Gry - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Gdy zaznaczone, wyłącza kompilator makr Just In Time. Włączenie tej opcji spowalnia działanie gier - + Disable Macro JIT Wyłącz Makro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Po zaznaczeniu, yuzu będzie rejestrować statystyki dotyczące skompilowanej pamięci podręcznej. - + Enable Shader Feedback Włącz funkcję Feedbacku Shaderów - + When checked, it executes shaders without loop logic changes Gdy zaznaczone, używa shaderów bez zmian logicznych pętli - + Disable Loop safety checks Wyłącz Zapętlanie sprawdzania bezpieczeństwa - + Debugging Debugowanie - - Enable FS Access Log - Włącz dziennik Dostępu FS - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Włącz Pełne Usługi Raportowania** - + + Enable FS Access Log + Włącz dziennik Dostępu FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Zaawansowane - + Kiosk (Quest) Mode Tryb Kiosk (Quest) - + Enable CPU Debugging Włącz Debugowanie CPU - + Enable Debug Asserts Włącz potwierdzenia debugowania - + Enable Auto-Stub** Włącz Auto-Stub** - + Enable All Controller Types - + Disable Web Applet Wyłącz Aplet internetowy - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **To zresetuje się automatycznie po wyłączeniu yuzu. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1321,193 +1396,218 @@ Gdy ta opcja jest włączona, niedopasowanie jest uruchamiane tylko wtedy, gdy d API: - + Graphics Settings Ustawienia Graficzne - + Use disk pipeline cache Użyj Pamięci Podręcznej Pipeline z dysku - + Use asynchronous GPU emulation Użyj asynchronicznej emulacji GPU - + Accelerate ASTC texture decoding Przyspiesz dekodowanie tekstur ASTC - + NVDEC emulation: Emulacja NVDEC: - + No Video Output Brak wyjścia wideo - + CPU Video Decoding Dekodowanie Wideo przez CPU - + GPU Video Decoding (Default) Dekodowanie Wideo przez GPU (Domyślne) - + Fullscreen Mode: Tryb Pełnoekranowy: - + Borderless Windowed W oknie (Bezramkowy) - + Exclusive Fullscreen Exclusive Fullscreen - + Aspect Ratio: Format obrazu: - + Default (16:9) Domyślne (16:9) - + Force 4:3 Wymuś 4:3 - + Force 21:9 Wymuś 21:9 - + + Force 16:10 + + + + Stretch to Window Rozciągnij do Okna - + Resolution: Rozdzielczość: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EKSPERYMENTALNE] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EKSPERYMENTALNE] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: Filtr Adaptującego Okna: - + Nearest Neighbor Najbliższy Sąsiad - + Bilinear Bilinearny - + Bicubic Bikubiczny - + Gaussian Gauss - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Rozdzielczość (Tylko Vulkan) - + Anti-Aliasing Method: Metoda Anty-Aliasingu: - + None Żadny - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Ustaw globalny kolor tła - + Set background color: Ustaw kolor tła: - + Background Color: Kolor tła @@ -1516,6 +1616,12 @@ Gdy ta opcja jest włączona, niedopasowanie jest uruchamiane tylko wtedy, gdy d GLASM (Assembly Shaders, NVIDIA Only) GLASM (Zgromadzone Shadery, tylko NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1571,37 +1677,47 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności.Użyj Szybszego Czasu GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtrowanie anizotropowe: - + Automatic Automatyczne - + Default Domyślne - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2093,7 +2209,7 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności. - + Left Stick Lewa gałka @@ -2187,14 +2303,14 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności. - + L L - + ZL ZL @@ -2213,7 +2329,7 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności. - + Plus Plus @@ -2226,15 +2342,15 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności. - + R R - + ZR ZR @@ -2291,231 +2407,236 @@ Pozostaw tą funkcję włączoną, jeśli nie widać różnicy w wydajności. - + Right Stick Prawa gałka - - - - + + + + Clear Wyczyść - - - - - + + + + + [not set] [nie ustawione] - - - Toggle button - Przycisk Toggle - - - - + + Invert button Odwróć przycisk - - + + + Toggle button + Przycisk Toggle + + + + Invert axis Odwróć oś - - - + + + Set threshold Ustaw próg - - + + Choose a value between 0% and 100% Wybierz wartość od 0% do 100% - + + Toggle axis + + + + Set gyro threshold Ustaw próg gyro - + Map Analog Stick Przypisz Drążek Analogowy - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Po naciśnięciu OK, najpierw przesuń joystick w poziomie, a następnie w pionie. Aby odwrócić osie, najpierw przesuń joystick pionowo, a następnie poziomo. - + Center axis - - + + Deadzone: %1% Martwa strefa: %1% - - + + Modifier Range: %1% Zasięg Modyfikatora: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Para Joyconów - + Left Joycon Lewy Joycon - + Right Joycon Prawy Joycon - + Handheld Handheld - + GameCube Controller Kontroler GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Kontroler NES/Pegasus - + SNES Controller Kontroler SNES - + N64 Controller Kontroler N64 - + Sega Genesis Sega Mega Drive - + Start / Pause Start / Pauza - + Z Z - + Control Stick Lewa gałka - + C-Stick C-gałka - + Shake! Potrząśnij! - + [waiting] [oczekiwanie] - + New Profile Nowy profil - + Enter a profile name: Wpisz nazwę profilu: - - + + Create Input Profile Utwórz profil wejściowy - + The given profile name is not valid! Podana nazwa profilu jest nieprawidłowa! - + Failed to create the input profile "%1" Nie udało się utworzyć profilu wejściowego "%1" - + Delete Input Profile Usuń profil wejściowy - + Failed to delete the input profile "%1" Nie udało się usunąć profilu wejściowego "%1" - + Load Input Profile Załaduj profil wejściowy - + Failed to load the input profile "%1" Nie udało się wczytać profilu wejściowego "%1" - + Save Input Profile Zapisz profil wejściowy - + Failed to save the input profile "%1" Nie udało się zapisać profilu wejściowego "%1" @@ -2770,42 +2891,42 @@ Aby odwrócić osie, najpierw przesuń joystick pionowo, a następnie poziomo.Deweloper - + Add-Ons Dodatki - + General Ogólne - + System System - + CPU CPU - + Graphics Grafika - + Adv. Graphics Zaaw. Grafika - + Audio Dźwięk - + Properties Właściwości @@ -2861,37 +2982,37 @@ Aby odwrócić osie, najpierw przesuń joystick pionowo, a następnie poziomo.Obecny użytkownik - + Username Nazwa Użytkownika - + Set Image Ustaw zdjęcie - + Add Dodaj - + Rename Zmień nazwę - + Remove Usuń - + Profile management is available only when game is not running. Menedżer Profili nie jest dostępny gdy gra jest uruchomiona. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2899,96 +3020,105 @@ Aby odwrócić osie, najpierw przesuń joystick pionowo, a następnie poziomo. - + Enter Username Wpisz nazwę użytkownika - + Users Użytkownicy - + Enter a username for the new user: Wprowadź nazwę dla nowego użytkownika: - + Enter a new username: Wpisz nową nazwę użytkownika: - - Confirm Delete - Potwierdź usunięcie - - - - You are about to delete user with name "%1". Are you sure? - Zamierzasz usunąć użytkownika "%1". Jesteś pewien? - - - + Select User Image Ustaw zdjęcie użytkownika - + JPEG Images (*.jpg *.jpeg) Obrazki JPEG (*.jpg *.jpeg) - + Error deleting image Bład usunięcia zdjęcia - + Error occurred attempting to overwrite previous image at: %1. Błąd podczas próby nadpisania poprzedniego zdjęcia dla: %1. - + Error deleting file Błąd usunięcia pliku - + Unable to delete existing file: %1. Nie można usunąć istniejącego pliku: %1 - + Error creating user image directory Błąd podczas tworzenia folderu ze zdjęciem użytkownika - + Unable to create directory %1 for storing user images. Nie można utworzyć ścieżki %1 do przechowywania zdjęć użytkownika. - + Error copying user image Błąd kopiowania zdjęcia użytkownika - + Unable to copy image from %1 to %2 Nie można skopiować zdjęcia z %1 do %2 - + Error resizing user image Błąd podczas zmieniania rozmiaru obrazu użytkownika - + Unable to resize image Nie można zmienić rozmiaru obrazu + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Potwierdź usunięcie + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3517,47 +3647,47 @@ Aby odwrócić osie, najpierw przesuń joystick pionowo, a następnie poziomo.<html><head/><body><p>Odczytuje dane wejściowe kontrolera ze skryptów takiego samego formatu jak skrypty TAS-nx.<br/> Jeśli chcesz otrzymać bardziej szczegółowe wyjaśnienie, wejdź na <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">stronę pomocy</span></a> na stronie internetowej yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Aby sprawdzić jakie skróty sterują odtwarzaniem/nagrywaniem, odnieś się do ustawień Skrótów (Konfiguruj -> Ogólne -> Skróty Klawiszowe). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. UWAGA: To jest funkcja eksperymentalna. Nie będzie odtwarzała skryptów co do klatki z teraźniejszą, nieperfekcyjną metodą synchronizacji. - + Settings Ustawienia - + Enable TAS features Włącz funkcje TAS - + Loop script Zapętlij skrypt - + Pause execution during loads Zatrzymuj wykonanie w trakcie ładowania - + Script Directory Ścieżka Skryptu - + Path Ścieżka - + ... ... @@ -3809,56 +3939,71 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe + Show Compatibility List + + + + Show Add-Ons Column Pokaż kolumnę dodatków - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: Rozmiar Ikony Gry - + Folder Icon Size: Rozmiar Ikony Folderu - + Row 1 Text: Tekst 1. linii - + Row 2 Text: Tekst 2. linii - + Screenshots Zrzuty ekranu - + Ask Where To Save Screenshots (Windows Only) Pytaj gdzie zapisać zrzuty ekranu (Tylko dla Windows) - + Screenshots Path: Ścieżka zrzutów ekranu: - + ... ... - + Select Screenshots Path... Wybierz ścieżkę zrzutów ekranu... - + <System> <System> @@ -3967,7 +4112,7 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe - + Verify Zweryfikuj @@ -4054,7 +4199,7 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe - + Unspecified Nieokreślona @@ -4069,17 +4214,36 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe Token nie został zweryfikowany. Zmiana w Twoim tokenie nie została zapisana. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Weryfikowanie... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Weryfikowanie nieudane + + + Verification failed Weryfikowanie nieudane - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Weryfikacja nie powiodła się. Sprawdź, czy poprawnie podałeś swój token oraz czy działa twoje połączenie internetowe. @@ -4148,12 +4312,12 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe DirectConnectWindow - + Connecting Łączenie - + Connect Połącz @@ -4161,489 +4325,492 @@ Przeciągnij punkty, aby zmienić pozycję, lub kliknij dwukrotnie komórki tabe GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Dane anonimowe są gromadzone</a> aby ulepszyć yuzu. <br/><br/>Czy chcesz udostępnić nam swoje dane o użytkowaniu? - + Telemetry Telemetria - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Ładowanie apletu internetowego... - - + + Disable Web Applet Wyłącz Aplet internetowy - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Wyłączanie web appletu może doprowadzić do nieokreślonych zachowań - wyłączyć applet należy jedynie grając w Super Mario 3D All-Stars. Na pewno chcesz wyłączyć web applet? (Można go ponownie włączyć w ustawieniach debug.) - + The amount of shaders currently being built Ilość budowanych shaderów - + The current selected resolution scaling multiplier. Obecnie wybrany mnożnik rozdzielczości. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Aktualna prędkość emulacji. Wartości większe lub niższe niż 100% wskazują, że emulacja działa szybciej lub wolniej niż Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Ile klatek na sekundę gra aktualnie wyświetla. To będzie się różnić w zależności od gry, od sceny do sceny. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Czas potrzebny do emulacji klatki na sekundę Switcha, nie licząc ograniczania klatek ani v-sync. Dla emulacji pełnej szybkości powinno to wynosić co najwyżej 16,67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Usuń Ostatnie pliki - + &Continue &Kontynuuj - + &Pause &Pauza - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu jest w trakcie gry - + Warning Outdated Game Format OSTRZEŻENIE! Nieaktualny format gry - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Używasz zdekonstruowanego formatu katalogu ROM dla tej gry, który jest przestarzałym formatem, który został zastąpiony przez inne, takie jak NCA, NAX, XCI lub NSP. W zdekonstruowanych katalogach ROM brakuje ikon, metadanych i obsługi aktualizacji.<br><br> Aby znaleźć wyjaśnienie różnych formatów Switch obsługiwanych przez yuzu,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'> sprawdź nasze wiki</a>. Ta wiadomość nie pojawi się ponownie. - - + + Error while loading ROM! Błąd podczas wczytywania ROMu! - + The ROM format is not supported. Ten format ROMu nie jest wspierany. - + An error occurred initializing the video core. Wystąpił błąd podczas inicjowania rdzenia wideo. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu napotkał błąd podczas uruchamiania rdzenia wideo. Jest to zwykle spowodowane przestarzałymi sterownikami GPU, w tym zintegrowanymi. Więcej szczegółów znajdziesz w pliku log. Więcej informacji na temat dostępu do log-u można znaleźć na następującej stronie: <a href='https://yuzu-emu.org/help/reference/log-files/'>Jak przesłać plik log</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Błąd podczas wczytywania ROMu! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Postępuj zgodnie z<a href='https://yuzu-emu.org/help/quickstart/'>yuzu quickstart guide</a> aby zrzucić ponownie swoje pliki.<br>Możesz odwołać się do wiki yuzu</a>lub discord yuzu </a> po pomoc. - + An unknown error occurred. Please see the log for more details. Wystąpił nieznany błąd. Więcej informacji można znaleźć w pliku log. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Zapis danych - + Mod Data Dane modów - + Error Opening %1 Folder Błąd podczas otwarcia folderu %1 - - + + Folder does not exist! Folder nie istnieje! - + Error Opening Transferable Shader Cache Błąd podczas otwierania przenośnej pamięci podręcznej Shaderów. - + Failed to create the shader cache directory for this title. Nie udało się stworzyć ścieżki shaderów dla tego tytułu. - - Contents - Zawartość + + Error Removing Contents + - - Update - Łatka + + Error Removing Update + - - DLC - DLC + + Error Removing DLC + - + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + Remove Entry Usuń wpis - - Remove Installed Game %1? - Usunąć zainstalowaną grę %1? - - - - - - - - + + + + + + Successfully Removed Pomyślnie usunięto - + Successfully removed the installed base game. Pomyślnie usunięto zainstalowaną grę. - - - - Error Removing %1 - Błąd podczas usuwania %1 - - - + The base game is not installed in the NAND and cannot be removed. Gra nie jest zainstalowana w NAND i nie może zostać usunięta. - + Successfully removed the installed update. Pomyślnie usunięto zainstalowaną łatkę. - + There is no update installed for this title. Brak zainstalowanych łatek dla tego tytułu. - + There are no DLC installed for this title. Brak zainstalowanych DLC dla tego tytułu. - + Successfully removed %1 installed DLC. Pomyślnie usunięto %1 zainstalowane DLC. - + Delete OpenGL Transferable Shader Cache? Usunąć Transferowalne Shadery OpenGL? - + Delete Vulkan Transferable Shader Cache? Usunąć Transferowalne Shadery Vulkan? - + Delete All Transferable Shader Caches? Usunąć Wszystkie Transferowalne Shadery? - + Remove Custom Game Configuration? Usunąć niestandardową konfigurację gry? - + Remove File Usuń plik - - + + Error Removing Transferable Shader Cache Błąd podczas usuwania przenośnej pamięci podręcznej Shaderów. - - + + A shader cache for this title does not exist. Pamięć podręczna Shaderów dla tego tytułu nie istnieje. - + Successfully removed the transferable shader cache. Pomyślnie usunięto przenośną pamięć podręczną Shaderów. - + Failed to remove the transferable shader cache. Nie udało się usunąć przenośnej pamięci Shaderów. - - + + Error Removing Transferable Shader Caches Błąd podczas usuwania Transferowalnych Shaderów - + Successfully removed the transferable shader caches. Pomyślnie usunięto transferowalne shadery. - + Failed to remove the transferable shader cache directory. Nie udało się usunąć ścieżki transferowalnych shaderów. - - + + Error Removing Custom Configuration Błąd podczas usuwania niestandardowej konfiguracji - + A custom configuration for this title does not exist. Niestandardowa konfiguracja nie istnieje dla tego tytułu. - + Successfully removed the custom game configuration. Pomyślnie usunięto niestandardową konfiguracje gry. - + Failed to remove the custom game configuration. Nie udało się usunąć niestandardowej konfiguracji gry. - - + + RomFS Extraction Failed! Wypakowanie RomFS nieudane! - + There was an error copying the RomFS files or the user cancelled the operation. Wystąpił błąd podczas kopiowania plików RomFS lub użytkownik anulował operację. - + Full Pełny - + Skeleton Szkielet - + Select RomFS Dump Mode Wybierz tryb zrzutu RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Proszę wybrać w jaki sposób chcesz, aby zrzut pliku RomFS został wykonany. <br>Pełna kopia ze wszystkimi plikami do nowego folderu, gdy <br>skielet utworzy tylko strukturę folderu. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Nie ma wystarczająco miejsca w %1 aby wyodrębnić RomFS. Zwolnij trochę miejsca, albo zmień ścieżkę zrzutu RomFs w Emulacja> Konfiguruj> System> System Plików> Źródło Zrzutu - + Extracting RomFS... Wypakowywanie RomFS... - - + + Cancel Anuluj - + RomFS Extraction Succeeded! Wypakowanie RomFS zakończone pomyślnie! - + The operation completed successfully. Operacja zakończona sukcesem. - + Error Opening %1 Błąd podczas otwierania %1 - + Select Directory Wybierz folder... - + Properties Właściwości - + The game properties could not be loaded. Właściwości tej gry nie mogły zostać załadowane. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Plik wykonywalny Switcha (%1);;Wszystkie pliki (*.*) - + Load File Załaduj plik... - + Open Extracted ROM Directory Otwórz folder wypakowanego ROMu - + Invalid Directory Selected Wybrano niewłaściwy folder - + The directory you have selected does not contain a 'main' file. Folder wybrany przez ciebie nie zawiera 'głownego' pliku. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Instalacyjne pliki Switch'a (*.nca *.nsp *.xci);;Archiwum zawartości Nintendo (*.nca);;Pakiet poddany Nintendo (*.nsp);;Obraz z kartridża NX (*.xci) - + Install Files Zainstaluj pliki - + %n file(s) remaining 1 plik został%n plików zostało%n plików zostało%n plików zostało - + Installing file "%1"... Instalowanie pliku "%1"... - - + + Install Results Wynik instalacji - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Aby uniknąć ewentualnych konfliktów, odradzamy użytkownikom instalowanie gier na NAND. Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + %n file(s) were newly installed 1 nowy plik został zainstalowany @@ -4653,424 +4820,423 @@ Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + %n file(s) were overwritten 1 plik został nadpisany%n plików zostało nadpisane%n plików zostało nadpisane%n plików zostało nadpisane - + %n file(s) failed to install 1 pliku nie udało się zainstalować%n plików nie udało się zainstalować%n plików nie udało się zainstalować%n plików nie udało się zainstalować - + System Application Aplikacja systemowa - + System Archive Archiwum systemu - + System Application Update Aktualizacja aplikacji systemowej - + Firmware Package (Type A) Paczka systemowa (Typ A) - + Firmware Package (Type B) Paczka systemowa (Typ B) - + Game Gra - + Game Update Aktualizacja gry - + Game DLC Dodatek do gry - + Delta Title Tytuł Delta - + Select NCA Install Type... Wybierz typ instalacji NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Wybierz typ tytułu, do którego chcesz zainstalować ten NCA, jako: (W większości przypadków domyślna "gra" jest w porządku.) - + Failed to Install Instalacja nieudana - + The title type you selected for the NCA is invalid. Typ tytułu wybrany dla NCA jest nieprawidłowy. - + File not found Nie znaleziono pliku - + File "%1" not found Nie znaleziono pliku "%1" - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Brakuje konta Yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Aby przesłać test zgodności gry, musisz połączyć swoje konto yuzu.<br><br/> Aby połączyć swoje konto yuzu, przejdź do opcji Emulacja &gt; Konfiguracja &gt; Sieć. - + Error opening URL Błąd otwierania adresu URL - + Unable to open the URL "%1". Nie można otworzyć adresu URL "%1". - + TAS Recording Nagrywanie TAS - + Overwrite file of player 1? Nadpisać plik gracza 1? - + Invalid config detected Wykryto nieprawidłową konfigurację - + Handheld controller can't be used on docked mode. Pro controller will be selected. Nie można używać kontrolera handheld w trybie zadokowanym. Zostanie wybrany kontroler Pro. - - - Error - Błąd - - - - - The current game is not looking for amiibos - Ta gra nie szuka amiibo - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed Amiibo zostało "zdjęte" - + + Error + Błąd + + + + + The current game is not looking for amiibos + Ta gra nie szuka amiibo + + + Amiibo File (%1);; All Files (*.*) Plik Amiibo (%1);;Wszyskie pliki (*.*) - + Load Amiibo Załaduj Amiibo - - Error opening Amiibo data file - Błąd otwarcia pliku danych Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Nie można otworzyć pliku Amiibo "%1" do odczytu. - - - - Error reading Amiibo data file - Błąd podczas odczytu pliku danych Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Nie można w pełni odczytać danych Amiibo. Oczekiwano odczytu %1 bajtów, ale był on w stanie odczytać tylko %2 bajty. - - - + Error loading Amiibo data Błąd podczas ładowania pliku danych Amiibo - - Unable to load Amiibo data. - Nie można załadować danych Amiibo. + + The selected file is not a valid amiibo + - + + The selected file is already on use + + + + + An unknown error occurred + + + + Capture Screenshot Zrób zrzut ekranu - + PNG Image (*.png) Obrazek PNG (*.png) - + TAS state: Running %1/%2 Status TAS: Działa %1%2 - + TAS state: Recording %1 Status TAS: Nagrywa %1 - + TAS state: Idle %1/%2 Status TAS: Bezczynny %1%2 - + TAS State: Invalid Status TAS: Niepoprawny - + &Stop Running &Wyłącz - + &Start &Start - + Stop R&ecording Przestań N&agrywać - + R&ecord N&agraj - + Building: %n shader(s) Budowanie shaderaBudowanie: %n shaderówBudowanie: %n shaderówBudowanie: %n shaderów - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% Prędkość: %1% / %2% - + Speed: %1% Prędkość: %1% - + Game: %1 FPS (Unlocked) Gra: %1 FPS (Odblokowane) - + Game: %1 FPS Gra: %1 FPS - + Frame: %1 ms Klatka: %1 ms - + GPU NORMAL GPU NORMALNE - + GPU HIGH GPU WYSOKIE - + GPU EXTREME GPU EKSTREMALNE - + GPU ERROR BŁĄD GPU - + DOCKED TRYB ZADOKOWANY - + HANDHELD TRYB PRZENOŚNY - + NEAREST NAJBLIŻSZY - - + + BILINEAR BILINEARNY - + BICUBIC BIKUBICZNY - + GAUSSIAN GAUSSIAN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA BEZ AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Gra, którą próbujesz wczytać, wymaga dodatkowych plików z Switch'a, które zostaną zrzucone przed graniem.<br/><br/> Aby uzyskać więcej informacji na temat wyrzucania tych plików, odwiedź następującą stronę wiki:<a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'> Zrzut archiw systemu i udostępnionych czcionek z konsoli Nintendo Switch</a>. <br/><br/>Czy chcesz wrócić do listy gier? Kontynuacja emulacji może spowodować awarie, uszkodzone dane zapisu lub inne błędy. - + yuzu was unable to locate a Switch system archive. %1 yuzu nie był w stanie znaleźć archiwum systemu Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu nie był w stanie znaleźć archiwum systemu Switch. %1. %2 - + System Archive Not Found Archiwum systemu nie znalezione. - + System Archive Missing Brak archiwum systemowego - + yuzu was unable to locate the Switch shared fonts. %1 yuzu nie był w stanie zlokalizować czcionek Switch'a. %1 - + Shared Fonts Not Found Czcionki nie zostały znalezione - + Shared Font Missing Brak wspólnej czcionki - + Fatal Error Fatalny błąd - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu napotkał błąd, proszę zobaczyć log po więcej szczegółów. Aby uzyskać więcej informacji na temat uzyskiwania dostępu do pliku log, zobacz następującą stronę: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Jak przesłać plik log</a>?<br/><br/> Czy chcesz wrócić do listy gier? Kontynuacja emulacji może spowodować awarie, uszkodzone dane zapisu lub inne błędy. - + Fatal Error encountered Wystąpił błąd krytyczny - + Confirm Key Rederivation Potwierdź ponowną aktywacje klucza - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5087,37 +5253,37 @@ i opcjonalnie tworzyć kopie zapasowe. Spowoduje to usunięcie wygenerowanych automatycznie plików kluczy i ponowne uruchomienie modułu pochodnego klucza. - + Missing fuses Brakujące bezpieczniki - + - Missing BOOT0 - Brak BOOT0 - + - Missing BCPKG2-1-Normal-Main - Brak BCPKG2-1-Normal-Main - + - Missing PRODINFO - Brak PRODINFO - + Derivation Components Missing Brak komponentów wyprowadzania - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Brakuje elementów, które mogą uniemożliwić zakończenie wyprowadzania kluczy. <br>Postępuj zgodnie z <a href='https://yuzu-emu.org/help/quickstart/'>yuzu quickstart guide</a> aby zdobyć wszystkie swoje klucze i gry.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5126,39 +5292,39 @@ Zależnie od tego może potrwać do minuty na wydajność twojego systemu. - + Deriving Keys Wyprowadzanie kluczy... - + Select RomFS Dump Target Wybierz cel zrzutu RomFS - + Please select which RomFS you would like to dump. Proszę wybrać RomFS, jakie chcesz zrzucić. - + Are you sure you want to close yuzu? Czy na pewno chcesz zamknąć yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Czy na pewno chcesz zatrzymać emulację? Wszystkie niezapisane postępy zostaną utracone. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5170,38 +5336,38 @@ Czy chcesz to ominąć i mimo to wyjść? GRenderWindow - + OpenGL not available! OpenGL niedostępny! - + yuzu has not been compiled with OpenGL support. yuzu nie zostało skompilowane z obsługą OpenGL. - - + + Error while initializing OpenGL! Błąd podczas inicjowania OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Twoja karta graficzna może nie obsługiwać OpenGL lub nie masz najnowszych sterowników karty graficznej. - + Error while initializing OpenGL 4.6! Błąd podczas inicjowania OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Twoja karta graficzna może nie obsługiwać OpenGL 4.6 lub nie masz najnowszych sterowników karty graficznej.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Twoja karta graficzna może nie obsługiwać co najmniej jednego wymaganego rozszerzenia OpenGL. Upewnij się, że masz najnowsze sterowniki karty graficznej<br><br>GL Renderer:<br>%1<br><br>Nieobsługiwane rozszerzenia:<br>%2 @@ -5209,153 +5375,153 @@ Czy chcesz to ominąć i mimo to wyjść? GameList - + Favorite Ulubione - + Start Game Uruchom grę - + Start Game without Custom Configuration Uruchom grę bez niestandardowej konfiguracji - + Open Save Data Location Otwórz lokalizację zapisów - + Open Mod Data Location Otwórz lokalizację modyfikacji - + Open Transferable Pipeline Cache Otwórz Transferowalną Pamięć Podręczną Pipeline - + Remove Usuń - + Remove Installed Update Usuń zainstalowaną łatkę - + Remove All Installed DLC Usuń wszystkie zainstalowane DLC - + Remove Custom Configuration Usuń niestandardową konfigurację - + Remove OpenGL Pipeline Cache Usuń Pamięć Podręczną Pipeline OpenGL - + Remove Vulkan Pipeline Cache Usuń Pamięć Podręczną Pipeline Vulkan - + Remove All Pipeline Caches Usuń całą pamięć podręczną Pipeline - + Remove All Installed Contents Usuń całą zainstalowaną zawartość - + Dump RomFS Zrzuć RomFS - + Dump RomFS to SDMC Zrzuć RomFS do SDMC - + Copy Title ID to Clipboard Kopiuj identyfikator gry do schowka - + Navigate to GameDB entry Nawiguj do wpisu kompatybilności gry - + Properties Właściwości - + Scan Subfolders Skanuj podfoldery - + Remove Game Directory Usuń katalog gier - + ▲ Move Up ▲ Przenieś w górę - + ▼ Move Down ▼ Przenieś w dół - + Open Directory Location Otwórz lokalizacje katalogu - + Clear Wyczyść - + Name Nazwa gry - + Compatibility Kompatybilność - + Add-ons Dodatki - + File type Typ pliku - + Size Rozmiar @@ -5364,81 +5530,61 @@ Czy chcesz to ominąć i mimo to wyjść? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Perfekcyjnie - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - Funkcje gry są bezbłędne, bez żadnych zakłóceń audio i graficznych, wszystkie przetestowane funkcje działają zgodnie z przeznaczeniem bez -wszelkich potrzebnych obejść. - - - - Great - Świetnie - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - Funkcje gry z drobnymi usterkami graficznymi lub dźwiękowymi i można je odtwarzać od początku do końca. Może wymagać niektórych -obejść. - - Okay - W porządku - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - Funkcje gry z dużymi usterkami graficznymi lub dźwiękowymi, ale gra jest odtwarzana od początku do końca z użyciem -obejść. + Game can be played without issues. + - Bad - Zła + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Funkcje gry, ale z dużymi usterkami graficznymi lub dźwiękowymi. Nie można wykonać postępu w określonych obszarach z powodu problemów -nawet z obejściami. + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu Intro/Menu - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Gra jest całkowicie niemożliwa do zagrania z powodu poważnych usterków graficznych lub dźwiękowych. Nie można przejść ekran -startowy. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Nie uruchamia się - + The game crashes when attempting to startup. Ta gra się zawiesza przy próbie startu. - + Not Tested Nie testowane - + The game has not yet been tested. Ta gra nie została jeszcze przetestowana. @@ -5446,7 +5592,7 @@ startowy. GameListPlaceholder - + Double-click to add a new folder to the game list Kliknij podwójnie aby dodać folder do listy gier @@ -5459,12 +5605,12 @@ startowy. 1 z %n rezultatów%1 z %n rezultatów%1 z %n rezultatów%1 z %n rezultatów - + Filter: Filter: - + Enter pattern to filter Wpisz typ do filtra @@ -5540,12 +5686,12 @@ startowy. HostRoomWindow - + Error Błąd - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5554,11 +5700,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute Wycisz/Odcisz Audio + @@ -5580,112 +5727,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Zrób zrzut ekranu - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation Kontynuuj/Zatrzymaj Emulację - + Exit Fullscreen Wyłącz Pełny Ekran - + Exit yuzu Wyjdź z yuzu - + Fullscreen Pełny ekran - + Load File Załaduj plik... - + Load/Remove Amiibo Załaduj/Usuń Amiibo - + Restart Emulation Zrestartuj Emulację - + Stop Emulation Zatrzymaj Emulację - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5800,42 +5946,42 @@ Debug Message: Odśwież Lobby - + Password Required to Join Aby dołączyć, potrzebne jest hasło - + Password: Hasło: - - Room Name - Nazwa Pokoju - - - - Preferred Game - Preferowana Gra - - - - Host - Host - - - + Players Gracze - + + Room Name + Nazwa Pokoju + + + + Preferred Game + Preferowana Gra + + + + Host + Host + + + Refreshing Odświeżam - + Refresh List Odśwież listę @@ -5858,232 +6004,237 @@ Debug Message: &Ostatnie Pliki - + &Emulation &Emulacja - + &View &Widok - + &Reset Window Size &Zresetuj Rozmiar Okna - + &Debugging &Debugowanie - + Reset Window Size to &720p Zresetuj rozmiar okna do &720p - + Reset Window Size to 720p Zresetuj rozmiar okna do 720p - + Reset Window Size to &900p Zresetuj Rozmiar okna do &900p - + Reset Window Size to 900p Zresetuj Rozmiar okna do 900p - + Reset Window Size to &1080p Zresetuj rozmiar okna do &1080p - + Reset Window Size to 1080p Zresetuj rozmiar okna do 1080p - + + &Multiplayer + + + + &Tools &Narzędzia - + &TAS &TAS - + &Help &Pomoc - + &Install Files to NAND... &Zainstaluj pliki na NAND... - + L&oad File... Z&aładuj Plik... - + Load &Folder... Załaduj &Folder... - + E&xit &Wyjście - + &Pause &Pauza - + &Stop &Stop - + &Reinitialize keys... &Zainicjuj ponownie klucze... - + &About yuzu &O yuzu - + Single &Window Mode Tryb &Pojedyńczego Okna - + Con&figure... Kon&figuruj... - + Display D&ock Widget Headers Wyłącz Nagłówek Widżetu Docku - + Show &Filter Bar Pokaż &Pasek Filtrów - + Show &Status Bar Pokaż &Pasek Statusu - + Show Status Bar Pokaż pasek statusu - - - Browse Public Game Lobby - - - - - Create Room - Stwórz Pokój - - Leave Room - Wyjdź z Pokoju Multiplayer - - - - Direct Connect to Room + &Browse Public Game Lobby - - Show Current Room + + &Create Room + + + + + &Leave Room + &Direct Connect to Room + + + + + &Show Current Room + + + + F&ullscreen P&ełny Ekran - + &Restart &Restart - + Load/Remove &Amiibo... Załaduj/Usuń &Amiibo... - + &Report Compatibility &Zraportuj Kompatybilność - + Open &Mods Page Otwórz &Stronę z Modami - + Open &Quickstart Guide Otwórz &Poradnik Szybkiego Startu - + &FAQ &FAQ - + Open &yuzu Folder Otwórz &Folder yuzu - + &Capture Screenshot &Zrób Zdjęcie - + &Configure TAS... &Skonfiguruj TAS - + Configure C&urrent Game... Skonfiguruj O&becną Grę... - + &Start &Start - + &Reset &Zresetuj - + R&ecord N&agraj @@ -6148,46 +6299,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Połączony - - - - Not Connected Nie połączono - + + Connected + Połączony + + + + New Messages Received + Otrzymano nowe wiadomości + + + Error Błąd - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - Otrzymano nowe wiadomości - NetworkMessage @@ -6289,22 +6435,39 @@ They may have left the room. Możliwe, że opuścił/a pokój. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room Wyjdź z Pokoju Multiplayer - + You are about to close the room. Any network connections will be closed. Zamierzasz zamknąć pokój multiplayer. Wszystkie połączenia zostaną zamknięte. - + Disconnect Rozłącz - + You are about to leave the room. Any network connections will be closed. Wychodzisz z pokoju multiplayer. Wszystkie połączenia zostaną zamknięte. @@ -6312,7 +6475,7 @@ Możliwe, że opuścił/a pokój. NetworkMessage::ErrorManager - + Error Błąd @@ -6361,42 +6524,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game %1 nie gra w żadną grę - + %1 is playing %2 %1 gra w %2 - + Not playing a game Nie gra w żadną grę - + Installed SD Titles Zainstalowane tytuły SD - + Installed NAND Titles Zainstalowane tytuły NAND - + System Titles Tytuły systemu - + Add New Game Directory Dodaj nowy katalog gier - + Favorites Ulubione @@ -6426,7 +6589,7 @@ p, li { white-space: pre-wrap; } - + [not set] [nie ustawione] @@ -6441,10 +6604,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Oś %1%2 @@ -6458,9 +6621,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [nieznane] @@ -6625,15 +6788,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [niepoprawne] - - + + %1%2Hat %3 %1%2Drążek %3 @@ -6641,35 +6804,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Oś %3 - + %1%2Axis %3,%4,%5 %1%2Oś %3,%4,%5 - + %1%2Motion %3 %1%2Ruch %3 - - + + %1%2Button %3 %1%2Przycisk %3 - + [unused] [nieużywane] @@ -6710,11 +6873,124 @@ p, li { white-space: pre-wrap; } Dodatkowe - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Nazwa + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6818,6 +7094,7 @@ p, li { white-space: pre-wrap; } + Handheld Handheld @@ -6857,11 +7134,6 @@ p, li { white-space: pre-wrap; } Docked Zadokowany - - - Undocked - Niezadokowany - Vibration diff --git a/dist/languages/pt_BR.ts b/dist/languages/pt_BR.ts index 40a8a4c..97c2254 100644 --- a/dist/languages/pt_BR.ts +++ b/dist/languages/pt_BR.ts @@ -36,7 +36,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Site</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Código fonte</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contribuidores</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Licença</span></a></p></body></html> @@ -82,95 +82,97 @@ p, li { white-space: pre-wrap; } Room Window - + Janela da sala Send Chat Message - + Enviar mensagem no bate-papo Send Message - + Enviar mensagem - + Members - + Membros - + %1 has joined - + %1 entrou - + %1 has left - + %1 saiu - + %1 has been kicked - + %1 foi expulso(a) - + %1 has been banned - + %1 foi banido(a) - + %1 has been unbanned - + %1 foi desbanido(a) - + View Profile - - - - - - Block Player - - - - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - - - - - Kick - - - - - Ban - + Ver perfil + - Kick Player - + Block Player + Bloquear jogador + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + Quando bloqueia um jogador, você não receberá mais mensagens dele.<br><br>Você deseja mesmo bloquear %1? + + + + Kick + Expulsar + + + + Ban + Banir + + + + Kick Player + Expulsar jogador + + + Are you sure you would like to <b>kick</b> %1? - + Você deseja mesmo <b>expulsar</b> %1? - + Ban Player - + Banir jogador - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - + Você deseja mesmo <b>expulsar e banir</b> %1? + +Isto banirá tanto o nome de usuário do fórum como o endereço IP. @@ -178,22 +180,22 @@ This would ban both their forum username and their IP address. Room Window - + Janela da sala Room Description - + Descrição da sala Moderation... - + Moderação... Leave Room - + Sair da sala @@ -206,11 +208,11 @@ This would ban both their forum username and their IP address. Disconnected - + Desconectado - %1 (%2/%3 members) - connected + %1 - %2 (%3/%4 members) - connected @@ -224,6 +226,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Informar compatibilidade de jogo @@ -233,92 +240,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Ao enviar um caso de teste à </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">lista de compatibilidade do yuzu</span></a><span style=" font-size:10pt;">, as seguintes informações serão recolhidas e exibidas no site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informações de hardware (CPU / GPU / sistema operacional)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qual versão do yuzu você está usando</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A conta do yuzu conectada</li></ul></body></html> - - Perfect - Perfeito + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>O jogo funciona perfeitamente, sem falhas no áudio ou nos gráficos.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Ótimo + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>O jogo funciona com leves falhas gráficas ou de áudio e é jogável do início ao fim. Pode exigir algumas soluções alternativas.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Razoável + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>O jogo funciona com grandes falhas gráficas ou de áudio, mas é jogável do início ao fim com o uso de soluções alternativas.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Ruim + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>O jogo funciona, só que com problemas significativos nos gráficos ou no áudio. Não é possível progredir em áreas específicas por conta de tais problemas, mesmo com soluções alternativas.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Intro/menu + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>É impossível jogar o jogo devido a grandes problemas nos gráficos ou no áudio. Não é possível passar da tela inicial do jogo.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Não inicia + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>O jogo trava ou se encerra abruptamente ao se tentar iniciá-lo.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Sem considerar velocidade e desempenho, quão bem o jogo se comporta, do início ao fim, nesta versão do yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Agradecemos pelo seu relatório! - + Submitting Enviando - + Communication error Erro de comunicação - + An error occurred while sending the Testcase Um erro ocorreu ao enviar o caso de teste - + Next Próximo @@ -339,7 +381,7 @@ This would ban both their forum username and their IP address. Output Device - + Dispositivo de saída @@ -378,37 +420,37 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + Configurar câmera infravermelha Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + Selecione de onde vem a imagem da câmera emulada. Pode ser uma câmera virtual ou uma câmera real. Camera Image Source: - + Origem da imagem da câmera: Input device: - + Dispositivo de entrada: Preview - + Prévia Resolution: 320*240 - + Resolução: 320*240 Click to preview - + Clique para pré-visualizar @@ -416,7 +458,7 @@ This would ban both their forum username and their IP address. Restaurar padrões - + Auto Automático @@ -769,200 +811,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger Depurador - + Enable GDB Stub Ativar GDB stub - + Port: Porta: - + Logging Registros de depuração - + Global Log Filter Filtro global de registros - + Show Log in Console Mostrar registro no console - + Open Log Location Abrir local dos registros - + When checked, the max size of the log increases from 100 MB to 1 GB Quando ativado, o tamanho máximo do arquivo de registro aumenta de 100 MB para 1 GB - + Enable Extended Logging** Ativar registros avançados** - + Homebrew Homebrew - + Arguments String Linha de argumentos - + Graphics Gráficos - + When checked, the graphics API enters a slower debugging mode Quando ativado, a API gráfica entra em um modo de depuração mais lento. - + Enable Graphics Debugging Ativar depuração de gráficos - + When checked, it enables Nsight Aftermath crash dumps Quando ativado, ativa a extração de registros de travamento do Nsight Aftermath - + Enable Nsight Aftermath Ativar Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Se selecionado, extrai todos os shaders originários do cache do disco ou do jogo conforme encontrá-los. - + Dump Game Shaders Descarregar shaders do jogo - + When checked, it will dump all the macro programs of the GPU Quando marcada, essa opção irá extrair todos os macro programas da GPU - + Dump Maxwell Macros Extrair macros Maxwell - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Quando ativado, desativa o macro compilador Just In Time. Ativar isto faz os jogos rodarem mais lentamente. - + Disable Macro JIT Desativar macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Quando ativado, o yuzu registrará estatísticas sobre o cache de pipeline compilado - + Enable Shader Feedback Ativar Feedback de Shaders - + When checked, it executes shaders without loop logic changes Quando ativado, executa shaders sem mudanças de lógica de loop - + Disable Loop safety checks Desativar verificação de segurança de loops - + Debugging Depuração - - Enable FS Access Log - Ativar acesso de registro FS - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Ativar serviços de relatório detalhado** - + + Enable FS Access Log + Ativar acesso de registro FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avançado - + Kiosk (Quest) Mode Modo quiosque (Quest) - + Enable CPU Debugging Ativar depuração de CPU - + Enable Debug Asserts Ativar asserções de depuração - + Enable Auto-Stub** Ativar auto-esboço** - + Enable All Controller Types Ativar todos os tipos de controles - + Disable Web Applet Desativar o applet da web - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Isto será restaurado automaticamente assim que o yuzu for fechado. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1332,193 +1409,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings Configurações gráficas - + Use disk pipeline cache Usar cache de pipeline em disco - + Use asynchronous GPU emulation Usar emulação assíncrona da GPU - + Accelerate ASTC texture decoding Acelerar a decodificação de textura ASTC - + NVDEC emulation: Emulação NVDEC: - + No Video Output Sem saída de vídeo - + CPU Video Decoding Decodificação de vídeo pela CPU - + GPU Video Decoding (Default) Decodificação de vídeo pela GPU (Padrão) - + Fullscreen Mode: Modo de tela cheia: - + Borderless Windowed Janela em tela cheia - + Exclusive Fullscreen Tela cheia exclusiva - + Aspect Ratio: Proporção de tela: - + Default (16:9) Padrão (16:9) - + Force 4:3 Forçar 4:3 - + Force 21:9 Forçar 21:9 - + + Force 16:10 + + + + Stretch to Window Esticar para a janela - + Resolution: Resolução: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: Filtro de adaptação de janela: - + Nearest Neighbor Vizinho mais próximo - + Bilinear Bilinear - + Bicubic Bicúbico - + Gaussian Gaussiano - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (somente Vulkan) - + Anti-Aliasing Method: Método de Anti-Aliasing - + None Nenhum - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Usar cor de fundo global - + Set background color: Configurar cor de fundo: - + Background Color: Cor de fundo: @@ -1527,6 +1629,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM (Shaders Assembly, apenas NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1581,37 +1689,47 @@ This would ban both their forum username and their IP address. Usar tempo de resposta rápido da GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtragem anisotrópica: - + Automatic Automático - + Default Padrão - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2103,7 +2221,7 @@ This would ban both their forum username and their IP address. - + Left Stick Analógico esquerdo @@ -2197,14 +2315,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2223,7 +2341,7 @@ This would ban both their forum username and their IP address. - + Plus Mais @@ -2236,15 +2354,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2301,231 +2419,236 @@ This would ban both their forum username and their IP address. - + Right Stick Analógico direito - - - - + + + + Clear Limpar - - - - - + + + + + [not set] [não definido] - - - Toggle button - Alternar pressionamento do botão - - - - + + Invert button Inverter botão - - + + + Toggle button + Alternar pressionamento do botão + + + + Invert axis Inverter eixo - - - + + + Set threshold Definir limite - - + + Choose a value between 0% and 100% Escolha um valor entre 0% e 100% - + + Toggle axis + + + + Set gyro threshold Definir limite do giroscópio - + Map Analog Stick Mapear analógico - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Após pressionar OK, mova o seu direcional analógico primeiro horizontalmente e depois verticalmente. Para inverter os eixos, mova seu analógico primeiro verticalmente e depois horizontalmente. - + Center axis Eixo central - - + + Deadzone: %1% Zona morta: %1% - - + + Modifier Range: %1% Alcance de modificador: %1% - - + + Pro Controller Pro Controller - + Dual Joycons Par de Joycons - + Left Joycon Joycon Esquerdo - + Right Joycon Joycon Direito - + Handheld Portátil - + GameCube Controller Controle de GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Controle NES - + SNES Controller Controle SNES - + N64 Controller Controle N64 - + Sega Genesis Mega Drive - + Start / Pause Iniciar / Pausar - + Z Z - + Control Stick Direcional de controle - + C-Stick C-Stick - + Shake! Balance! - + [waiting] [esperando] - + New Profile Novo perfil - + Enter a profile name: Insira um nome para o perfil: - - + + Create Input Profile Criar perfil de controle - + The given profile name is not valid! O nome de perfil inserido não é válido! - + Failed to create the input profile "%1" Falha ao criar o perfil de controle "%1" - + Delete Input Profile Excluir perfil de controle - + Failed to delete the input profile "%1" Falha ao excluir o perfil de controle "%1" - + Load Input Profile Carregar perfil de controle - + Failed to load the input profile "%1" Falha ao carregar o perfil de controle "%1" - + Save Input Profile Salvar perfil de controle - + Failed to save the input profile "%1" Falha ao salvar o perfil de controle "%1" @@ -2780,42 +2903,42 @@ Para inverter os eixos, mova seu analógico primeiro verticalmente e depois hori Desenvolvedor - + Add-Ons Adicionais - + General Geral - + System Sistema - + CPU CPU - + Graphics Gráficos - + Adv. Graphics Gráf. avançados - + Audio Áudio - + Properties Propriedades @@ -2871,37 +2994,37 @@ Para inverter os eixos, mova seu analógico primeiro verticalmente e depois hori Usuário atual - + Username Nome de usuário - + Set Image Definir imagem - + Add Adicionar - + Rename Renomear - + Remove Excluir - + Profile management is available only when game is not running. Esta tela só fica disponível apenas quando não houver nenhum jogo em execução. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2909,96 +3032,105 @@ Para inverter os eixos, mova seu analógico primeiro verticalmente e depois hori %2 - + Enter Username Escreva o nome de usuário - + Users Usuários - + Enter a username for the new user: Digite o nome do novo usuário: - + Enter a new username: Digite um novo nome de usuário: - - Confirm Delete - Confirmar exclusão - - - - You are about to delete user with name "%1". Are you sure? - Você está prestes a excluir o usuário "%1". Tem certeza? - - - + Select User Image Selecione a imagem do usuário - + JPEG Images (*.jpg *.jpeg) Imagens JPEG (*.jpg *.jpeg) - + Error deleting image Erro ao excluir a imagem - + Error occurred attempting to overwrite previous image at: %1. Ocorreu um erro ao tentar substituir a imagem anterior em: %1. - + Error deleting file Erro ao excluir arquivo - + Unable to delete existing file: %1. Não foi possível excluir o arquivo existente: %1. - + Error creating user image directory Erro ao criar a pasta de imagens do usuário - + Unable to create directory %1 for storing user images. Não foi possível criar a pasta %1 para armazenar as imagens do usuário. - + Error copying user image Erro ao copiar a imagem do usuário - + Unable to copy image from %1 to %2 Não foi possível copiar a imagem de %1 para %2 - + Error resizing user image Erro no redimensionamento da imagem do usuário - + Unable to resize image Não foi possível redimensionar a imagem + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Confirmar exclusão + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3527,47 +3659,47 @@ Para inverter os eixos, mova seu analógico primeiro verticalmente e depois hori <html><head/><body><p>Lê entradas de controle a partir de scripts no mesmo formato que TAS-nx. <br/>Para uma explicação mais detalhada, por favor consulte a <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">página de ajuda</span></a> no website do yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Para checar que atalhos controlam rodar/gravar, por favor refira-se às Teclas de atalhos (Configurar -> Geral -> Teclas de atalhos) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ATENÇÃO: Este é um recurso experimental. <br/>Não irá rodar os scrips em quadros perfeitos com o atual, imperfeito método de sincronização. - + Settings Configurações - + Enable TAS features Ativar funcionalidades TAS - + Loop script Repetir script em loop - + Pause execution during loads Pausar execução durante carregamentos - + Script Directory Diretório do script - + Path Caminho - + ... ... @@ -3819,56 +3951,71 @@ Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabe + Show Compatibility List + + + + Show Add-Ons Column Mostrar coluna de adicionais - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: Tamanho do ícone do jogo: - + Folder Icon Size: Tamanho do ícone da pasta: - + Row 1 Text: Texto da 1ª linha: - + Row 2 Text: Texto da 2ª linha: - + Screenshots Capturas de tela - + Ask Where To Save Screenshots (Windows Only) Perguntar onde salvar capturas de tela (apenas Windows) - + Screenshots Path: Pasta para capturas de tela: - + ... ... - + Select Screenshots Path... Selecione a pasta de capturas de tela... - + <System> <System> @@ -3977,7 +4124,7 @@ Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabe - + Verify Verificar @@ -4064,7 +4211,7 @@ Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabe - + Unspecified Não especificado @@ -4079,17 +4226,36 @@ Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabe O token não foi verificado. A alteração no seu token não foi salva. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Verificando... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Falha na verificação + + + Verification failed Falha na verificação - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Falha na verificação. Verifique se o seu token foi inserido corretamente e se a sua conexão à internet está funcionando. @@ -4158,12 +4324,12 @@ Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabe DirectConnectWindow - + Connecting - + Connect @@ -4171,488 +4337,491 @@ Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabe GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Dados anônimos são recolhidos</a> para ajudar a melhorar o yuzu. <br/><br/>Gostaria de compartilhar os seus dados de uso conosco? - + Telemetry Telemetria - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Carregando applet web... - - + + Disable Web Applet Desativar o applet da web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) A desativação do applet da web pode causar comportamento inesperado e deve apenas ser usada com Super Mario 3D All-Stars. Você deseja mesmo desativar o applet da web? (Ele pode ser reativado nas configurações de depuração.) - + The amount of shaders currently being built A quantidade de shaders sendo construídos - + The current selected resolution scaling multiplier. O atualmente multiplicador de escala de resolução selecionado. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocidade atual de emulação. Valores maiores ou menores que 100% indicam que a emulação está rodando mais rápida ou lentamente que em um Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Quantos quadros por segundo o jogo está exibindo atualmente. Isto irá variar de jogo para jogo e cena para cena. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tempo que leva para emular um quadro do Switch, sem considerar o limitador de taxa de quadros ou a sincronização vertical. Um valor menor ou igual a 16.67 ms indica que a emulação está em velocidade plena. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Limpar arquivos recentes - + &Continue &Continuar - + &Pause &Pausar - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu está rodando um jogo - + Warning Outdated Game Format Aviso - formato de jogo desatualizado - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Você está usando neste jogo o formato de ROM desconstruída e extraída em uma pasta, que é um formato desatualizado que foi substituído por outros, como NCA, NAX, XCI ou NSP. Pastas desconstruídas de ROMs não possuem ícones, metadados e suporte a atualizações.<br><br>Para saber mais sobre os vários formatos de ROMs de Switch compatíveis com o yuzu, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>confira a nossa wiki</a>. Esta mensagem não será exibida novamente. - - + + Error while loading ROM! Erro ao carregar a ROM! - + The ROM format is not supported. O formato da ROM não é suportado. - + An error occurred initializing the video core. Ocorreu um erro ao inicializar o núcleo de vídeo. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu encontrou um erro enquanto rodando o núcleo de vídeo. Normalmente isto é causado por drivers de GPU desatualizados, incluindo integrados. Por favor veja o registro para mais detalhes. Para mais informações em acesso ao registro por favor veja a seguinte página: <a href='https://yuzu-emu.org/help/reference/log-files/'>Como fazer envio de arquivo de registro</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Erro ao carregar a ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Por favor, siga <a href='https://yuzu-emu.org/help/quickstart/'>o guia de início rápido</a> para reextrair os seus arquivos.<br>Você pode consultar a wiki do yuzu</a> ou o Discord do yuzu</a> para obter ajuda. - + An unknown error occurred. Please see the log for more details. Ocorreu um erro desconhecido. Consulte o registro para mais detalhes. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Dados de jogos salvos - + Mod Data Dados de mods - + Error Opening %1 Folder Erro ao abrir a pasta %1 - - + + Folder does not exist! A pasta não existe! - + Error Opening Transferable Shader Cache Erro ao abrir o cache de shaders transferível - + Failed to create the shader cache directory for this title. Falha ao criar o diretório de cache de shaders para este título. - - Contents - Conteúdo + + Error Removing Contents + - - Update - Atualização + + Error Removing Update + - - DLC - DLC + + Error Removing DLC + - + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + Remove Entry Remover item - - Remove Installed Game %1? - Remover o jogo instalado %1? - - - - - - - - + + + + + + Successfully Removed Removido com sucesso - + Successfully removed the installed base game. O jogo base foi removido com sucesso. - - - - Error Removing %1 - Erro ao remover %1 - - - + The base game is not installed in the NAND and cannot be removed. O jogo base não está instalado na NAND e não pode ser removido. - + Successfully removed the installed update. A atualização instalada foi removida com sucesso. - + There is no update installed for this title. Não há nenhuma atualização instalada para este título. - + There are no DLC installed for this title. Não há nenhum DLC instalado para este título. - + Successfully removed %1 installed DLC. %1 DLC(s) instalados foram removidos com sucesso. - + Delete OpenGL Transferable Shader Cache? Apagar o cache de shaders transferível do OpenGL? - + Delete Vulkan Transferable Shader Cache? Apagar o cache de shaders transferível do Vulkan? - + Delete All Transferable Shader Caches? Apagar todos os caches de shaders transferíveis? - + Remove Custom Game Configuration? Remover configurações customizadas do jogo? - + Remove File Remover arquivo - - + + Error Removing Transferable Shader Cache Erro ao remover cache de shaders transferível - - + + A shader cache for this title does not exist. Não existe um cache de shaders para este título. - + Successfully removed the transferable shader cache. O cache de shaders transferível foi removido com sucesso. - + Failed to remove the transferable shader cache. Falha ao remover o cache de shaders transferível. - - + + Error Removing Transferable Shader Caches Erro ao remover os caches de shaders transferíveis - + Successfully removed the transferable shader caches. Os caches de shaders transferíveis foram removidos com sucesso. - + Failed to remove the transferable shader cache directory. Falha ao remover o diretório do cache de shaders transferível. - - + + Error Removing Custom Configuration Erro ao remover as configurações customizadas do jogo. - + A custom configuration for this title does not exist. Não há uma configuração customizada para este título. - + Successfully removed the custom game configuration. As configurações customizadas do jogo foram removidas com sucesso. - + Failed to remove the custom game configuration. Falha ao remover as configurações customizadas do jogo. - - + + RomFS Extraction Failed! Falha ao extrair RomFS! - + There was an error copying the RomFS files or the user cancelled the operation. Houve um erro ao copiar os arquivos RomFS ou o usuário cancelou a operação. - + Full Extração completa - + Skeleton Apenas estrutura - + Select RomFS Dump Mode Selecione o modo de extração do RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Selecione a forma como você gostaria que o RomFS seja extraído.<br>"Extração completa" copiará todos os arquivos para a nova pasta, enquanto que <br>"Apenas estrutura" criará apenas a estrutura de pastas. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Não há espaço suficiente em %1 para extrair o RomFS. Por favor abra espaço ou selecione um diretório diferente em Emulação > Configurar > Sistema > Sistema de arquivos > Extrair raiz - + Extracting RomFS... Extraindo RomFS... - - + + Cancel Cancelar - + RomFS Extraction Succeeded! Extração do RomFS concluida! - + The operation completed successfully. A operação foi concluída com sucesso. - + Error Opening %1 Erro ao abrir %1 - + Select Directory Selecionar pasta - + Properties Propriedades - + The game properties could not be loaded. As propriedades do jogo não puderam ser carregadas. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Executável do Switch (%1);;Todos os arquivos (*.*) - + Load File Carregar arquivo - + Open Extracted ROM Directory Abrir pasta da ROM extraída - + Invalid Directory Selected Pasta inválida selecionada - + The directory you have selected does not contain a 'main' file. A pasta que você selecionou não contém um arquivo 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Arquivo de Switch instalável (*.nca *.nsp *.xci);; Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Instalar arquivos - + %n file(s) remaining %n arquivo restante%n arquivo(s) restante(s)%n arquivo(s) restante(s) - + Installing file "%1"... Instalando arquivo "%1"... - - + + Install Results Resultados da instalação - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Para evitar possíveis conflitos, desencorajamos que os usuários instalem os jogos base na NAND. Por favor, use esse recurso apenas para instalar atualizações e DLCs. - + %n file(s) were newly installed %n arquivo(s) instalado(s) @@ -4661,7 +4830,7 @@ Por favor, use esse recurso apenas para instalar atualizações e DLCs. - + %n file(s) were overwritten %n arquivo(s) sobrescrito(s) @@ -4670,7 +4839,7 @@ Por favor, use esse recurso apenas para instalar atualizações e DLCs. - + %n file(s) failed to install %n arquivo(s) não instalado(s) @@ -4679,411 +4848,410 @@ Por favor, use esse recurso apenas para instalar atualizações e DLCs. - + System Application Aplicativo do sistema - + System Archive Arquivo do sistema - + System Application Update Atualização de aplicativo do sistema - + Firmware Package (Type A) Pacote de firmware (tipo A) - + Firmware Package (Type B) Pacote de firmware (tipo B) - + Game Jogo - + Game Update Atualização de jogo - + Game DLC DLC de jogo - + Delta Title Título delta - + Select NCA Install Type... Selecione o tipo de instalação do NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Selecione o tipo de título como o qual você gostaria de instalar este NCA: (Na maioria dos casos, o padrão 'Jogo' serve bem.) - + Failed to Install Falha ao instalar - + The title type you selected for the NCA is invalid. O tipo de título que você selecionou para o NCA é inválido. - + File not found Arquivo não encontrado - + File "%1" not found Arquivo "%1" não encontrado - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Conta do yuzu faltando - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Para enviar um caso de teste de compatibilidade de jogo, você precisa entrar com a sua conta do yuzu.<br><br/>Para isso, vá para Emulação &gt; Configurar... &gt; Rede. - + Error opening URL Erro ao abrir URL - + Unable to open the URL "%1". Não foi possível abrir o URL "%1". - + TAS Recording Gravando TAS - + Overwrite file of player 1? Sobrescrever arquivo do jogador 1? - + Invalid config detected Configuração inválida detectada - + Handheld controller can't be used on docked mode. Pro controller will be selected. O controle portátil não pode ser usado no modo encaixado na base. O Pro Controller será selecionado. - - - Error - Erro - - - - - The current game is not looking for amiibos - O jogo atual não está procurando amiibos - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed O amiibo atual foi removido - + + Error + Erro + + + + + The current game is not looking for amiibos + O jogo atual não está procurando amiibos + + + Amiibo File (%1);; All Files (*.*) Arquivo Amiibo (%1);; Todos os arquivos (*.*) - + Load Amiibo Carregar Amiibo - - Error opening Amiibo data file - Erro ao abrir arquivo de dados do Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Não foi possível abrir o arquivo de Amiibo "%1" para leitura. - - - - Error reading Amiibo data file - Erro ao ler arquivo de dados de Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Não foi possível ler completamente os dados do Amiibo. O yuzu esperava ler %1 bytes, mas foi capaz de ler apenas %2 bytes. - - - + Error loading Amiibo data Erro ao carregar dados do Amiibo - - Unable to load Amiibo data. - Não foi possível carregar os dados do Amiibo. + + The selected file is not a valid amiibo + - + + The selected file is already on use + + + + + An unknown error occurred + + + + Capture Screenshot Capturar tela - + PNG Image (*.png) Imagem PNG (*.png) - + TAS state: Running %1/%2 Situação TAS: Rodando %1%2 - + TAS state: Recording %1 Situação TAS: Gravando %1 - + TAS state: Idle %1/%2 Situação TAS: Repouso %1%2 - + TAS State: Invalid Situação TAS: Inválido - + &Stop Running &Parar de rodar - + &Start &Iniciar - + Stop R&ecording Parar G&ravação - + R&ecord G&ravação - + Building: %n shader(s) Compilando: %n shader(s)Compilando: %n shader(s)Compilando: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Escala: %1x - + Speed: %1% / %2% Velocidade: %1% / %2% - + Speed: %1% Velocidade: %1% - + Game: %1 FPS (Unlocked) Jogo: %1 FPS (Desbloqueado) - + Game: %1 FPS Jogo: %1 FPS - + Frame: %1 ms Quadro: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU ALTA - + GPU EXTREME GPU EXTREMA - + GPU ERROR ERRO DE GPU - + DOCKED - + HANDHELD - + NEAREST VIZINHO - - + + BILINEAR BILINEAR - + BICUBIC BICÚBICO - + GAUSSIAN GAUSSIANO - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA Sem AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. O jogo que você está tentando carregar precisa que arquivos adicionais do seu Switch sejam extraídos antes de jogá-lo.<br/><br/>Para saber mais sobre como extrair esses arquivos, visite a seguinte página da wiki: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Extraindo arquivos de sistema e fontes compartilhadas de um Switch</a>.<br/><br/> Gostaria de voltar para a lista de jogos? Continuar com a emulação pode resultar em travamentos, dados salvos corrompidos ou outros problemas. - + yuzu was unable to locate a Switch system archive. %1 O yuzu não foi capaz de encontrar um arquivo de sistema do Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 O yuzu não foi capaz de encontrar um arquivo de sistema do Switch. %1. %2 - + System Archive Not Found Arquivo do sistema não encontrado - + System Archive Missing Arquivo de sistema faltando - + yuzu was unable to locate the Switch shared fonts. %1 O yuzu não foi capaz de encontrar as fontes compartilhadas do Switch. %1 - + Shared Fonts Not Found Fontes compartilhadas não encontradas - + Shared Font Missing Fonte compartilhada faltando - + Fatal Error Erro fatal - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. O yuzu encontrou um erro fatal. Consulte o registro para mais detalhes. Para mais informações sobre como acessar o registro, consulte a seguinte página: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Como enviar o arquivo de registro</a>.<br/><br/>Gostaria de voltar para a lista de jogos? Continuar com a emulação pode resultar em travamentos, dados salvos corrompidos ou outros problemas. - + Fatal Error encountered Erro fatal encontrado - + Confirm Key Rederivation Confirmar rederivação de chave - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5100,37 +5268,37 @@ e opcionalmente faça cópias de segurança. Isto excluirá o seus arquivos de chaves geradas automaticamente, e reexecutar o módulo de derivação de chaves. - + Missing fuses Faltando fusíveis - + - Missing BOOT0 - Faltando BOOT0 - + - Missing BCPKG2-1-Normal-Main - Faltando BCPKG2-1-Normal-Main - + - Missing PRODINFO - Faltando PRODINFO - + Derivation Components Missing Faltando componentes de derivação - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Chaves de encriptação faltando. <br>Por favor, siga <a href='https://yuzu-emu.org/help/quickstart/'>o guia de início rápido</a> para extrair suas chaves, firmware e jogos. <br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5139,39 +5307,39 @@ Isto pode demorar até um minuto, dependendo do desempenho do seu sistema. - + Deriving Keys Derivando chaves - + Select RomFS Dump Target Selecionar alvo de extração do RomFS - + Please select which RomFS you would like to dump. Selecione qual RomFS você quer extrair. - + Are you sure you want to close yuzu? Você deseja mesmo fechar o yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Deseja mesmo parar a emulação? Qualquer progresso não salvo será perdido. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5183,38 +5351,38 @@ Deseja ignorar isso e sair mesmo assim? GRenderWindow - + OpenGL not available! OpenGL não disponível! - + yuzu has not been compiled with OpenGL support. O yuzu não foi compilado com suporte para OpenGL. - - + + Error while initializing OpenGL! Erro ao inicializar o OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Sua GPU pode não suportar OpenGL, ou você não possui o driver gráfico mais recente. - + Error while initializing OpenGL 4.6! Erro ao inicializar o OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Sua GPU pode não suportar o OpenGL 4.6, ou você não possui os drivers gráficos mais recentes.<br><br>Renderizador GL:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Sua GPU pode não suportar uma ou mais extensões necessárias do OpenGL. Verifique se você possui a última versão dos drivers gráficos.<br><br>Renderizador GL:<br>%1<br><br>Extensões não suportadas:<br>%2 @@ -5222,153 +5390,153 @@ Deseja ignorar isso e sair mesmo assim? GameList - + Favorite Favorito - + Start Game Iniciar jogo - + Start Game without Custom Configuration Iniciar jogo sem configuração personalizada - + Open Save Data Location Abrir local dos jogos salvos - + Open Mod Data Location Abrir local dos dados de mods - + Open Transferable Pipeline Cache Abrir cache de pipeline transferível - + Remove Remover - + Remove Installed Update Remover atualização instalada - + Remove All Installed DLC Remover todos os DLCs instalados - + Remove Custom Configuration Remover configuração customizada - + Remove OpenGL Pipeline Cache Remover cache de pipeline do OpenGL - + Remove Vulkan Pipeline Cache Remover cache de pipeline do Vulkan - + Remove All Pipeline Caches Remover todos os caches de pipeline - + Remove All Installed Contents Remover todo o conteúdo instalado - + Dump RomFS Extrair RomFS - + Dump RomFS to SDMC Extrair RomFS para SDMC - + Copy Title ID to Clipboard Copiar ID do título para a área de transferência - + Navigate to GameDB entry Abrir artigo do jogo no GameDB - + Properties Propriedades - + Scan Subfolders Examinar subpastas - + Remove Game Directory Remover pasta de jogo - + ▲ Move Up ▲ Mover para cima - + ▼ Move Down ▼ Mover para baixo - + Open Directory Location Abrir local da pasta - + Clear Limpar - + Name Nome - + Compatibility Compatibilidade - + Add-ons Adicionais - + File type Tipo de arquivo - + Size Tamanho @@ -5377,81 +5545,61 @@ Deseja ignorar isso e sair mesmo assim? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Perfeito - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - O jogo funciona impecavelmente, sem falhas gráficas ou de áudio. Todas as funcionalidades testadas -do jogo funcionam como deveriam, sem nenhuma solução alternativa necessária. - - - - Great - Ótimo - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - O jogo funciona com leves falhas gráficas ou de áudio e é jogável do início ao fim. Pode exigir -algumas soluções alternativas. - - Okay - Razoável - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - O jogo funciona com graves falhas gráficas ou de áudio, mas é jogável do início ao fim -com o uso de soluções alternativas. + Game can be played without issues. + - Bad - Ruim + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - O jogo funciona, só que com problemas significativos nos gráficos ou no áudio. Não é possível progredir em áreas -específicas por conta de tais problemas, mesmo com soluções alternativas. + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu Intro/menu - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - É impossível jogar o jogo devido a grandes problemas nos gráficos ou no áudio. Não é possível passar da -tela inicial do jogo. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Não inicia - + The game crashes when attempting to startup. O jogo trava ou se encerra abruptamente ao se tentar iniciá-lo. - + Not Tested Não testado - + The game has not yet been tested. Esse jogo ainda não foi testado. @@ -5459,7 +5607,7 @@ tela inicial do jogo. GameListPlaceholder - + Double-click to add a new folder to the game list Clique duas vezes para adicionar uma pasta à lista de jogos @@ -5472,12 +5620,12 @@ tela inicial do jogo. %1 de %n resultado(s)%1 de %n resultado(s)%1 de %n resultado(s) - + Filter: Filtro: - + Enter pattern to filter Digite o padrão para filtrar @@ -5527,7 +5675,7 @@ tela inicial do jogo. Room Description - + Descrição da sala @@ -5553,12 +5701,12 @@ tela inicial do jogo. HostRoomWindow - + Error Erro - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5567,11 +5715,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5593,112 +5742,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Capturar tela - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Tela cheia - + Load File Carregar arquivo - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5813,42 +5961,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Jogadores - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5871,232 +6019,237 @@ Debug Message: &Arquivos recentes - + &Emulation &Emulação - + &View &Exibir - + &Reset Window Size &Restaurar tamanho da janela - + &Debugging &Depurar - + Reset Window Size to &720p Restaurar tamanho da janela para &720p - + Reset Window Size to 720p Restaurar tamanho da janela para 720p - + Reset Window Size to &900p Restaurar tamanho da janela para &900p - + Reset Window Size to 900p Restaurar tamanho da janela para 900p - + Reset Window Size to &1080p Restaurar tamanho da janela para &1080p - + Reset Window Size to 1080p Restaurar tamanho da janela para 1080p - + + &Multiplayer + + + + &Tools &Ferramentas - + &TAS &TAS - + &Help &Ajuda - + &Install Files to NAND... &Instalar arquivos para NAND... - + L&oad File... &Carregar arquivo... - + Load &Folder... Carregar &pasta... - + E&xit S&air - + &Pause &Pausar - + &Stop &Parar - + &Reinitialize keys... &Reinicializar chaves... - + &About yuzu &Sobre o yuzu - + Single &Window Mode Modo de &janela única - + Con&figure... Con&figurar... - + Display D&ock Widget Headers Exibir barra de títul&os de widgets afixados - + Show &Filter Bar Exibir barra de &filtro - + Show &Status Bar Exibir barra de &status - + Show Status Bar Exibir barra de status - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room + &Browse Public Game Lobby - - Direct Connect to Room + + &Create Room - - Show Current Room + + &Leave Room + &Direct Connect to Room + + + + + &Show Current Room + + + + F&ullscreen &Tela cheia - + &Restart &Reiniciar - + Load/Remove &Amiibo... Carregar/Remover &Amiibo... - + &Report Compatibility &Reportar compatibilidade - + Open &Mods Page Abrir página de &mods - + Open &Quickstart Guide Abrir &guia de início rápido - + &FAQ &Perguntas frequentes - + Open &yuzu Folder Abrir pasta do &yuzu - + &Capture Screenshot &Captura de tela - + &Configure TAS... &Configurar TAS - + Configure C&urrent Game... Configurar jogo &atual.. - + &Start &Iniciar - + &Reset &Restaurar - + R&ecord G&ravar @@ -6161,46 +6314,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Conectado - - - - Not Connected - + + Connected + Conectado + + + + New Messages Received + + + + Error Erro - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6301,22 +6449,39 @@ They may have left the room. - - Leave Room + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. - + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + + Leave Room + Sair da sala + + + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6324,7 +6489,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Erro @@ -6373,42 +6538,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 is playing %2 - + Not playing a game - + Installed SD Titles Títulos instalados no SD - + Installed NAND Titles Títulos instalados na NAND - + System Titles Títulos do sistema - + Add New Game Directory Adicionar pasta de jogos - + Favorites Favoritos @@ -6438,7 +6603,7 @@ p, li { white-space: pre-wrap; } - + [not set] [não definido] @@ -6453,10 +6618,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Eixo %1%2 @@ -6470,9 +6635,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [desconhecido] @@ -6637,15 +6802,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [inválido] - - + + %1%2Hat %3 %1%2Direcional %3 @@ -6653,35 +6818,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Eixo %3 - + %1%2Axis %3,%4,%5 %1%2Eixo %3,%4,%5 - + %1%2Motion %3 %1%2Movimentação %3 - - + + %1%2Button %3 %1%2Botão %3 - + [unused] [não utilizado] @@ -6722,11 +6887,124 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Nome + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6830,6 +7108,7 @@ p, li { white-space: pre-wrap; } + Handheld Portátil @@ -6869,11 +7148,6 @@ p, li { white-space: pre-wrap; } Docked Na base - - - Undocked - Fora da base - Vibration diff --git a/dist/languages/pt_PT.ts b/dist/languages/pt_PT.ts index 74d7656..eeff1a9 100644 --- a/dist/languages/pt_PT.ts +++ b/dist/languages/pt_PT.ts @@ -36,7 +36,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + Site | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Código fonte | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contribuidores</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Licença</span></a></p></body></html> @@ -82,95 +82,97 @@ p, li { white-space: pre-wrap; } Room Window - + Janela da sala Send Chat Message - + Enviar mensagem Send Message - + Enviar mensagem - + Members - + Membros - + %1 has joined - + %1 entrou - + %1 has left - + %1 saiu - + %1 has been kicked - + %1 foi expulso(a) - + %1 has been banned - + %1 foi banido(a) - + %1 has been unbanned - + %1 foi desbanido(a) - + View Profile - - - - - - Block Player - - - - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - - - - - Kick - - - - - Ban - + Ver perfil + - Kick Player - + Block Player + Bloquear jogador + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + Quando bloqueia um jogador, você não receberá mais mensagens dele.<br><br>Você deseja mesmo bloquear %1? + + + + Kick + Expulsar + + + + Ban + Banir + + + + Kick Player + Expulsar jogador + + + Are you sure you would like to <b>kick</b> %1? - + Você deseja mesmo <b>expulsar</b> %1? - + Ban Player - + Banir jogador - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - + Você deseja mesmo <b>expulsar e banir</b> %1? + +Isto banirá tanto o nome de usuário do fórum como o endereço IP. @@ -178,22 +180,22 @@ This would ban both their forum username and their IP address. Room Window - + Janela da sala Room Description - + Descrição da sala Moderation... - + Moderação... Leave Room - + Sair da sala @@ -206,11 +208,11 @@ This would ban both their forum username and their IP address. Disconnected - + Desconectado - %1 (%2/%3 members) - connected + %1 - %2 (%3/%4 members) - connected @@ -224,6 +226,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Reportar compatibilidade de jogos @@ -233,92 +240,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Se você optar por enviar um caso de teste para a</span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">lista de compatibilidade do yuzu</span></a><span style=" font-size:10pt;">As seguintes informações serão coletadas e exibidas no site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Informações de Hardware (CPU / GPU / Sistema operativo)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Que versão do yuzu você está executando</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A conta yuzu conectada</li></ul></body></html> - - Perfect - Perfeito + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Jogo funciona na perfeição sem falhas de áudio ou gráficas.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Ótimo + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>O jogo funciona com pequenas falhas gráficas ou de áudio e pode ser jogado do início ao fim. Pode exigir algumas soluções alternativas.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - OK + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>O jogo funciona com grandes falhas gráficas ou de áudio, mas o jogo é jogável do início ao fim com soluções alternativas.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Mau + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>O jogo funciona, mas com grandes falhas gráficas ou de áudio. Não é possível progredir em áreas específicas devido a falhas, mesmo com soluções alternativas.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Introdução / Menu + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>O jogo não é jogável devido a grandes falhas gráficas ou de áudio. Não é possível passar da tela inicial.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Não Inicia + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>O jogo trava ao tentar iniciar.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Independente da velocidade ou performance, como este jogo funciona de principio ao fim nesta versão do yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Obrigado pelo seu envio! - + Submitting Entregando - + Communication error Erro de comunicação - + An error occurred while sending the Testcase Um erro ocorreu ao enviar o caso de teste - + Next Próximo @@ -339,7 +381,7 @@ This would ban both their forum username and their IP address. Output Device - + Dispositivo de saída @@ -378,37 +420,37 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + Configurar câmera infravermelha Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + Selecione de onde vem a imagem da câmera emulada. Pode ser uma câmera virtual ou uma câmera real. Camera Image Source: - + Origem da imagem da câmera: Input device: - + Dispositivo de entrada: Preview - + Prévia Resolution: 320*240 - + Resolução: 320*240 Click to preview - + Clique para pré-visualizar @@ -416,7 +458,7 @@ This would ban both their forum username and their IP address. Restaurar Padrões - + Auto Automático @@ -759,200 +801,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger Depurador - + Enable GDB Stub Activar GDB Stub - + Port: Porta: - + Logging Entrando - + Global Log Filter Filtro de registro global - + Show Log in Console Mostrar Relatório na Consola - + Open Log Location Abrir a localização do registro - + When checked, the max size of the log increases from 100 MB to 1 GB Quando ativado, o tamanho máximo do registo aumenta de 100 MB para 1 GB - + Enable Extended Logging** Ativar registros avançados** - + Homebrew Homebrew - + Arguments String Argumentos String - + Graphics Gráficos - + When checked, the graphics API enters a slower debugging mode Quando ativado, a API gráfica entra em um modo de depuração mais lento. - + Enable Graphics Debugging Activar Depuração Gráfica - + When checked, it enables Nsight Aftermath crash dumps Quando ativado, ativa a extração de registros de travamento do Nsight Aftermath - + Enable Nsight Aftermath Ativar Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Se selecionado, descarrega todos os shaders originários do cache do disco ou do jogo conforme encontrá-los. - + Dump Game Shaders Descarregar shaders do jogo - + When checked, it will dump all the macro programs of the GPU Quando marcada, essa opção irá despejar todos os macro programas da GPU - + Dump Maxwell Macros Despejar macros Maxwell - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower Quando ativado, desativa o macro compilador Just In Time. Ativar isto faz os jogos rodarem mais lentamente. - + Disable Macro JIT Desactivar Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache Quando ativado, o yuzu registrará estatísticas sobre o cache de pipeline compilado - + Enable Shader Feedback Ativar Feedback de Shaders - + When checked, it executes shaders without loop logic changes Quando ativado, executa shaders sem mudanças de lógica de loop - + Disable Loop safety checks Desativar verificação de segurança de loops - + Debugging Depuração - - Enable FS Access Log - Ativar acesso de registro FS - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Ativar serviços de relatório detalhado** - + + Enable FS Access Log + Ativar acesso de registro FS + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avançado - + Kiosk (Quest) Mode Modo Quiosque (Quest) - + Enable CPU Debugging Ativar depuração de CPU - + Enable Debug Asserts Ativar asserções de depuração - + Enable Auto-Stub** Ativar auto-esboço** - + Enable All Controller Types Ativar todos os tipos de controles - + Disable Web Applet Desativar Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Isto será restaurado automaticamente assim que o yuzu for fechado. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1322,193 +1399,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings Definições Gráficas - + Use disk pipeline cache Usar cache de pipeline em disco - + Use asynchronous GPU emulation Usar emulação assíncrona de GPU - + Accelerate ASTC texture decoding Acelerar a decodificação de textura ASTC - + NVDEC emulation: Emulação NVDEC: - + No Video Output Sem saída de vídeo - + CPU Video Decoding Decodificação de vídeo pela CPU - + GPU Video Decoding (Default) Decodificação de vídeo pela GPU (Padrão) - + Fullscreen Mode: Tela Cheia - + Borderless Windowed Janela sem bordas - + Exclusive Fullscreen Tela cheia exclusiva - + Aspect Ratio: Proporção do Ecrã: - + Default (16:9) Padrão (16:9) - + Force 4:3 Forçar 4:3 - + Force 21:9 Forçar 21:9 - + + Force 16:10 + + + + Stretch to Window Esticar à Janela - + Resolution: Resolução: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: Filtro de adaptação de janela: - + Nearest Neighbor Vizinho mais próximo - + Bilinear Bilinear - + Bicubic Bicúbico - + Gaussian Gaussiano - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (somente Vulkan) - + Anti-Aliasing Method: Método de Anti-Aliasing - + None Nenhum - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Usar cor de fundo global - + Set background color: Definir cor de fundo: - + Background Color: Cor de fundo: @@ -1517,6 +1619,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM (Shaders Assembly, apenas NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1571,37 +1679,47 @@ This would ban both their forum username and their IP address. Usar tempo de resposta rápido da GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Filtro Anisotrópico: - + Automatic Automático - + Default Padrão - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2093,7 +2211,7 @@ This would ban both their forum username and their IP address. - + Left Stick Analógico Esquerdo @@ -2187,14 +2305,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2213,7 +2331,7 @@ This would ban both their forum username and their IP address. - + Plus Mais @@ -2226,15 +2344,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2291,231 +2409,236 @@ This would ban both their forum username and their IP address. - + Right Stick Analógico Direito - - - - + + + + Clear Limpar - - - - - + + + + + [not set] [não definido] - - - Toggle button - Alternar pressionamento do botão - - - - + + Invert button Inverter botão - - + + + Toggle button + Alternar pressionamento do botão + + + + Invert axis Inverter eixo - - - + + + Set threshold Definir limite - - + + Choose a value between 0% and 100% Escolha um valor entre 0% e 100% - + + Toggle axis + + + + Set gyro threshold Definir limite do giroscópio - + Map Analog Stick Mapear analógicos - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Após pressionar OK, mova o seu analógico primeiro horizontalmente e depois verticalmente. Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois horizontalmente. - + Center axis Eixo central - - + + Deadzone: %1% Ponto Morto: %1% - - + + Modifier Range: %1% Modificador de Alcance: %1% - - + + Pro Controller Comando Pro - + Dual Joycons Joycons Duplos - + Left Joycon Joycon Esquerdo - + Right Joycon Joycon Direito - + Handheld Portátil - + GameCube Controller Controlador de depuração - + Poke Ball Plus Poke Ball Plus - + NES Controller Controle NES - + SNES Controller Controle SNES - + N64 Controller Controle N64 - + Sega Genesis Mega Drive - + Start / Pause Iniciar / Pausar - + Z Z - + Control Stick Direcional de controle - + C-Stick C-Stick - + Shake! Abane! - + [waiting] [em espera] - + New Profile Novo Perfil - + Enter a profile name: Introduza um novo nome de perfil: - - + + Create Input Profile Criar perfil de controlo - + The given profile name is not valid! O nome de perfil dado não é válido! - + Failed to create the input profile "%1" Falha ao criar o perfil de controlo "%1" - + Delete Input Profile Apagar Perfil de Controlo - + Failed to delete the input profile "%1" Falha ao apagar o perfil de controlo "%1" - + Load Input Profile Carregar perfil de controlo - + Failed to load the input profile "%1" Falha ao carregar o perfil de controlo "%1" - + Save Input Profile Guardar perfil de controlo - + Failed to save the input profile "%1" Falha ao guardar o perfil de controlo "%1" @@ -2770,42 +2893,42 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho Desenvolvedor - + Add-Ons Add-Ons - + General Geral - + System Sistema - + CPU CPU - + Graphics Gráficos - + Adv. Graphics Gráficos Avç. - + Audio Audio - + Properties Propriedades @@ -2861,37 +2984,37 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho Utilizador Atual - + Username Nome de Utilizador - + Set Image Definir Imagem - + Add Adicionar - + Rename Renomear - + Remove Remover - + Profile management is available only when game is not running. O gestor de perfis só está disponível apenas quando o jogo não está em execução. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2899,96 +3022,105 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho %2 - + Enter Username Introduza o Nome de Utilizador - + Users Utilizadores - + Enter a username for the new user: Introduza um nome de utilizador para o novo utilizador: - + Enter a new username: Introduza um novo nome de utilizador: - - Confirm Delete - Confirmar para eliminar - - - - You are about to delete user with name "%1". Are you sure? - Estás preste a eliminar o utilizador com o nome "%1". Tens a certeza? - - - + Select User Image Definir Imagem de utilizador - + JPEG Images (*.jpg *.jpeg) Imagens JPEG (*.jpg *.jpeg) - + Error deleting image Error ao eliminar a imagem - + Error occurred attempting to overwrite previous image at: %1. Ocorreu um erro ao tentar substituir imagem anterior em: %1. - + Error deleting file Erro ao eliminar o arquivo - + Unable to delete existing file: %1. Não é possível eliminar o arquivo existente: %1. - + Error creating user image directory Erro ao criar o diretório de imagens do utilizador - + Unable to create directory %1 for storing user images. Não é possível criar o diretório %1 para armazenar imagens do utilizador. - + Error copying user image Erro ao copiar a imagem do utilizador - + Unable to copy image from %1 to %2 Não é possível copiar a imagem de %1 para %2 - + Error resizing user image Erro no redimensionamento da imagem do usuário - + Unable to resize image Não foi possível redimensionar a imagem + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Confirmar para eliminar + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3517,47 +3649,47 @@ Para inverter os eixos, mova o seu analógico primeiro verticalmente e depois ho <html><head/><body><p>Lê entradas de controle a partir de scripts no mesmo formato que TAS-nx. <br/>Para uma explicação mais detalhada, por favor consulte a <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">página de ajuda</span></a> no website do yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Para checar que atalhos controlam rodar/gravar, por favor refira-se às Teclas de atalhos (Configurar -> Geral -> Teclas de atalhos) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ATENÇÃO: Este é um recurso experimental. <br/>Não irá rodar os scrips em quadros perfeitos com o atual, imperfeito método de sincronização. - + Settings Configurações - + Enable TAS features Ativar funcionalidades TAS - + Loop script Repetir script em loop - + Pause execution during loads Pausar execução durante carregamentos - + Script Directory Diretório do script - + Path Caminho - + ... ... @@ -3809,56 +3941,71 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta + Show Compatibility List + + + + Show Add-Ons Column Mostrar coluna de Add-Ons - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: Tamanho do ícone do jogo: - + Folder Icon Size: Tamanho do ícone da pasta: - + Row 1 Text: Linha 1 Texto: - + Row 2 Text: Linha 2 Texto: - + Screenshots Captura de Ecrã - + Ask Where To Save Screenshots (Windows Only) Perguntar Onde Guardar Capturas de Ecrã (Apenas Windows) - + Screenshots Path: Caminho das Capturas de Ecrã: - + ... ... - + Select Screenshots Path... Seleccionar Caminho de Capturas de Ecrã... - + <System> <System> @@ -3967,7 +4114,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta - + Verify Verificar @@ -4054,7 +4201,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta - + Unspecified Não especificado @@ -4069,17 +4216,36 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta O token não foi verificado. A alteração do token não foi gravada. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... A verificar... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verificação Falhada + + + Verification failed Verificação Falhada - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verificação Falhada. Verifique se introduziu seu nome de utilizador e o token correctamente e se a conexão com a Internet está operacional. @@ -4148,12 +4314,12 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta DirectConnectWindow - + Connecting - + Connect @@ -4161,910 +4327,912 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Dados anônimos são coletados</a>para ajudar a melhorar o yuzu.<br/><br/>Gostaria de compartilhar seus dados de uso conosco? - + Telemetry Telemetria - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... A Carregar o Web Applet ... - - + + Disable Web Applet Desativar Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) A desativação do applet da web pode causar comportamento inesperado e deve apenas ser usada com Super Mario 3D All-Stars. Você deseja mesmo desativar o applet da web? (Ele pode ser reativado nas configurações de depuração.) - + The amount of shaders currently being built Quantidade de shaders a serem construídos - + The current selected resolution scaling multiplier. O atualmente multiplicador de escala de resolução selecionado. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocidade da emulação actual. Valores acima ou abaixo de 100% indicam que a emulação está sendo executada mais depressa ou mais devagar do que a Switch - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Quantos quadros por segundo o jogo está exibindo de momento. Isto irá variar de jogo para jogo e de cena para cena. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tempo gasto para emular um frame da Switch, sem contar o a limitação de quadros ou o v-sync. Para emulação de velocidade máxima, esta deve ser no máximo 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Limpar arquivos recentes - + &Continue &Continuar - + &Pause &Pausa - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu está rodando um jogo - + Warning Outdated Game Format Aviso de Formato de Jogo Desactualizado - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Você está usando o formato de directório ROM desconstruído para este jogo, que é um formato desactualizado que foi substituído por outros, como NCA, NAX, XCI ou NSP. Os directórios de ROM não construídos não possuem ícones, metadados e suporte de actualização.<br><br>Para uma explicação dos vários formatos de Switch que o yuzu suporta,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>Verifique a nossa Wiki</a>. Esta mensagem não será mostrada novamente. - - + + Error while loading ROM! Erro ao carregar o ROM! - + The ROM format is not supported. O formato do ROM não é suportado. - + An error occurred initializing the video core. Ocorreu um erro ao inicializar o núcleo do vídeo. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu encontrou um erro enquanto rodando o núcleo de vídeo. Normalmente isto é causado por drivers de GPU desatualizados, incluindo integrados. Por favor veja o registro para mais detalhes. Para mais informações em acesso ao registro por favor veja a seguinte página: <a href='https://yuzu-emu.org/help/reference/log-files/'>Como fazer envio de arquivo de registro</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Erro ao carregar a ROM! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Por favor, siga <a href='https://yuzu-emu.org/help/quickstart/'>a guia de início rápido do yuzu</a> para fazer o redespejo dos seus arquivos.<br>Você pode consultar a wiki do yuzu</a> ou o Discord do yuzu</a> para obter ajuda. - + An unknown error occurred. Please see the log for more details. Ocorreu um erro desconhecido. Por favor, veja o log para mais detalhes. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Save Data - + Mod Data Mod Data - + Error Opening %1 Folder Erro ao abrir a pasta %1 - - + + Folder does not exist! A Pasta não existe! - + Error Opening Transferable Shader Cache Erro ao abrir os Shader Cache transferíveis - + Failed to create the shader cache directory for this title. Falha ao criar o diretório de cache de shaders para este título. - - Contents - Conteúdos + + Error Removing Contents + - - Update - Actualização + + Error Removing Update + - - DLC - DLC + + Error Removing DLC + - + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + Remove Entry Remover Entrada - - Remove Installed Game %1? - Remover Jogo Instalado %1? - - - - - - - - + + + + + + Successfully Removed Removido com Sucesso - + Successfully removed the installed base game. Removida a instalação do jogo base com sucesso. - - - - Error Removing %1 - Erro ao Remover %1 - - - + The base game is not installed in the NAND and cannot be removed. O jogo base não está instalado no NAND e não pode ser removido. - + Successfully removed the installed update. Removida a actualização instalada com sucesso. - + There is no update installed for this title. Não há actualização instalada neste título. - + There are no DLC installed for this title. Não há DLC instalado neste título. - + Successfully removed %1 installed DLC. Removido DLC instalado %1 com sucesso. - + Delete OpenGL Transferable Shader Cache? Apagar o cache de shaders transferível do OpenGL? - + Delete Vulkan Transferable Shader Cache? Apagar o cache de shaders transferível do Vulkan? - + Delete All Transferable Shader Caches? Apagar todos os caches de shaders transferíveis? - + Remove Custom Game Configuration? Remover Configuração Personalizada do Jogo? - + Remove File Remover Ficheiro - - + + Error Removing Transferable Shader Cache Error ao Remover Cache de Shader Transferível - - + + A shader cache for this title does not exist. O Shader Cache para este titulo não existe. - + Successfully removed the transferable shader cache. Removido a Cache de Shader Transferível com Sucesso. - + Failed to remove the transferable shader cache. Falha ao remover a cache de shader transferível. - - + + Error Removing Transferable Shader Caches Erro ao remover os caches de shaders transferíveis - + Successfully removed the transferable shader caches. Os caches de shaders transferíveis foram removidos com sucesso. - + Failed to remove the transferable shader cache directory. Falha ao remover o diretório do cache de shaders transferível. - - + + Error Removing Custom Configuration Erro ao Remover Configuração Personalizada - + A custom configuration for this title does not exist. Não existe uma configuração personalizada para este titúlo. - + Successfully removed the custom game configuration. Removida a configuração personalizada do jogo com sucesso. - + Failed to remove the custom game configuration. Falha ao remover a configuração personalizada do jogo. - - + + RomFS Extraction Failed! A Extração de RomFS falhou! - + There was an error copying the RomFS files or the user cancelled the operation. Houve um erro ao copiar os arquivos RomFS ou o usuário cancelou a operação. - + Full Cheio - + Skeleton Esqueleto - + Select RomFS Dump Mode Selecione o modo de despejo do RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Por favor, selecione a forma como você gostaria que o RomFS fosse despejado<br>Full irá copiar todos os arquivos para o novo diretório enquanto<br>skeleton criará apenas a estrutura de diretórios. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Não há espaço suficiente em %1 para extrair o RomFS. Por favor abra espaço ou selecione um diretório diferente em Emulação > Configurar > Sistema > Sistema de arquivos > Extrair raiz - + Extracting RomFS... Extraindo o RomFS ... - - + + Cancel Cancelar - + RomFS Extraction Succeeded! Extração de RomFS Bem-Sucedida! - + The operation completed successfully. A operação foi completa com sucesso. - + Error Opening %1 Erro ao abrir %1 - + Select Directory Selecione o Diretório - + Properties Propriedades - + The game properties could not be loaded. As propriedades do jogo não puderam ser carregadas. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Executáveis Switch (%1);;Todos os Ficheiros (*.*) - + Load File Carregar Ficheiro - + Open Extracted ROM Directory Abrir o directório ROM extraído - + Invalid Directory Selected Diretório inválido selecionado - + The directory you have selected does not contain a 'main' file. O diretório que você selecionou não contém um arquivo 'Main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Ficheiro Switch Instalável (*.nca *.nsp *.xci);;Arquivo de Conteúdo Nintendo (*.nca);;Pacote de Envio Nintendo (*.nsp);;Imagem de Cartucho NX (*.xci) - + Install Files Instalar Ficheiros - + %n file(s) remaining - + Installing file "%1"... Instalando arquivo "%1"... - - + + Install Results Instalar Resultados - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Para evitar possíveis conflitos, desencorajamos que os utilizadores instalem os jogos base na NAND. Por favor, use esse recurso apenas para instalar atualizações e DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Aplicação do sistema - + System Archive Arquivo do sistema - + System Application Update Atualização do aplicativo do sistema - + Firmware Package (Type A) Pacote de Firmware (Tipo A) - + Firmware Package (Type B) Pacote de Firmware (Tipo B) - + Game Jogo - + Game Update Actualização do Jogo - + Game DLC DLC do Jogo - + Delta Title Título Delta - + Select NCA Install Type... Selecione o tipo de instalação do NCA ... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Por favor, selecione o tipo de título que você gostaria de instalar este NCA como: (Na maioria dos casos, o padrão 'Jogo' é suficiente). - + Failed to Install Falha na instalação - + The title type you selected for the NCA is invalid. O tipo de título que você selecionou para o NCA é inválido. - + File not found Arquivo não encontrado - + File "%1" not found Arquivo "%1" não encontrado - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Conta Yuzu Ausente - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Para enviar um caso de teste de compatibilidade de jogos, você deve vincular sua conta yuzu.<br><br/>Para vincular sua conta yuzu, vá para Emulação &gt; Configuração &gt; Rede. - + Error opening URL Erro ao abrir URL - + Unable to open the URL "%1". Não foi possível abrir o URL "%1". - + TAS Recording Gravando TAS - + Overwrite file of player 1? Sobrescrever arquivo do jogador 1? - + Invalid config detected Configação inválida detectada - + Handheld controller can't be used on docked mode. Pro controller will be selected. O comando portátil não pode ser usado no modo encaixado na base. O Pro controller será selecionado. - - - Error - Erro - - - - - The current game is not looking for amiibos - O jogo atual não está procurando amiibos - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed O amiibo atual foi removido - + + Error + Erro + + + + + The current game is not looking for amiibos + O jogo atual não está procurando amiibos + + + Amiibo File (%1);; All Files (*.*) Arquivo Amiibo (%1);; Todos os Arquivos (*.*) - + Load Amiibo Carregar Amiibo - - Error opening Amiibo data file - Erro ao abrir o arquivo de dados do Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Não é possível abrir o arquivo Amiibo "%1" para leitura. - - - - Error reading Amiibo data file - Erro ao ler o arquivo de dados do Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Não é possível ler completamente os dados do Amiibo. Espera-se que leia %1 bytes, mas só conseguiu ler %2 bytes. - - - + Error loading Amiibo data Erro ao carregar dados do Amiibo - - Unable to load Amiibo data. - Não foi possível carregar os dados do Amiibo. + + The selected file is not a valid amiibo + - + + The selected file is already on use + + + + + An unknown error occurred + + + + Capture Screenshot Captura de Tela - + PNG Image (*.png) Imagem PNG (*.png) - + TAS state: Running %1/%2 Situação TAS: Rodando %1%2 - + TAS state: Recording %1 Situação TAS: Gravando %1 - + TAS state: Idle %1/%2 Situação TAS: Repouso %1%2 - + TAS State: Invalid Situação TAS: Inválido - + &Stop Running &Parar de rodar - + &Start &Começar - + Stop R&ecording Parar G&ravação - + R&ecord G&ravação - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Escala: %1x - + Speed: %1% / %2% Velocidade: %1% / %2% - + Speed: %1% Velocidade: %1% - + Game: %1 FPS (Unlocked) Jogo: %1 FPS (Desbloqueado) - + Game: %1 FPS Jogo: %1 FPS - + Frame: %1 ms Quadro: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU ALTA - + GPU EXTREME GPU EXTREMA - + GPU ERROR ERRO DE GPU - + DOCKED - + HANDHELD - + NEAREST VIZINHO - - + + BILINEAR BILINEAR - + BICUBIC BICÚBICO - + GAUSSIAN GAUSSIANO - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA Sem AA - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. O jogo que você está tentando carregar requer arquivos adicionais do seu Switch para serem despejados antes de jogar.<br/><br/>Para obter mais informações sobre como despejar esses arquivos, consulte a seguinte página da wiki:<a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Despejando arquivos do sistema e as fontes compartilhadas de uma consola Switch</a>.<br/><br/>Você gostaria de regressar para a lista de jogos? Continuar a emulação pode resultar em falhas, dados de salvamento corrompidos ou outros erros. - + yuzu was unable to locate a Switch system archive. %1 O yuzu não conseguiu localizar um arquivo de sistema do Switch. % 1 - + yuzu was unable to locate a Switch system archive: %1. %2 O yuzu não conseguiu localizar um arquivo de sistema do Switch: %1. %2 - + System Archive Not Found Arquivo do Sistema Não Encontrado - + System Archive Missing Arquivo de Sistema em falta - + yuzu was unable to locate the Switch shared fonts. %1 yuzu não conseguiu localizar as fontes compartilhadas do Switch. %1 - + Shared Fonts Not Found Fontes compartilhadas não encontradas - + Shared Font Missing Fontes compartilhadas em falta - + Fatal Error Erro fatal - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu encontrou um erro fatal, por favor veja o registro para mais detalhes. Para mais informações sobre como acessar o registro, por favor, veja a seguinte página:<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Como carregar o arquivo de registro</a>.<br/><br/>Você gostaria de regressar para a lista de jogos? Continuar a emulação pode resultar em falhas, dados de salvamento corrompidos ou outros erros. - + Fatal Error encountered Ocorreu um Erro fatal - + Confirm Key Rederivation Confirme a rederivação da chave - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5081,37 +5249,37 @@ e opcionalmente faça backups. Isso irá excluir os seus arquivos de chave gerados automaticamente e executará novamente o módulo de derivação de chave. - + Missing fuses Fusíveis em Falta - + - Missing BOOT0 - BOOT0 em Falta - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main em Falta - + - Missing PRODINFO - PRODINFO em Falta - + Derivation Components Missing Componentes de Derivação em Falta - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Chaves de encriptação faltando. <br>Por favor, siga <a href='https://yuzu-emu.org/help/quickstart/'>o guia de início rápido</a> para extrair suas chaves, firmware e jogos. <br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5120,39 +5288,39 @@ Isto pode demorar até um minuto, dependendo do desempenho do seu sistema. - + Deriving Keys Derivando Chaves - + Select RomFS Dump Target Selecione o destino de despejo do RomFS - + Please select which RomFS you would like to dump. Por favor, selecione qual o RomFS que você gostaria de despejar. - + Are you sure you want to close yuzu? Tem a certeza que quer fechar o yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Tem a certeza de que quer parar a emulação? Qualquer progresso não salvo será perdido. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5164,38 +5332,38 @@ Deseja ignorar isso e sair mesmo assim? GRenderWindow - + OpenGL not available! OpenGL não está disponível! - + yuzu has not been compiled with OpenGL support. yuzu não foi compilado com suporte OpenGL. - - + + Error while initializing OpenGL! Erro ao inicializar OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. O seu GPU pode não suportar OpenGL, ou não tem os drivers gráficos mais recentes. - + Error while initializing OpenGL 4.6! Erro ao inicializar o OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 O teu GPU pode não suportar OpenGL 4.6, ou não tem os drivers gráficos mais recentes. - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Sua GPU pode não suportar uma ou mais extensões necessárias do OpenGL. Verifique se você possui a última versão dos drivers gráficos.<br><br>Renderizador GL:<br>%1<br><br>Extensões não suportadas:<br>%2 @@ -5203,153 +5371,153 @@ Deseja ignorar isso e sair mesmo assim? GameList - + Favorite Favorito - + Start Game Iniciar jogo - + Start Game without Custom Configuration Iniciar jogo sem configuração personalizada - + Open Save Data Location Abrir Localização de Dados Salvos - + Open Mod Data Location Abrir a Localização de Dados do Mod - + Open Transferable Pipeline Cache Abrir cache de pipeline transferível - + Remove Remover - + Remove Installed Update Remover Actualizações Instaladas - + Remove All Installed DLC Remover Todos os DLC Instalados - + Remove Custom Configuration Remover Configuração Personalizada - + Remove OpenGL Pipeline Cache Remover cache de pipeline do OpenGL - + Remove Vulkan Pipeline Cache Remover cache de pipeline do Vulkan - + Remove All Pipeline Caches Remover todos os caches de pipeline - + Remove All Installed Contents Remover Todos os Conteúdos Instalados - + Dump RomFS Despejar RomFS - + Dump RomFS to SDMC Extrair RomFS para SDMC - + Copy Title ID to Clipboard Copiar título de ID para a área de transferência - + Navigate to GameDB entry Navegue para a Entrada da Base de Dados de Jogos - + Properties Propriedades - + Scan Subfolders Examinar Sub-pastas - + Remove Game Directory Remover diretório do Jogo - + ▲ Move Up ▲ Mover para Cima - + ▼ Move Down ▼ Mover para Baixo - + Open Directory Location Abrir Localização do diretório - + Clear Limpar - + Name Nome - + Compatibility Compatibilidade - + Add-ons Add-ons - + File type Tipo de Arquivo - + Size Tamanho @@ -5358,81 +5526,61 @@ Deseja ignorar isso e sair mesmo assim? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Perfeito - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - O Jogo Funciona na Perfeição sem falhas de áudio ou gráficas, todas as funcionalidades testadas funcionam como planeadas sem -quaisquer soluções alternativas necessárias. - - - - Great - Ótimo - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - O Jogo funciona com pequenas falhas gráficas ou de áudio e pode ser jogado do principio ao fim. Pode exigir algumas -soluções alternativas. - - Okay - Ok - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - O Jogo funciona com grandes falhas gráficas ou de áudio, mas o jogo é jogável do principio ao fim com -soluções alternativas. + Game can be played without issues. + - Bad - Mau + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Jogo Funcional, mas com grandes falhas gráficas ou de áudio. Incapaz de progredir em áreas específicas devido a falhas -mesmo com soluções alternativas + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu Introdução / Menu - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - O Jogo não é jogável devido a grandes falhas gráficas ou de áudio. Não é possível passar da Tela -Inicial + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Não Inicia - + The game crashes when attempting to startup. O jogo trava ao tentar iniciar. - + Not Tested Não Testado - + The game has not yet been tested. O jogo ainda não foi testado. @@ -5440,7 +5588,7 @@ Inicial GameListPlaceholder - + Double-click to add a new folder to the game list Clique duas vezes para adicionar uma nova pasta à lista de jogos @@ -5453,12 +5601,12 @@ Inicial - + Filter: Filtro: - + Enter pattern to filter Digite o padrão para filtrar @@ -5508,7 +5656,7 @@ Inicial Room Description - + Descrição da sala @@ -5534,12 +5682,12 @@ Inicial HostRoomWindow - + Error Erro - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5548,11 +5696,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5574,112 +5723,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Captura de Tela - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Tela Cheia - + Load File Carregar Ficheiro - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5794,42 +5942,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Jogadores - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5852,232 +6000,237 @@ Debug Message: &Arquivos recentes - + &Emulation &Emulação - + &View &Vista - + &Reset Window Size &Restaurar tamanho da janela - + &Debugging &Depurar - + Reset Window Size to &720p Restaurar tamanho da janela para &720p - + Reset Window Size to 720p Restaurar tamanho da janela para 720p - + Reset Window Size to &900p Restaurar tamanho da janela para &900p - + Reset Window Size to 900p Restaurar tamanho da janela para 900p - + Reset Window Size to &1080p Restaurar tamanho da janela para &1080p - + Reset Window Size to 1080p Restaurar tamanho da janela para 1080p - + + &Multiplayer + + + + &Tools &Ferramentas - + &TAS &TAS - + &Help &Ajuda - + &Install Files to NAND... &Instalar arquivos na NAND... - + L&oad File... C&arregar arquivo... - + Load &Folder... Carregar &pasta... - + E&xit &Sair - + &Pause &Pausa - + &Stop &Parar - + &Reinitialize keys... &Reinicializar chaves... - + &About yuzu &Sobre o yuzu - + Single &Window Mode Modo de &janela única - + Con&figure... Con&figurar... - + Display D&ock Widget Headers Exibir barra de títul&os de widgets afixados - + Show &Filter Bar Mostrar Barra de &Filtros - + Show &Status Bar Mostrar Barra de &Estado - + Show Status Bar Mostrar Barra de Estado - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room + &Browse Public Game Lobby - - Direct Connect to Room + + &Create Room - - Show Current Room + + &Leave Room + &Direct Connect to Room + + + + + &Show Current Room + + + + F&ullscreen T&ela cheia - + &Restart &Reiniciar - + Load/Remove &Amiibo... Carregar/Remover &Amiibo... - + &Report Compatibility &Reportar compatibilidade - + Open &Mods Page Abrir Página de &Mods - + Open &Quickstart Guide Abrir &guia de início rápido - + &FAQ &Perguntas frequentes - + Open &yuzu Folder Abrir pasta &yuzu - + &Capture Screenshot &Captura de Tela - + &Configure TAS... &Configurar TAS - + Configure C&urrent Game... Configurar jogo atual... - + &Start &Começar - + &Reset &Restaurar - + R&ecord G&ravar @@ -6142,46 +6295,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Conectado - - - - Not Connected - + + Connected + Conectado + + + + New Messages Received + + + + Error Erro - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6282,22 +6430,39 @@ They may have left the room. - - Leave Room + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. - + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + + Leave Room + Sair da sala + + + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6305,7 +6470,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Erro @@ -6354,42 +6519,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 is playing %2 - + Not playing a game - + Installed SD Titles Títulos SD instalados - + Installed NAND Titles Títulos NAND instalados - + System Titles Títulos do sistema - + Add New Game Directory Adicionar novo diretório de jogos - + Favorites Favoritos @@ -6419,7 +6584,7 @@ p, li { white-space: pre-wrap; } - + [not set] [não configurado] @@ -6434,10 +6599,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Eixo %1%2 @@ -6451,9 +6616,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [Desconhecido] @@ -6618,15 +6783,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [inválido] - - + + %1%2Hat %3 %1%2Direcional %3 @@ -6634,35 +6799,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Eixo %3 - + %1%2Axis %3,%4,%5 %1%2Eixo %3,%4,%5 - + %1%2Motion %3 %1%2Movimentação %3 - - + + %1%2Button %3 %1%2Botão %3 - + [unused] [sem uso] @@ -6703,11 +6868,124 @@ p, li { white-space: pre-wrap; } Extra - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Nome + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6811,6 +7089,7 @@ p, li { white-space: pre-wrap; } + Handheld Portátil @@ -6850,11 +7129,6 @@ p, li { white-space: pre-wrap; } Docked Ancorado - - - Undocked - Desancorado - Vibration diff --git a/dist/languages/ru_RU.ts b/dist/languages/ru_RU.ts index 8ba92a5..ae7a5ef 100644 --- a/dist/languages/ru_RU.ts +++ b/dist/languages/ru_RU.ts @@ -95,78 +95,78 @@ p, li { white-space: pre-wrap; } Отправить сообщение - + Members Участники - + %1 has joined %1 присоединился - + %1 has left %1 вышел - + %1 has been kicked %1 был выгнан - + %1 has been banned %1 был забанен - + %1 has been unbanned %1 был разбанен - + View Profile Посмотреть профиль - - + + Block Player Заблокировать игрока - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? Когда вы блокируете игрока, вы больше не будете получать от него сообщения в чате.<br><br>Вы уверены, что хотите заблокировать %1? - + Kick Выгнать - + Ban Забанить - + Kick Player Выгнать игрока - + Are you sure you would like to <b>kick</b> %1? Вы уверены, что хотите <b>выгнать</b> %1? - + Ban Player Забанить игрока - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -212,8 +212,8 @@ This would ban both their forum username and their IP address. - %1 (%2/%3 members) - connected - %1 (%2/%3 участников) - подключился + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 участников) - подключено @@ -226,6 +226,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Сообщить о совместимости игры @@ -235,92 +240,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Если вы захотите отправить отчёт в </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">список совместимости yuzu</a><span style=" font-size:10pt;">, следующая информация будет собрана и отображена на сайте:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> Информация о железе (ЦП / ГП / Операционная система)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Версия yuzu</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Подключённый аккаунт yuzu</li></ul></body></html> - - Perfect - Идеально + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Игра работает идеально, без звуковых или графических артефактов.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Отлично + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Игра работает с небольшими графическими или звуковыми артефактами и может быть пройдена от начала до конца. Могут потребоваться обходные пути.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Хорошо + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Игра работает с существенными графическими или звуковыми артефактами, но с обходными путями может быть пройдена.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Плохо + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Игра работает, но с существенными графическими или звуковыми артефактами. В некоторых местах невозможно пройти даже с обходными путями.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Вступление/Меню + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>В игру невозможно играть из-за графических или звуковых артефактов. Невозможно продвинуться дальше стартового меню.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Не запускается + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Игра вылетает при попытке запуска.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Независимо от скорости и производительности, насколько хорошо эта игра работает от начала до конца в текущей версии yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Спасибо за ваш отчет! - + Submitting Отправка - + Communication error Ошибка соединения - + An error occurred while sending the Testcase Произошла ошибка при отправке отчета - + Next Далее @@ -418,7 +458,7 @@ This would ban both their forum username and their IP address. По умолчанию - + Auto Авто @@ -478,16 +518,14 @@ This would ban both their forum username and their IP address. These settings reduce accuracy for speed. - Эти настройки уменьшают точность ради скорости. Перевод этих настроек не очень точный, используйте английский язык по возможности. + Эти настройки уменьшают точность ради скорости. <div>This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.</div> - - <div>Эта опция повышает скорость, уменьшая точность сложенных умноженных инструкций на ЦП без поддержки FMA.</div> - + @@ -499,9 +537,7 @@ This would ban both their forum username and their IP address. <div>This option improves the speed of some approximate floating-point functions by using less accurate native approximations.</div> - - <div>Эта опция повышает скорость работы некоторых функций с плавающей запятой за счет использования менее точных родных приближений.</div> - + @@ -513,9 +549,7 @@ This would ban both their forum username and their IP address. <div>This option improves the speed of 32 bits ASIMD floating-point functions by running with incorrect rounding modes.</div> - - <div>Эта опция повышает скорость работы 32-битных ASIMD-функций с плавающей запятой, работая с неправильными режимами округления.</div> - + @@ -527,42 +561,36 @@ This would ban both their forum username and their IP address. <div>This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.</div> - - <div>Эта опция повышает скорость, убирая проверку NaN. Обратите внимание, что это также снижает точность некоторых инструкций с плавающей точкой. </div> - + Inaccurate NaN handling - Неправильная обработка NaN + <div>This option improves speed by eliminating a safety check before every memory read/write in guest. Disabling it may allow a game to read/write the emulator's memory.</div> - - <div>Эта опция повышает скорость работы за счет устранения проверки безопасности перед каждым чтением/записью памяти у гостя. Отключение этой опции может позволить игре читать/записывать память эмулятора.</div> - + Disable address space checks - Отключить проверку адресного пространства + <div>This option improves speed by relying only on the semantics of cmpxchg to ensure safety of exclusive access instructions. Please note this may result in deadlocks and other race conditions.</div> - - <div>Эта опция повышает скорость, полагаясь только на семантику cmpxchg для обеспечения безопасности инструкций исключительного доступа. Обратите внимание, что это может привести к тупиковым ситуациям и другим условиям гонки.</div> - + Ignore global monitor - Игнорировать глобальный монитор + @@ -599,95 +627,79 @@ This would ban both their forum username and their IP address. <div style="white-space: nowrap">Enabling it inlines accesses to PageTable::pointers into emitted code.</div> <div style="white-space: nowrap">Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.</div> - - <div style="white-space: nowrap">Эта оптимизация ускоряет доступ гостевой программы к памяти.</div> - <div style="white-space: nowrap">Включение этой функции встраивает доступ к PageTable::pointers в выданный код.</div> - <div style="white-space: nowrap">Отключение этой функции заставит все обращения к памяти проходить через функции Memory::Read/Memory::Write.</div> - + Enable inline page tables - Включить встроенные таблицы страниц + <div>This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.</div> - - <div>Эта оптимизация позволяет избежать поиска диспетчера, позволяя излучаемым базовым блокам перепрыгивать непосредственно на другие базовые блоки, если целевой ПК является статическим.</div> - + Enable block linking - Разрешить связывание блоков + <div>This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.</div> - - <div>Эта оптимизация позволяет избежать поиска диспетчера, отслеживая потенциальные адреса возврата BL-инструкций. Это приблизительно соответствует тому, что происходит с буфером стека возвратов на реальном ЦП.</div> - + Enable return stack buffer - Включить буфер стека возврата + <div>Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.</div> - - <div>Включите двухуровневую систему диспетчеризации. Более быстрый диспетчер, написанный на ассемблере, имеет небольшой MRU кэш пунктов назначения прыжков используется в первую очередь. Если это не удается, диспетчер возвращается к более медленному диспетчеру C++.</div> - + Enable fast dispatcher - Включить быстрый диспетчер + <div>Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.</div> - - <div>Обеспечивает ИК-оптимизацию, уменьшающую ненужный доступ к структуре контекста процессора.</div> - + Enable context elimination - Включить исключение контекста + <div>Enables IR optimizations that involve constant propagation.</div> - - <div>Обеспечивает ИК-оптимизацию, которая подразумевает постоянное распространение.</div> - + Enable constant propagation - Включить постоянное распространение + <div>Enables miscellaneous IR optimizations.</div> - - <div>Обеспечивает различные ИК-оптимизации.</div> - + @@ -700,15 +712,12 @@ This would ban both their forum username and their IP address. <div style="white-space: nowrap">When enabled, a misalignment is only triggered when an access crosses a page boundary.</div> <div style="white-space: nowrap">When disabled, a misalignment is triggered on all misaligned accesses.</div> - - <div style="white-space: nowrap">При включении этого параметра смещение срабатывает только в том случае, если доступ пересекает границу страницы.</div> - <div style="white-space: nowrap">При отключении, во всех случаях неправильного доступа срабатывает несовпадение.</div> - + Enable misalignment check reduction - Включить снижение проверок на неправильное выравнивание + @@ -717,16 +726,12 @@ This would ban both their forum username and their IP address. <div style="white-space: nowrap">Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU.</div> <div style="white-space: nowrap">Disabling this forces all memory accesses to use Software MMU Emulation.</div> - - <div style="white-space: nowrap">Эта оптимизация ускоряет доступ гостевой программы к памяти.</div> - <div style="white-space: nowrap">Включение этой оптимизации приводит к тому, что чтение/запись гостевой памяти производится непосредственно в память и использует MMU хоста.</div> - <div style="white-space: nowrap">Отключение этой функции заставляет все обращения к памяти использовать программную эмуляцию MMU.</div> - + Enable Host MMU Emulation (general memory instructions) - Включить эмуляцию MMU хоста (инструкции общей памяти) + @@ -735,16 +740,12 @@ This would ban both their forum username and their IP address. <div style="white-space: nowrap">Enabling it causes guest exclusive memory reads/writes to be done directly into memory and make use of Host's MMU.</div> <div style="white-space: nowrap">Disabling this forces all exclusive memory accesses to use Software MMU Emulation.</div> - - <div style="white-space: nowrap">Эта оптимизация ускоряет эксклюзивный доступ гостевой программы к памяти.</div> - <div style="white-space: nowrap">Включение этой оптимизации приводит к тому, что чтение/запись гостевой эксклюзивной памяти производится непосредственно в память и не использует MMU хоста.</div> - <div style="white-space: nowrap">Отключение этой функции заставляет все эксклюзивные доступы к памяти использовать эмуляцию программного MMU.</div> - + Enable Host MMU Emulation (exclusive memory instructions) - Включить эмуляцию MMU хоста (инструкции экслюзивной памяти) + @@ -752,15 +753,12 @@ This would ban both their forum username and their IP address. <div style="white-space: nowrap">This optimization speeds up exclusive memory accesses by the guest program.</div> <div style="white-space: nowrap">Enabling it reduces the overhead of fastmem failure of exclusive memory accesses.</div> - - <div style="white-space: nowrap">Эта оптимизация ускоряет эксклюзивный доступ гостевой программы к памяти.</div> - <div style="white-space: nowrap">Его включение снижает накладные расходы fastmem при отказе эксклюзивных обращений к памяти.</div> - + Enable recompilation of exclusive memory instructions - Включить перекомпиляцию инструкций экслюзивной памяти + @@ -771,200 +769,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger Отладчик - + Enable GDB Stub - Включить GDB Stub + - + Port: Порт: - + Logging Журналирование - + Global Log Filter Глобальный фильтр журналов - + Show Log in Console Показывать журнал в консоли - + Open Log Location Открыть папку для журналов - + When checked, the max size of the log increases from 100 MB to 1 GB Если включено, максимальный размер журнала увеличивается со 100 МБ до 1 ГБ - + Enable Extended Logging** Включить расширенное ведение журнала** - + Homebrew Homebrew - + Arguments String Строка аргументов - + Graphics Графика - + When checked, the graphics API enters a slower debugging mode Если включено, графический API переходит в более медленный режим отладки - + Enable Graphics Debugging Включить отладку графики - + When checked, it enables Nsight Aftermath crash dumps - Если включено, включает дампы крашей Nsight Aftermath + - + Enable Nsight Aftermath - Включить Nsight Aftermath + - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found Если включено, будет дампить все оригинальные шейдеры ассемблера из кэша шейдеров на диске или игры как найденные - + Dump Game Shaders Дамп игровых шейдеров - + When checked, it will dump all the macro programs of the GPU - Если включено, будет дампить все макропрограммы ГП + - + Dump Maxwell Macros - Дамп макросов Maxwell + - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - Если включено, отключает компилятор макроса Just In Time. Включение опции делает игры медленнее + - + Disable Macro JIT - Отключить Макрос JIT + - + When checked, yuzu will log statistics about the compiled pipeline cache Если включено, yuzu будет записывать статистику о скомпилированном кэше конвейера - + Enable Shader Feedback Включить обратную связь о шейдерах - + When checked, it executes shaders without loop logic changes - Если включено, производит выполнение шейдеров без изменения логики цикла + - + Disable Loop safety checks - Отключить проверку безопасности цикла + - + Debugging Отладка - - Enable FS Access Log - Включить журнал доступа к FS - - - - Dump Audio Commands To Console** - Дамп аудиокоманд в консоль** - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - Включите эту опцию, чтобы выводить на консоль последний сгенерированный список аудиокоманд. Влияет только на игры, использующие аудио рендерер. - - - + Enable Verbose Reporting Services** - Включить службу отчётов в развернутом виде** + - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced - Дополнительно + Расширенные - + Kiosk (Quest) Mode Режим киоска (Квест) - + Enable CPU Debugging Включить отладку ЦП - + Enable Debug Asserts - Включить отладочные сигналы + - + Enable Auto-Stub** - Включить Автоподставку** + - + Enable All Controller Types Включить все типы контроллеров - + Disable Web Applet Отключить веб-апплет - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + Позволяет yuzu проверять наличие рабочей среды Vulkan при запуске программы. Отключите эту опцию, если это вызывает проблемы с тем, что внешние программы видят yuzu. + + + + Perform Startup Vulkan Check + Выполнять проверку Vulkan при запуске + + + **This will be reset automatically when yuzu closes. **Это будет автоматически сброшено после закрытия yuzu. + + + Restart Required + Требуется перезапуск + + + + yuzu is required to restart in order to apply this setting. + yuzu необходимо перезапустить, чтобы применить эту настройку. + + + + Web applet not compiled + Веб-апплет не скомпилирован + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1047,7 +1080,7 @@ This would ban both their forum username and their IP address. GraphicsAdvanced - ГрафикаДополительно + ГрафикаРасширенные @@ -1199,7 +1232,7 @@ This would ban both their forum username and their IP address. Select Gamecard Path... - Выберите папку с картриджами... + Выберите папку для картриджей... @@ -1209,7 +1242,7 @@ This would ban both their forum username and their IP address. Select Mod Load Directory... - Выберите папку с модами... + Выберите папку для модов... @@ -1334,193 +1367,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings Настройки графики - + Use disk pipeline cache Использовать кэш конвейера на диске - + Use asynchronous GPU emulation Использовать асинхронную эмуляцию ГП - + Accelerate ASTC texture decoding Ускорение декодирования текстур ASTC - + NVDEC emulation: Эмуляция NVDEC: - + No Video Output Отсутствие видеовыхода - + CPU Video Decoding Декодирование видео на ЦП - + GPU Video Decoding (Default) Декодирование видео на ГП (по умолчанию) - + Fullscreen Mode: Полноэкранный режим: - + Borderless Windowed Окно без границ - + Exclusive Fullscreen Эксклюзивный полноэкранный - + Aspect Ratio: Соотношение сторон: - + Default (16:9) Стандартное (16:9) - + Force 4:3 Заставить 4:3 - + Force 21:9 Заставить 21:9 - + + Force 16:10 + Заставить 16:10 + + + Stretch to Window Растянуть до окна - + Resolution: Разрешение: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [ЭКСПЕРИМЕНТАЛЬНО] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [ЭКСПЕРИМЕНТАЛЬНО] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: Фильтр адаптации окна: - + Nearest Neighbor Ближайший сосед - + Bilinear Билинейный - + Bicubic Бикубический - + Gaussian Гаусс - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (Только для Vulkan) - + Anti-Aliasing Method: Метод сглаживания: - + None Выкл. - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Использовать общий фоновый цвет - + Set background color: Установить фоновый цвет: - + Background Color: Фоновый цвет: @@ -1529,6 +1587,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM (ассемблерные шейдеры, только для NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1540,7 +1604,7 @@ This would ban both their forum username and their IP address. Advanced - Дополнительно + Расширенные @@ -1560,12 +1624,12 @@ This would ban both their forum username and their IP address. Use VSync - + Использовать вертикальную синхронизацию Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental. - Включает асинхронную компиляцию шейдеров, что уменьшит разрывы шейдеров. Функция является экспериментальной. + Включает асинхронную компиляцию шейдеров, что уменьшит зависания из-за шейдеров. Функция является экспериментальной. @@ -1583,37 +1647,47 @@ This would ban both their forum username and their IP address. Включить Fast GPU Time (Хак) - - Anisotropic Filtering: - Анизотропная Фильтрация: + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + Включает пессимистическую очистку буферов. Эта опция заставляет промывать немодифицированные буферы, что может снизить производительность. - + + Use pessimistic buffer flushes (Hack) + Использовать пессимистическую очистку буферов (Хак) + + + + Anisotropic Filtering: + Анизотропная фильтрация: + + + Automatic Автоматически - + Default Стандартная - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -1653,7 +1727,7 @@ This would ban both their forum username and their IP address. Hotkey - Горячая Клавиша + Горячая клавиша @@ -1701,17 +1775,17 @@ This would ban both their forum username and their IP address. Conflicting Button Sequence - Конфликтующее сочетание клавиш + Конфликтующее сочетание кнопок The default button sequence is already assigned to: %1 - Сочетание по умолчанию уже назначено на: %1 + Сочетание кнопок по умолчанию уже назначено на: %1 The default key sequence is already assigned to: %1 - Сочетание по умолчанию уже назначено на: %1 + Сочетание клавиш по умолчанию уже назначено на: %1 @@ -1773,7 +1847,7 @@ This would ban both their forum username and their IP address. Advanced - Дополнительно + Расширенные @@ -1877,7 +1951,7 @@ This would ban both their forum username and their IP address. Joycon Colors - Цвета Joycon'ов + Цвета Joy-Con'ов @@ -1894,7 +1968,7 @@ This would ban both their forum username and their IP address. L Body - Левый + Левый контроллер @@ -1906,7 +1980,7 @@ This would ban both their forum username and their IP address. L Button - Левая кнопка + Кнопка L @@ -1918,7 +1992,7 @@ This would ban both their forum username and their IP address. R Body - Правый + Правый контроллер @@ -1930,7 +2004,7 @@ This would ban both their forum username and their IP address. R Button - Правая кнопка + Кнопка R @@ -1990,7 +2064,7 @@ This would ban both their forum username and their IP address. Advanced - Дополнительно + Расширенные @@ -2063,7 +2137,7 @@ This would ban both their forum username and their IP address. Motion / Touch - Движение и/или сенсор + Движение и сенсор @@ -2105,9 +2179,9 @@ This would ban both their forum username and their IP address. - + Left Stick - Левый стик + Левый мини-джойстик @@ -2194,19 +2268,19 @@ This would ban both their forum username and their IP address. D-Pad - Крестовина + Кнопки направлений - + L L - + ZL ZL @@ -2225,7 +2299,7 @@ This would ban both their forum username and their IP address. - + Plus Плюс @@ -2233,20 +2307,20 @@ This would ban both their forum username and their IP address. Home - Домой + Home - + R R - + ZR ZR @@ -2303,231 +2377,236 @@ This would ban both their forum username and their IP address. - + Right Stick - Правый стик + Правый мини-джойстик - - - - + + + + Clear Очистить - - - - - + + + + + [not set] [не задано] - - - Toggle button - Переключить кнопку - - - - + + Invert button Инвертировать кнопку - - + + + Toggle button + Переключить кнопку + + + + Invert axis Инвертировать оси - - - + + + Set threshold Установить порог - - + + Choose a value between 0% and 100% Выберите значение между 0% и 100% - + + Toggle axis + Переключить оси + + + Set gyro threshold Установить порог гироскопа - + Map Analog Stick - Задать аналоговый стик + Задать аналоговый мини-джойстик - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. - После нажатия на ОК, двигайте ваш джойстик горизонтально, а затем вертикально. -Чтобы инвертировать оси, сначала двигайте ваш джойстик вертикально, а затем горизонтально. + После нажатия на ОК, двигайте ваш мини-джойстик горизонтально, а затем вертикально. +Чтобы инвертировать оси, сначала двигайте ваш мини-джойстик вертикально, а затем горизонтально. - + Center axis Центрировать оси - - + + Deadzone: %1% Мёртвая зона: %1% - - + + Modifier Range: %1% Диапазон модификатора: %1% - - + + Pro Controller Контроллер Pro - + Dual Joycons - Двойные Joycon'ы + Двойные Joy-Con'ы - + Left Joycon - Левый Joycon + Левый Joy-Сon - + Right Joycon - Правый Joycon + Правый Joy-Сon - + Handheld Портативный - + GameCube Controller Контроллер GameCube - + Poke Ball Plus Poke Ball Plus - + NES Controller Контроллер NES - + SNES Controller Контроллер SNES - + N64 Controller Контроллер N64 - + Sega Genesis Sega Genesis - + Start / Pause Старт / Пауза - + Z Z - + Control Stick - Стик управления + Мини-джойстик управления - + C-Stick - C-Стик + C-Джойстик - + Shake! Встряхните! - + [waiting] [ожидание] - + New Profile Новый профиль - + Enter a profile name: Введите имя профиля: - - + + Create Input Profile Создать профиль управления - + The given profile name is not valid! Заданное имя профиля недействительно! - + Failed to create the input profile "%1" Не удалось создать профиль управления "%1" - + Delete Input Profile Удалить профиль управления - + Failed to delete the input profile "%1" Не удалось удалить профиль управления "%1" - + Load Input Profile Загрузить профиль управления - + Failed to load the input profile "%1" Не удалось загрузить профиль управления "%1" - + Save Input Profile Сохранить профиль управления - + Failed to save the input profile "%1" Не удалось сохранить профиль управления "%1" @@ -2555,7 +2634,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Configure Motion / Touch - Настройка движения и/или сенсора + Настройка движения и сенсора @@ -2782,42 +2861,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Разработчик - + Add-Ons Дополнения - + General Общие - + System Система - + CPU ЦП - + Graphics Графика - + Adv. Graphics Расш. Графика - + Audio Звук - + Properties Свойства @@ -2873,37 +2952,37 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Текущий пользователь - + Username Имя пользователя - + Set Image Выбрать изображение - + Add Добавить - + Rename Переименовать - + Remove Удалить - + Profile management is available only when game is not running. Управление профилями недоступно пока запущена игра. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2911,96 +2990,105 @@ To invert the axes, first move your joystick vertically, and then horizontally.< %2 - + Enter Username Введите имя пользователя - + Users Пользователи - + Enter a username for the new user: Введите имя пользователя для нового профиля: - + Enter a new username: Введите новое имя пользователя: - - Confirm Delete - Подтвердите удаление - - - - You are about to delete user with name "%1". Are you sure? - Вы собираетесь удалить пользователя "%1". Вы уверены? - - - + Select User Image Выберите изображение пользователя - + JPEG Images (*.jpg *.jpeg) Изображения JPEG (*.jpg, *.jpeg) - + Error deleting image Ошибка при удалении изображения - + Error occurred attempting to overwrite previous image at: %1. Ошибка при попытке перезаписи предыдущего изображения в: %1. - + Error deleting file Ошибка при удалении файла - + Unable to delete existing file: %1. Не удалось удалить существующий файл: %1. - + Error creating user image directory Ошибка при создании папки пользовательских изображений - + Unable to create directory %1 for storing user images. Не получилось создать папку %1 для хранения изображений пользователя. - + Error copying user image Ошибка при копировании изображения пользователя - + Unable to copy image from %1 to %2 Не получилось скопировать изображение из %1 в %2 - + Error resizing user image Ошибка при изменении размера изображения пользователя - + Unable to resize image Невозможно изменить размер изображения + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Подтвердите удаление + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3011,7 +3099,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly. - Если вы хотите использовать этот контроллер, настройте игрока 1 как правый контроллер, а игрока 2 как двойной Joycon перед началом игры, чтобы этот контроллер был обнаружен правильно. + Если вы хотите использовать этот контроллер, настройте игрока 1 как правый контроллер, а игрока 2 как двойной Joy-Сon перед началом игры, чтобы этот контроллер был обнаружен правильно. @@ -3318,7 +3406,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Zulu - Зулус + Зулусы @@ -3529,47 +3617,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< <html><head/><body><p>Считывает входные данные контроллера из скриптов в том же формате, что и скрипты TAS-nx.<br/>Для более подробного объяснения обратитесь к <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">странице помощи</span></a> на сайте yuzu.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Чтобы проверить, какие горячие клавиши управляют воспроизведением/записью, обратитесь к настройкам горячих клавиш (Настройки - Общие -> Горячие клавиши). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. ПРЕДУПРЕЖДЕНИЕ: Это экспериментальная функция.<br/>Она не будет идеально воспроизводить кадры сценариев при текущем несовершенном методе синхронизации. - + Settings Настройки - + Enable TAS features Включить функции TAS - + Loop script Зациклить скрипт - + Pause execution during loads Приостановить выполнение во время загрузки - + Script Directory Папка для скриптов - + Path Путь - + ... ... @@ -3821,56 +3909,71 @@ Drag points to change position, or double-click table cells to edit values. + Show Compatibility List + Показать список совместимости + + + Show Add-Ons Column Показывать столбец дополнений - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: Размер иконки игры: - + Folder Icon Size: Размер иконки папки: - + Row 1 Text: - Строка 1: + Текст 1-ой строки: - + Row 2 Text: - Строка 2: + Текст 2-ой строки: - + Screenshots Скриншоты - + Ask Where To Save Screenshots (Windows Only) Спрашивать куда сохранять скриншоты (Только для Windows) - + Screenshots Path: Папка для скриншотов: - + ... ... - + Select Screenshots Path... Выберите папку для скриншотов... - + <System> <System> @@ -3979,7 +4082,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify Подтвердить @@ -4001,7 +4104,7 @@ Drag points to change position, or double-click table cells to edit values. What is my token? - Что такое токен? + Что такое токен и где его найти? @@ -4056,7 +4159,7 @@ Drag points to change position, or double-click table cells to edit values. <a href='https://yuzu-emu.org/wiki/yuzu-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> - <a href='https://yuzu-emu.org/wiki/yuzu-web-service/'><span style="text-decoration: underline; color:#039be5;">Что такое токен?</span></a> + <a href='https://yuzu-emu.org/wiki/yuzu-web-service/'><span style="text-decoration: underline; color:#039be5;">Что такое токен и где его найти?</span></a> @@ -4066,7 +4169,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified Отсутствует @@ -4081,17 +4184,36 @@ Drag points to change position, or double-click table cells to edit values.Токен не был подтвержден. Изменение вашего токена не было сохранено. - - Verifying... - Проверка... + + Unverified, please click Verify before saving configuration + Tooltip + Не подтверждено, пожалуйста нажмите кнопку Подтвердить прежде чем сохранять конфигурацию. - + + + Verifying... + Подтверждение... + + + + Verified + Tooltip + Потверждён + + + + Verification failed + Tooltip + Ошибка подтверждения + + + Verification failed Ошибка подтверждения - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Ошибка подтверждения. Убедитесь, что вы правильно ввели свой токен и что ваше подключение к Интернету работает. @@ -4160,12 +4282,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting Подключение - + Connect Подключиться @@ -4173,488 +4295,491 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Анонимные данные собираются для того,</a> чтобы помочь улучшить работу yuzu. <br/><br/>Хотели бы вы делиться данными об использовании с нами? - + Telemetry Телеметрия - + Broken Vulkan Installation Detected Обнаружена поврежденная установка Vulkan - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. Не удалось выполнить инициализацию Vulkan во время загрузки.<br><br>Нажмите <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>здесь для получения инструкций по устранению проблемы</a>. - + Loading Web Applet... Загрузка веб-апплета... - - + + Disable Web Applet Отключить веб-апплет - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Отключение веб-апплета может привести к неожиданному поведению и должно использоваться только с Super Mario 3D All-Stars. Вы уверены, что хотите отключить веб-апплет? (Его можно снова включить в настройках отладки.) - + The amount of shaders currently being built Количество создаваемых шейдеров на данный момент - + The current selected resolution scaling multiplier. Текущий выбранный множитель масштабирования разрешения. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Текущая скорость эмуляции. Значения выше или ниже 100% указывают на то, что эмуляция идет быстрее или медленнее, чем на Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Количество кадров в секунду в данный момент. Значение будет меняться между играми и сценами. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Время, которое нужно для эмуляции 1 кадра Switch, не принимая во внимание ограничение FPS или вертикальную синхронизацию. Для эмуляции в полной скорости значение должно быть не больше 16,67 мс. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files [&C] Очистить недавние файлы - + &Continue [&C] Продолжить - + &Pause [&P] Пауза - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping В yuzu запущена игра - + Warning Outdated Game Format Предупреждение устаревший формат игры - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Для этой игры вы используете разархивированный формат ROM'а, который является устаревшим и был заменен другими, такими как NCA, NAX, XCI или NSP. В разархивированных каталогах ROM'а отсутствуют иконки, метаданные и поддержка обновлений. <br><br>Для получения информации о различных форматах Switch, поддерживаемых yuzu, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>просмотрите нашу вики</a>. Это сообщение больше не будет отображаться. - - + + Error while loading ROM! Ошибка при загрузке ROM'а! - + The ROM format is not supported. Формат ROM'а не поддерживается. - + An error occurred initializing the video core. Произошла ошибка при инициализации видеоядра. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - yuzu столкнулся с ошибкой при запуске видеоядра. Обычно это вызвано устаревшими драйверами GPU, включая интегрированные. Проверьте журнал для получения более подробной информации. Дополнительную информацию о доступе к журналу смотрите на следующей странице: <a href='https://yuzu-emu.org/help/reference/log-files/'>Как загрузить файл журнала</a>. + yuzu столкнулся с ошибкой при запуске видеоядра. Обычно это вызвано устаревшими драйверами ГП, включая интегрированные. Проверьте журнал для получения более подробной информации. Дополнительную информацию о доступе к журналу смотрите на следующей странице: <a href='https://yuzu-emu.org/help/reference/log-files/'>Как загрузить файл журнала</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Ошибка при загрузке ROM'а! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Пожалуйста, следуйте <a href='https://yuzu-emu.org/help/quickstart/'>краткому руководству пользователя yuzu</a> чтобы пере-дампить ваши файлы<br>Вы можете обратиться к вики yuzu</a> или Discord yuzu</a> для помощи. - + An unknown error occurred. Please see the log for more details. Произошла неизвестная ошибка. Пожалуйста, проверьте журнал для подробностей. - + (64-bit) (64-х битный) - + (32-bit) (32-х битный) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Сохранения - + Mod Data Данные модов - + Error Opening %1 Folder Ошибка при открытии папки %1 - - + + Folder does not exist! Папка не существует! - + Error Opening Transferable Shader Cache Ошибка при открытии переносного кэша шейдеров - + Failed to create the shader cache directory for this title. Не удалось создать папку кэша шейдеров для этой игры. - - Contents - Содержание + + Error Removing Contents + - - Update - Обновление + + Error Removing Update + - - DLC - Загружаемый контент + + Error Removing DLC + - + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + Remove Entry Удалить запись - - Remove Installed Game %1? - Удалить установленную игру %1? - - - - - - - - + + + + + + Successfully Removed Успешно удалено - + Successfully removed the installed base game. Установленная игра успешно удалена. - - - - Error Removing %1 - Ошибка при удалении %1 - - - + The base game is not installed in the NAND and cannot be removed. Игра не установлена в NAND и не может быть удалена. - + Successfully removed the installed update. Установленное обновление успешно удалено. - + There is no update installed for this title. Для этой игры не было установлено обновление. - + There are no DLC installed for this title. Для этой игры не был установлен загружаемый контент. - + Successfully removed %1 installed DLC. Установленный загружаемый контент %1 был успешно удалён - + Delete OpenGL Transferable Shader Cache? Удалить переносной кэш шейдеров OpenGL? - + Delete Vulkan Transferable Shader Cache? Удалить переносной кэш шейдеров Vulkan? - + Delete All Transferable Shader Caches? Удалить весь переносной кэш шейдеров? - + Remove Custom Game Configuration? Удалить пользовательскую настройку игры? - + Remove File Удалить файл - - + + Error Removing Transferable Shader Cache Ошибка при удалении переносного кэша шейдеров - - + + A shader cache for this title does not exist. Кэш шейдеров для этой игры не существует. - + Successfully removed the transferable shader cache. Переносной кэш шейдеров успешно удалён. - + Failed to remove the transferable shader cache. Не удалось удалить переносной кэш шейдеров. - - + + Error Removing Transferable Shader Caches Ошибка при удалении переносного кэша шейдеров - + Successfully removed the transferable shader caches. Переносной кэш шейдеров успешно удален. - + Failed to remove the transferable shader cache directory. Ошибка при удалении папки переносного кэша шейдеров. - - + + Error Removing Custom Configuration Ошибка при удалении пользовательской настройки - + A custom configuration for this title does not exist. Пользовательская настройка для этой игры не существует. - + Successfully removed the custom game configuration. Пользовательская настройка игры успешно удалена. - + Failed to remove the custom game configuration. Не удалось удалить пользовательскую настройку игры. - - + + RomFS Extraction Failed! Не удалось извлечь RomFS! - + There was an error copying the RomFS files or the user cancelled the operation. Произошла ошибка при копировании файлов RomFS или пользователь отменил операцию. - + Full Полный - + Skeleton Скелет - + Select RomFS Dump Mode Выберите режим дампа RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Пожалуйста, выберите, как вы хотите выполнить дамп RomFS. <br>Полный скопирует все файлы в новую папку, в то время как <br>скелет создаст только структуру папок. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root В %1 недостаточно свободного места для извлечения RomFS. Пожалуйста, освободите место или выберите другую папку для дампа в Эмуляция > Настройка > Система > Файловая система > Корень дампа - + Extracting RomFS... Извлечение RomFS... - - + + Cancel Отмена - + RomFS Extraction Succeeded! Извлечение RomFS прошло успешно! - + The operation completed successfully. Операция выполнена. - + Error Opening %1 Ошибка открытия %1 - + Select Directory Выбрать папку - + Properties Свойства - + The game properties could not be loaded. Не удалось загрузить свойства игры. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Исполняемый файл Switch (%1);;Все файлы (*.*) - + Load File Загрузить файл - + Open Extracted ROM Directory Открыть папку извлечённого ROM'а - + Invalid Directory Selected Выбрана недопустимая папка - + The directory you have selected does not contain a 'main' file. Папка, которую вы выбрали, не содержит файла 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Устанавливаемый файл Switch (*.nca, *.nsp, *.xci);;Архив контента Nintendo (*.nca);;Пакет подачи Nintendo (*.nsp);;Образ картриджа NX (*.xci) - + Install Files Установить файлы - + %n file(s) remaining Остался %n файлОсталось %n файл(ов)Осталось %n файл(ов)Осталось %n файл(ов) - + Installing file "%1"... Установка файла "%1"... - - + + Install Results - Установить результаты + Результаты установки - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Чтобы избежать возможных конфликтов, мы не рекомендуем пользователям устанавливать игры в NAND. Пожалуйста, используйте эту функцию только для установки обновлений и загружаемого контента. - + %n file(s) were newly installed %n файл был недавно установлен @@ -4664,7 +4789,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) were overwritten %n файл был перезаписан @@ -4674,7 +4799,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) failed to install %n файл не удалось установить @@ -4684,411 +4809,410 @@ Please, only use this feature to install updates and DLC. - + System Application Системное приложение - + System Archive Системный архив - + System Application Update Обновление системного приложения - + Firmware Package (Type A) Пакет прошивки (Тип А) - + Firmware Package (Type B) Пакет прошивки (Тип Б) - + Game Игра - + Game Update Обновление игры - + Game DLC Загружаемый контент игры - + Delta Title Дельта-титул - + Select NCA Install Type... Выберите тип установки NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Пожалуйста, выберите тип приложения, который вы хотите установить для этого NCA: (В большинстве случаев, подходит стандартный выбор «Игра».) - + Failed to Install Ошибка установки - + The title type you selected for the NCA is invalid. Тип приложения, который вы выбрали для NCA, недействителен. - + File not found Файл не найден - + File "%1" not found Файл "%1" не найден - + OK ОК - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Отсутствует аккаунт yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Чтобы отправить отчет о совместимости игры, необходимо привязать свою учетную запись yuzu.<br><br/>Чтобы привязать свою учетную запись yuzu, перейдите в раздел Эмуляция &gt; Параметры &gt; Сеть. - + Error opening URL Ошибка при открытии URL - + Unable to open the URL "%1". Не удалось открыть URL: "%1". - + TAS Recording Запись TAS - + Overwrite file of player 1? Перезаписать файл игрока 1? - + Invalid config detected Обнаружена недопустимая конфигурация - + Handheld controller can't be used on docked mode. Pro controller will be selected. Портативный контроллер не может быть использован в режиме док-станции. Будет выбран контроллер Pro. - - - Error - Ошибка - - - - - The current game is not looking for amiibos - Текущая игра не ищет amiibo - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed Текущий amiibo был убран - + + Error + Ошибка + + + + + The current game is not looking for amiibos + Текущая игра не ищет amiibo + + + Amiibo File (%1);; All Files (*.*) Файл Amiibo (%1);; Все Файлы (*.*) - + Load Amiibo Загрузить Amiibo - - Error opening Amiibo data file - Ошибка открытия файла данных Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Невозможно открыть файл Amiibo "%1" для чтения. - - - - Error reading Amiibo data file - Ошибка чтения файла данных Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Невозможно полностью прочитать данные Amiibo. Ожидалось прочитать %1 байт, но удалось прочитать только %2 байт. - - - + Error loading Amiibo data Ошибка загрузки данных Amiibo - - Unable to load Amiibo data. - Невозможно загрузить данные Amiibo. + + The selected file is not a valid amiibo + Выбранный файл не является допустимым amiibo - + + The selected file is already on use + Выбранный файл уже используется + + + + An unknown error occurred + Произошла неизвестная ошибка + + + Capture Screenshot Сделать скриншот - + PNG Image (*.png) Изображение PNG (*.png) - + TAS state: Running %1/%2 Состояние TAS: Выполняется %1/%2 - + TAS state: Recording %1 Состояние TAS: Записывается %1 - + TAS state: Idle %1/%2 Состояние TAS: Простой %1/%2 - + TAS State: Invalid Состояние TAS: Неверное - + &Stop Running [&S] Остановка - + &Start [&S] Начать - + Stop R&ecording [&E] Закончить запись - + R&ecord [&E] Запись - + Building: %n shader(s) Постройка: %n шейдерПостройка: %n шейдер(ов)Постройка: %n шейдер(ов)Постройка: %n шейдер(ов) - + Scale: %1x %1 is the resolution scaling factor Масштаб: %1x - + Speed: %1% / %2% Скорость: %1% / %2% - + Speed: %1% Скорость: %1% - + Game: %1 FPS (Unlocked) Игра: %1 FPS (Неограниченно) - + Game: %1 FPS Игра: %1 FPS - + Frame: %1 ms Кадр: %1 мс - + GPU NORMAL ГП НОРМАЛЬНО - + GPU HIGH ГП ВЫСОКО - + GPU EXTREME ГП ЭКСТРИМ - + GPU ERROR ГП ОШИБКА - + DOCKED В ДОК-СТАНЦИИ - + HANDHELD ПОРТАТИВНЫЙ - + NEAREST БЛИЖАЙШИЙ - - + + BILINEAR БИЛИНЕЙНЫЙ - + BICUBIC БИКУБИЧЕСКИЙ - + GAUSSIAN ГАУСС - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA БЕЗ СГЛАЖИВАНИЯ - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Игра, которую вы пытаетесь загрузить, требует, чтобы дополнительные файлы были сдамплены с вашего Switch перед началом игры. <br/><br/>Для получения дополнительной информации о дампе этих файлов см. следующую вики: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Дамп системных архивов и общих шрифтов с консоли</a>. <br/><br/>Хотите вернуться к списку игр? Продолжение эмуляции может привести к сбоям, повреждению сохраненных данных или другим ошибкам. - + yuzu was unable to locate a Switch system archive. %1 yuzu не удалось найти системный архив Switch. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu не удалось найти системный архив Switch: %1. %2 - + System Archive Not Found Системный архив не найден - + System Archive Missing Отсутствует системный архив - + yuzu was unable to locate the Switch shared fonts. %1 yuzu не удалось найти общие шрифты Switch. %1 - + Shared Fonts Not Found Общие шрифты не найдены - + Shared Font Missing Общие шрифты отсутствуют - + Fatal Error Фатальная ошибка - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu столкнулся с фатальной ошибкой, проверьте журнал для получения более подробной информации. Для получения дополнительной информации о доступе к журналу откройте следующую страницу: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Как загрузить файл журнала</a>.<br/><br/>Вы хотите вернуться к списку игр? Продолжение эмуляции может привести к сбоям, повреждению сохранений или другим ошибкам. - + Fatal Error encountered Произошла фатальная ошибка - + Confirm Key Rederivation Подтвердите перерасчет ключа - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5105,37 +5229,37 @@ This will delete your autogenerated key files and re-run the key derivation modu Это удалит ваши автоматически сгенерированные файлы ключей и повторно запустит модуль расчета ключей. - + Missing fuses Отсутствуют предохранители - + - Missing BOOT0 - Отсутствует BOOT0 - + - Missing BCPKG2-1-Normal-Main - Отсутствует BCPKG2-1-Normal-Main - + - Missing PRODINFO - Отсутствует PRODINFO - + Derivation Components Missing Компоненты расчета отсутствуют - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Ключи шифрования отсутствуют. <br>Пожалуйста, следуйте <a href='https://yuzu-emu.org/help/quickstart/'>краткому руководству пользователя yuzu</a>, чтобы получить все ваши ключи, прошивку и игры.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5144,39 +5268,39 @@ on your system's performance. от производительности вашей системы. - + Deriving Keys Получение ключей - + Select RomFS Dump Target Выберите цель для дампа RomFS - + Please select which RomFS you would like to dump. Пожалуйста, выберите, какой RomFS вы хотите сдампить. - + Are you sure you want to close yuzu? Вы уверены, что хотите закрыть yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Вы уверены, что хотите остановить эмуляцию? Любой несохраненный прогресс будет потерян. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5188,38 +5312,38 @@ Would you like to bypass this and exit anyway? GRenderWindow - + OpenGL not available! OpenGL не доступен! - + yuzu has not been compiled with OpenGL support. yuzu не был скомпилирован с поддержкой OpenGL. - - + + Error while initializing OpenGL! Ошибка при инициализации OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. Ваш ГП может не поддерживать OpenGL, или у вас установлен устаревший графический драйвер. - + Error while initializing OpenGL 4.6! Ошибка при инициализации OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 Ваш ГП может не поддерживать OpenGL 4.6, или у вас установлен устаревший графический драйвер.<br><br>Рендерер GL:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 Ваш ГП может не поддерживать одно или несколько требуемых расширений OpenGL. Пожалуйста, убедитесь в том, что у вас установлен последний графический драйвер.<br><br>Рендерер GL:<br>%1<br><br>Неподдерживаемые расширения:<br>%2 @@ -5227,153 +5351,153 @@ Would you like to bypass this and exit anyway? GameList - + Favorite Избранное - + Start Game Запустить игру - + Start Game without Custom Configuration Запустить игру без пользовательской настройки - + Open Save Data Location Открыть папку для сохранений - + Open Mod Data Location Открыть папку для модов - + Open Transferable Pipeline Cache Открыть переносной кэш конвейера - + Remove Удалить - + Remove Installed Update Удалить установленное обновление - + Remove All Installed DLC Удалить весь установленный загружаемый контент - + Remove Custom Configuration Удалить пользовательскую настройку - + Remove OpenGL Pipeline Cache Удалить кэш конвейера OpenGL - + Remove Vulkan Pipeline Cache Удалить кэш конвейера Vulkan - + Remove All Pipeline Caches Удалить весь кэш конвейеров - + Remove All Installed Contents Удалить все установленное содержимое - + Dump RomFS Дамп RomFS - + Dump RomFS to SDMC Сдампить RomFS в SDMC - + Copy Title ID to Clipboard Скопировать идентификатор приложения в буфер обмена - + Navigate to GameDB entry Перейти к странице GameDB - + Properties Свойства - + Scan Subfolders Сканировать подпапки - + Remove Game Directory Удалить папку с играми - + ▲ Move Up ▲ Переместить вверх - + ▼ Move Down ▼ Переместить вниз - + Open Directory Location Открыть расположение папки - + Clear Очистить - + Name Имя - + Compatibility Совместимость - + Add-ons Дополнения - + File type Тип файла - + Size Размер @@ -5382,81 +5506,61 @@ Would you like to bypass this and exit anyway? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Идеально - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - Игра работает безупречно, без звуковых или графических артефактов, все протестированные функции работают без -обходных путей. - - - - Great - Отлично - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - Игра работает с небольшими графическими или звуковыми артефактами и может быть -пройдена от начала до конца. Могут потребоваться обходные пути. - - Okay - Нормально - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - Игра работает с существенными графическими или звуковыми артефактами, но может -быть пройдена с использованием обходных путей. + Game can be played without issues. + - Bad - Плохо + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Игра работает, но с существенными графическими или звуковыми артефактами. -В некоторых частях невозможно продвинуться даже с обходными путями. + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu Вступление/Меню - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - В игру невозможно играть из-за графических или звуковых артефактов. -Невозможно продвинуться дальше стартового экрана. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Не запускается - + The game crashes when attempting to startup. Игра вылетает при запуске. - + Not Tested Не проверено - + The game has not yet been tested. Игру ещё не проверяли на совместимость. @@ -5464,7 +5568,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Нажмите дважды, чтобы добавить новую папку в список игр @@ -5477,12 +5581,12 @@ Screen. %1 из %n результат(ов)%1 из %n результат(ов)%1 из %n результат(ов)%1 из %n результат(ов) - + Filter: Поиск: - + Enter pattern to filter Введите текст для поиска @@ -5552,18 +5656,18 @@ Screen. Host Room - Комната хоста + Создать комнату HostRoomWindow - + Error Ошибка - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: Не удалось объявить комнату в публичном лобби. Чтобы хостить публичную комнату, у вас должна быть действующая учетная запись yuzu, настроенная в Эмуляция -> Параметры -> Сеть. Если вы не хотите объявлять комнату в публичном лобби, выберите вместо этого скрытый тип. @@ -5573,11 +5677,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute Включение/отключение звука + @@ -5599,112 +5704,111 @@ Debug Message: - Main Window Основное окно - + Audio Volume Down Уменьшить громкость звука - + Audio Volume Up Повысить громкость звука - + Capture Screenshot Сделать скриншот - + Change Adapting Filter Изменить адаптирующий фильтр - + Change Docked Mode Изменить режим консоли - + Change GPU Accuracy Изменить точность ГП - + Continue/Pause Emulation Продолжение/Пауза эмуляции - + Exit Fullscreen Выйти из полноэкранного режима - + Exit yuzu Выйти из yuzu - + Fullscreen Полный экран - + Load File Загрузить файл - + Load/Remove Amiibo Загрузить/удалить Amiibo - + Restart Emulation Перезапустить эмуляцию - + Stop Emulation Остановить эмуляцию - + TAS Record Запись TAS - + TAS Reset Сброс TAS - + TAS Start/Stop Старт/Стоп TAS - + Toggle Filter Bar Переключить панель поиска - + Toggle Framerate Limit Переключить ограничение частоты кадров - + Toggle Mouse Panning Переключить панорамирование мыши - + Toggle Status Bar Переключить панель состояния @@ -5819,42 +5923,42 @@ Debug Message: Обновить лобби - + Password Required to Join Для входа необходим пароль - + Password: Пароль: - - Room Name - Название комнаты - - - - Preferred Game - Предпочтительная игра - - - - Host - Хост - - - + Players Игроки - + + Room Name + Название комнаты + + + + Preferred Game + Предпочтительная игра + + + + Host + Хост + + + Refreshing Обновление - + Refresh List Обновить список @@ -5877,232 +5981,237 @@ Debug Message: [&R] Недавние файлы - + &Emulation [&E] Эмуляция - + &View [&V] Вид - + &Reset Window Size [&R] Сбросить размер окна - + &Debugging [&D] Отладка - + Reset Window Size to &720p Сбросить размер окна до &720p - + Reset Window Size to 720p Сбросить размер окна до 720p - + Reset Window Size to &900p Сбросить размер окна до &900p - + Reset Window Size to 900p Сбросить размер окна до 900p - + Reset Window Size to &1080p Сбросить размер окна до &1080p - + Reset Window Size to 1080p Сбросить размер окна до 1080p - + + &Multiplayer + [&M] Мультиплеер + + + &Tools [&T] Инструменты - + &TAS [&T] TAS - + &Help [&H] Помощь - + &Install Files to NAND... [&I] Установить файлы в NAND... - + L&oad File... [&O] Загрузить файл... - + Load &Folder... [&F] Загрузить папку... - + E&xit [&X] Выход - + &Pause [&P] Пауза - + &Stop [&S] Стоп - + &Reinitialize keys... [&R] Переинициализировать ключи... - + &About yuzu [&A] О yuzu - + Single &Window Mode [&W] Режим одного окна - + Con&figure... [&F] Параметры... - + Display D&ock Widget Headers [&O] Отображать заголовки виджетов дока - + Show &Filter Bar [&F] Показать панель поиска - + Show &Status Bar [&S] Показать панель статуса - + Show Status Bar Показать панель статуса - - - Browse Public Game Lobby - Просмотреть публичные игровые лобби - - - - Create Room - Создать комнату - - Leave Room - Покинуть комнату + &Browse Public Game Lobby + [&B] Просмотреть публичные игровые лобби - - Direct Connect to Room - Прямое подключение к комнате + + &Create Room + [&C] Создать комнату - - Show Current Room - Показать текущую комнау + + &Leave Room + [&L] Покинуть комнату - F&ullscreen - [&U] Полнокэранный + &Direct Connect to Room + [&D] Прямое подключение к комнате + &Show Current Room + [&S] Показать текущую комнату + + + + F&ullscreen + [&U] Полноэкранный + + + &Restart [&R] Перезапустить - + Load/Remove &Amiibo... [&A] Загрузить/Удалить Amiibo... - + &Report Compatibility [&R] Сообщить о совместимости - + Open &Mods Page [&M] Открыть страницу модов - + Open &Quickstart Guide [&Q] Открыть руководство пользователя - + &FAQ [&F] ЧАВО - + Open &yuzu Folder [&Y] Открыть папку yuzu - + &Capture Screenshot [&C] Сделать скриншот - + &Configure TAS... [&C] Настройка TAS... - + Configure C&urrent Game... [&U] Настроить текущую игру... - + &Start [&S] Запустить - + &Reset [&S] Сбросить - + R&ecord [&E] Запись @@ -6167,47 +6276,42 @@ Debug Message: MultiplayerState - - + Current connection status Текущий статус подключения - - + Not Connected. Click here to find a room! Не подключено. Нажмите здесь, чтобы найти комнату! - - - Connected - Подключено - - - - Not Connected Не подключено - + + Connected + Подключено + + + + New Messages Received + Получены новые сообщения + + + Error Ошибка - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: Не удалось обновить информацию о комнате. Пожалуйста, проверьте подключение к Интернету и попробуйте снова захостить комнату. Сообщение отладки: - - - New Messages Received - Получены новые сообщения - NetworkMessage @@ -6264,7 +6368,7 @@ Debug Message: The host of the room has banned you. Speak with the host to unban you or try a different room. - Хозяин комнаты забанил вас. Поговорите с хостом, чтобы он разбанил вас, или попробуйте другую комнату. + Хост комнаты забанил вас. Поговорите с хостом, чтобы он разбанил вас, или попробуйте другую комнату. @@ -6294,7 +6398,7 @@ Debug Message: IP address is already in use. Please choose another. - + IP-адрес уже используется. Пожалуйста, выберите другой. @@ -6309,22 +6413,41 @@ They may have left the room. Возможно, они покинули комнату. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + Не выбран допустимый интерфейс сети. +Пожалуйста, перейдите в Параметры -> Система -> Сеть и сделайте выбор. + + + + Game already running + Игра уже запущена + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + Присоединяться к комнате, когда игра уже запущена, не рекомендуется, это может привести к неправильной работе функции комнаты. +Все равно продолжить? + + + Leave Room Покинуть комнату - + You are about to close the room. Any network connections will be closed. Вы собираетесь закрыть комнату. Все сетевые подключения будут закрыты. - + Disconnect Отключиться - + You are about to leave the room. Any network connections will be closed. Вы собираетесь покинуть комнату. Все сетевые подключения будут закрыты. @@ -6332,7 +6455,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error Ошибка @@ -6381,42 +6504,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game %1 не играет в игру - + %1 is playing %2 %1 играет в %2 - + Not playing a game Не играет в игру - + Installed SD Titles Установленные SD игры - + Installed NAND Titles Установленные NAND игры - + System Titles Системные игры - + Add New Game Directory Добавить новую папку с играми - + Favorites Избранные @@ -6446,7 +6569,7 @@ p, li { white-space: pre-wrap; } - + [not set] [не задано] @@ -6461,10 +6584,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Ось %1%2 @@ -6478,9 +6601,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [неизвестно] @@ -6645,15 +6768,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [недопустимо] - - + + %1%2Hat %3 %1%2Крест. %3 @@ -6661,42 +6784,42 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Ось %3 - + %1%2Axis %3,%4,%5 %1%2Ось %3,%4,%5 - + %1%2Motion %3 %1%2Движение %3 - - + + %1%2Button %3 %1%2Кнопка %3 - + [unused] [не используется] Home - Домой + Home @@ -6707,7 +6830,7 @@ p, li { white-space: pre-wrap; } Wheel Indicates the mouse wheel - Колёсико мыши + Колёсико @@ -6730,11 +6853,124 @@ p, li { white-space: pre-wrap; } Дополнительная - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + Тип + + + + Name + Название + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6786,7 +7022,7 @@ p, li { white-space: pre-wrap; } Dual Joycons - Двойные Joycon'ы + Двойные Joy-Сon'ы @@ -6799,7 +7035,7 @@ p, li { white-space: pre-wrap; } Left Joycon - Левый Joycon + Левый Joy-Сon @@ -6812,7 +7048,7 @@ p, li { white-space: pre-wrap; } Right Joycon - Правый Joycon + Правый Joy-Сon @@ -6838,6 +7074,7 @@ p, li { white-space: pre-wrap; } + Handheld Портативный @@ -6877,11 +7114,6 @@ p, li { white-space: pre-wrap; } Docked В док-станции - - - Undocked - Не в док-станции - Vibration @@ -7110,17 +7342,17 @@ p, li { white-space: pre-wrap; } waiting for mutex 0x%1 - ожидание мьютекса 0x%1 + has waiters: %1 - ожидающие: %1 + owner handle: 0x%1 - ссылка на владельца: 0x%1 + @@ -7128,12 +7360,12 @@ p, li { white-space: pre-wrap; } waiting for all objects - в ожидании всех объектов + waiting for one of the following objects - в ожидании одного из объектов + @@ -7146,7 +7378,7 @@ p, li { white-space: pre-wrap; } waited by no thread - не ожидается потоком + @@ -7154,62 +7386,62 @@ p, li { white-space: pre-wrap; } runnable - запускаемый + paused - приостановлен + sleeping - сон + waiting for IPC reply - ожидание ответа IPC + waiting for objects - ожидание объектов + waiting for condition variable - ожидание условной переменной + waiting for address arbiter - ожидание адресного арбитра + waiting for suspend resume - ожидание приостановки возобновления + waiting - ожидание + initialized - инициализированный + terminated - прекращено + unknown - неизвестно + @@ -7219,47 +7451,47 @@ p, li { white-space: pre-wrap; } ideal - идеально + core %1 - ядро %1 + processor = %1 - процессор = %1 + ideal core = %1 - идеальное ядро = %1 + affinity mask = %1 - маска сходства = %1 + thread id = %1 - id потока = %1 + priority = %1(current) / %2(normal) - приоритет = %1(текущий) / %2(обычный) + last running ticks = %1 - последние тики = %1 + not waiting for mutex - не ожидает мьютекс + @@ -7267,7 +7499,7 @@ p, li { white-space: pre-wrap; } waited by thread - ожидается потоком + diff --git a/dist/languages/sv.ts b/dist/languages/sv.ts index 00d08a5..84703a1 100644 --- a/dist/languages/sv.ts +++ b/dist/languages/sv.ts @@ -14,7 +14,7 @@ <html><head/><body><p>%1 (%2)</p></body></html> - + <html><head/><body><p>%1 (%2)</p></body></html> @@ -25,12 +25,18 @@ p, li { white-space: pre-wrap; } <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">This software should not be used to play games you have not legally obtained.</span></p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu är en experimentell Nintendo Switch emulator byggd på öppen källkod licenserad under GPL.3.0+.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Denna mjukvara bör inte användas för att spela spel som du inte har förvärvat på laglig väg.</span></p></body></html> <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Hemsida</span></a>I<a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Källkod</span></a>I<a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Bidragsgivare</span></a>I<a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Licens</span></a></p></body></html> @@ -76,95 +82,97 @@ p, li { white-space: pre-wrap; } Room Window - + Rumsfönster Send Chat Message - + Skicka Chat- meddelande Send Message - + Skicka meddelande - + Members - + Medlemmar - + %1 has joined - + %1 har anslutit - + %1 has left - + %1 har lämnat - + %1 has been kicked - + %1 har blivit utkastad - + %1 has been banned - + %1 har blivit bannlyst - + %1 has been unbanned - + View Profile - - - - - - Block Player - - - - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - - - - - Kick - - - - - Ban - + Se Profil + - Kick Player - + Block Player + Blockera Spelare + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + När du blockerar en spelare så kommer du inte längre att kunna ta emot chat-meddelanden från denne. <br><br>Är du säker på att du vill blockera %1? + + + + Kick + Kasta ut + + + + Ban + Bannlys + + + + Kick Player + Kasta ut Spelare + + + Are you sure you would like to <b>kick</b> %1? - + Är du säker på att du vill <b>kasta ut</b> %1? - + Ban Player - + Bannlys Spelare - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - + Är du säker på att du vill <b>sparka ut och bannlysa</b> %1? + +Detta kommer bannlysa både dennes användarnamn på forum samt IP-adress. @@ -172,22 +180,22 @@ This would ban both their forum username and their IP address. Room Window - + Rumsfönster Room Description - + Rumsbeskrivning Moderation... - + Moderering... Leave Room - + Lämna Rum @@ -195,17 +203,17 @@ This would ban both their forum username and their IP address. Connected - Kopplad + Uppkopplad Disconnected - + Nedkopplad - %1 (%2/%3 members) - connected - + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 medlemmar) - Uppkopplad @@ -218,6 +226,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Rapportera Spelkompatibilitet @@ -227,92 +240,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Skulle du välja att skicka in ett testfall till </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">yuzus Kompatibilitetslista </span></a><span style=" font-size:10pt;">, så kommer följande information sparas och visas på sidan: </span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> Hårdvaruinformation (CPU / GPU / Operativsystem) </li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Vilken version av yuzu du använder </li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Det anslutna yuzu kontot </li></ul></body></html> - - Perfect - Perfekt + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Spelet körs perfekt utan ljud- eller grafikproblem.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Utmärkt + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Spelet körs från början till slut med ett fåtal ljud eller grafikproblem. Kan kräva några ändringar.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Okej + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <head/><body><p><html>Spelet körs med stora ljud- eller grafikproblem men går att spela från början till slut med några ändringar. </p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Dåligt + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Spelet körs men med stora ljud- eller grafikproblem. Går inte att spela klart, även med ändringar. </p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Intro/Meny + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Spelet går inte att köra på grund utav stora ljud- eller grafikproblem. Kommer inte förbi startskärmen. </p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Startar Inte + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Spelet kraschar när man försöker starta det. </p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Oberoende av hastighet eller prestanda, hur bra körs spelet från början till slut på denna version av yuzu? </p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Tack för din feedback! - + Submitting Skickar in - + Communication error Kommunikationsfel - + An error occurred while sending the Testcase - + Ett fel inträffade medan Testcase skickades - + Next Nästa @@ -372,37 +420,37 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + Konfigurera Infraröd Kamera Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + Välj var bilden från den emulerade kameran kommer från. Det kommer vara antingen en virtuell kamera eller en riktig kamera. Camera Image Source: - + Källa för Kamerabild: Input device: - + Inmatningsenhet: Preview - + Förhandsgranskning Resolution: 320*240 - + Upplösning: 320*240 Click to preview - + Klicka för förhandsgranskning @@ -410,7 +458,7 @@ This would ban both their forum username and their IP address. Återställ till standard - + Auto Auto @@ -455,12 +503,12 @@ This would ban both their forum username and their IP address. Paranoid (disables most optimizations) - + Paranoid (stänger av de flesta optimeringar) We recommend setting accuracy to "Auto". - + Vi rekommenderar att sätta noggrannhet till "Auto". @@ -489,7 +537,9 @@ This would ban both their forum username and their IP address. <div>This option improves the speed of some approximate floating-point functions by using less accurate native approximations.</div> - <div>Denna inställning förbättrar hastigheten av vissa ungefärliga flyttalsfunktioner genom att använda mindre noggranna nativa approximationer </div> + + <div>Denna inställning förbättrar hastigheten av vissa ungefärliga flyttalsfunktioner genom att använda mindre noggranna nativa approximationer </div> + @@ -501,12 +551,13 @@ This would ban both their forum username and their IP address. <div>This option improves the speed of 32 bits ASIMD floating-point functions by running with incorrect rounding modes.</div> - + + <div> Detta val förbättrar farten för 32-bitars ASIMD flyttalsfunktioner genom att köra med felaktiga avrundningslägen. Faster ASIMD instructions (32 bits only) - + Snabbare ASIMD instruktioner (enbart 32-bitars) @@ -570,7 +621,7 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-weight:600;">For debugging only.</span><br/>If you're not sure what these do, keep all of these enabled. <br/>These settings, when disabled, only take effect when CPU Debugging is enabled. </p></body></html> - + <html><head/><body><p><span style=" font-weight:600;">Endast för felsökning.</span><br/> Om du inte är säker på vad dessa gör, behåll alla dessa påslagna.<br/> Dessa inställningar, när avslagna, kommer bara att ha effekt när CPU-felsökning är påslagen.</p></body></html> @@ -740,200 +791,235 @@ avgjord kod.</div> ConfigureDebug - + Debugger - + Felsökare - + Enable GDB Stub Aktivera GDB Stub - + Port: Port: - + Logging Loggning - + Global Log Filter Globalt Loggfilter - + Show Log in Console - + Visa Logg i Terminal - + Open Log Location Öppna Logg-Destination - + When checked, the max size of the log increases from 100 MB to 1 GB - + När ibockad, ökar maxstorleken för loggen från 100 MB till 1 GB - + Enable Extended Logging** - + Slå på Utökad Loggning** - + Homebrew Homebrew - + Arguments String Argumentsträng - + Graphics Grafik - + When checked, the graphics API enters a slower debugging mode - + När ibockad så går grafik API:et in i ett långsammare felsökningsläge - + Enable Graphics Debugging Sätt på grafikdebugging - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - + Disable Macro JIT Stäng av Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Felsökning - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Avancerat - + Kiosk (Quest) Mode Kiosk(Quest)-läge - + Enable CPU Debugging - + Enable Debug Asserts - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1037,7 +1123,7 @@ avgjord kod.</div> Network - + Nätverk @@ -1227,7 +1313,7 @@ avgjord kod.</div> Extended memory layout (6GB DRAM) - + Utökad minnesöversikt (6GB DRAM) @@ -1257,7 +1343,7 @@ avgjord kod.</div> Reset All Settings - + Återställ Alla Inställningar @@ -1303,193 +1389,218 @@ avgjord kod.</div> API: - + Graphics Settings Grafikinställningar - + Use disk pipeline cache - + Use asynchronous GPU emulation Använd asynkron GPU-emulering - + Accelerate ASTC texture decoding - + NVDEC emulation: - + No Video Output - + CPU Video Decoding - + GPU Video Decoding (Default) - + Fullscreen Mode: - + Borderless Windowed - + Exclusive Fullscreen - + Aspect Ratio: Bildförhållande: - + Default (16:9) Standard (16:9) - + Force 4:3 Tvinga 4:3 - + Force 21:9 Tvinga 21:9 - + + Force 16:10 + + + + Stretch to Window Tänj över fönster - + Resolution: - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) - + 2X (1440p/2160p) - + 3X (2160p/3240p) - + 4X (2880p/4320p) - + 5X (3600p/5400p) - + 6X (4320p/6480p) - + Window Adapting Filter: - + Nearest Neighbor - + Bilinear - + Bicubic - + Gaussian - + ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) - + Anti-Aliasing Method: - + None Ingen - + FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Använd global bakgrundsfärg - + Set background color: Sätt backgrundsfärg: - + Background Color: Bakgrundsfärg: @@ -1498,6 +1609,12 @@ avgjord kod.</div> GLASM (Assembly Shaders, NVIDIA Only) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1552,37 +1669,47 @@ avgjord kod.</div> - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotropisk filtrering: - + Automatic - + Default Standard - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2074,7 +2201,7 @@ avgjord kod.</div> - + Left Stick Vänster Spak @@ -2168,14 +2295,14 @@ avgjord kod.</div> - + L L - + ZL ZL @@ -2194,7 +2321,7 @@ avgjord kod.</div> - + Plus Pluss @@ -2207,15 +2334,15 @@ avgjord kod.</div> - + R R - + ZR ZR @@ -2272,230 +2399,235 @@ avgjord kod.</div> - + Right Stick Höger Spak - - - - + + + + Clear Rensa - - - - - + + + + + [not set] [ej angett] - - - Toggle button - - - - - + + Invert button - - + + + Toggle button + + + + + Invert axis - - - + + + Set threshold - - + + Choose a value between 0% and 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. - + Center axis - - + + Deadzone: %1% Dödzon: %1% - - + + Modifier Range: %1% Modifieringsräckvidd: %1% - - + + Pro Controller Prokontroller - + Dual Joycons Dubbla Joycons - + Left Joycon Vänster Joycon - + Right Joycon Höger Joycon - + Handheld Handhållen - + GameCube Controller - + GameCube-kontroll - + Poke Ball Plus - + Poke Ball Plus - + NES Controller - + NES-kontroll - + SNES Controller - + SNES-kontroll - + N64 Controller - + N64-kontroll - + Sega Genesis - + Sega Genesis - + Start / Pause - + Z - + Z - + Control Stick - + C-Stick - + Shake! - + [waiting] [väntar] - + New Profile Ny profil - + Enter a profile name: - - + + Create Input Profile - + The given profile name is not valid! - + Failed to create the input profile "%1" - + Delete Input Profile - + Failed to delete the input profile "%1" - + Load Input Profile - + Failed to load the input profile "%1" - + Save Input Profile - + Failed to save the input profile "%1" @@ -2684,7 +2816,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Network - + Nätverk @@ -2750,42 +2882,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Utvecklare - + Add-Ons Tillägg - + General Allmänt - + System System - + CPU CPU - + Graphics Grafik - + Adv. Graphics Avancerade Grafikinställningar - + Audio Ljud - + Properties egenskaper @@ -2841,37 +2973,37 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Nuvarande användare - + Username Användarnamn - + Set Image Välj bild - + Add Lägg till - + Rename Döp om - + Remove Ta bort - + Profile management is available only when game is not running. Profilhantering är endast tillgänglig när spelet inte körs. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2879,96 +3011,105 @@ To invert the axes, first move your joystick vertically, and then horizontally.< %2 - + Enter Username Skriv in användarnamn - + Users Användare - + Enter a username for the new user: Skriv in användarnamn för den nya användaren: - + Enter a new username: Skriv in ett nytt användarnamn: - - Confirm Delete - Bekräfta Radering - - - - You are about to delete user with name "%1". Are you sure? - Du håller på att radera användaren med namn "%1". Är du säker? - - - + Select User Image Välj Användarbild - + JPEG Images (*.jpg *.jpeg) JPEG-bilder (*.jpg *.jpeg) - + Error deleting image Fel när bilden raderades - + Error occurred attempting to overwrite previous image at: %1. Fel uppstod när man försökte överskriva föregående bild vid: %1. - + Error deleting file Fel när fil raderades - + Unable to delete existing file: %1. Kan inte radera existerande fil: %1. - + Error creating user image directory Fel när användarbild skapades - + Unable to create directory %1 for storing user images. Oförmögen att skapa katalog %1 för att spara användarbilder. - + Error copying user image Fel under kopiering av användarbild - + Unable to copy image from %1 to %2 Oförmögen att kopiera bild från %1 till %2 - + Error resizing user image - + Unable to resize image + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Bekräfta Radering + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3497,47 +3638,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Sökväg - + ... ... @@ -3789,56 +3930,71 @@ Dra punkter för att ändra position, eller dubbelklicka tabellceller för att r + Show Compatibility List + + + + Show Add-Ons Column Visa Add-Ons-Kolumn - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: - + Folder Icon Size: - + Row 1 Text: Rad 1 Text: - + Row 2 Text: Rad 2 Text: - + Screenshots Skrämdump - + Ask Where To Save Screenshots (Windows Only) Fråga till var man ska spara skärmdumpar (endast Windows) - + Screenshots Path: Skärmdumpssökväg - + ... ... - + Select Screenshots Path... Välj Skärmdumpssökväg... - + <System> <System> @@ -3947,7 +4103,7 @@ Dra punkter för att ändra position, eller dubbelklicka tabellceller för att r - + Verify Verifiera @@ -4034,7 +4190,7 @@ Dra punkter för att ändra position, eller dubbelklicka tabellceller för att r - + Unspecified Ospecificerat @@ -4049,17 +4205,36 @@ Dra punkter för att ändra position, eller dubbelklicka tabellceller för att r Polletten verifierades inte. Ändringen till din pollett har inte sparats. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... Verifierar... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Verifiering misslyckad + + + Verification failed Verifiering misslyckad - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Verifiering misslyckad. Kontrollera att du har skrivit in din pollett korrekt, och att din internetuppkoppling fungerar. @@ -4128,12 +4303,12 @@ Dra punkter för att ändra position, eller dubbelklicka tabellceller för att r DirectConnectWindow - + Connecting - + Connect @@ -4141,908 +4316,910 @@ Dra punkter för att ändra position, eller dubbelklicka tabellceller för att r GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonym data skickas </a>För att förbättra yuzu. <br/><br/>Vill du dela med dig av din användarstatistik med oss? - + Telemetry Telemetri - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Laddar WebApplet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Mängden shaders som just nu byggs - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Nuvarande emuleringshastighet. Värden över eller under 100% indikerar på att emulationen körs snabbare eller långsammare än en Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Hur många bilder per sekund som spelet just nu visar. Detta varierar från spel till spel och scen till scen. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tid det tar att emulera en Switch bild, utan att räkna med framelimiting eller v-sync. För emulering på full hastighet så ska det vara som mest 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files - + &Continue - + &Pause &Paus - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Varning Föråldrat Spelformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Du använder det dekonstruerade ROM-formatet för det här spelet. Det är ett föråldrat format som har överträffats av andra som NCA, NAX, XCI eller NSP. Dekonstruerade ROM-kataloger saknar ikoner, metadata och uppdatering.<br><br>För en förklaring av de olika format som yuzu stöder, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>kolla in vår wiki</a>. Det här meddelandet visas inte igen. - - + + Error while loading ROM! Fel vid laddning av ROM! - + The ROM format is not supported. ROM-formatet stöds inte. - + An error occurred initializing the video core. Ett fel inträffade vid initiering av videokärnan. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. Ett okänt fel har uppstått. Se loggen för mer information. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Save Data Spardata - + Mod Data Mod-data - + Error Opening %1 Folder Fel Öppnar %1 Mappen - - + + Folder does not exist! Mappen finns inte! - + Error Opening Transferable Shader Cache Fel Under Öppning Av Överförbar Shadercache - + Failed to create the shader cache directory for this title. - - Contents - Innehåll + + Error Removing Contents + - - Update - Uppdatera + + Error Removing Update + - - DLC - DLC + + Error Removing DLC + - + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + Remove Entry Ta bort katalog - - Remove Installed Game %1? - Ta Bort Installerat Spel %1? - - - - - - - - + + + + + + Successfully Removed Framgångsrikt borttagen - + Successfully removed the installed base game. Tog bort det installerade basspelet framgångsrikt. - - - - Error Removing %1 - Fel Under Borttagning Av %1 - - - + The base game is not installed in the NAND and cannot be removed. Basspelet är inte installerat i NAND och kan inte tas bort. - + Successfully removed the installed update. Tog bort den installerade uppdateringen framgångsrikt. - + There is no update installed for this title. Det finns ingen uppdatering installerad för denna titel. - + There are no DLC installed for this title. Det finns inga DLC installerade för denna titel. - + Successfully removed %1 installed DLC. Tog framgångsrikt bort den %1 installerade DLCn. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? Ta Bort Anpassad Spelkonfiguration? - + Remove File Radera fil - - + + Error Removing Transferable Shader Cache Fel När Överförbar Shader Cache Raderades - - + + A shader cache for this title does not exist. En shader cache för denna titel existerar inte. - + Successfully removed the transferable shader cache. Raderade den överförbara shadercachen framgångsrikt. - + Failed to remove the transferable shader cache. Misslyckades att ta bort den överförbara shadercache - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration Fel När Anpassad Konfiguration Raderades - + A custom configuration for this title does not exist. En anpassad konfiguration för denna titel existerar inte. - + Successfully removed the custom game configuration. Tog bort den anpassade spelkonfigurationen framgångsrikt. - + Failed to remove the custom game configuration. Misslyckades att ta bort den anpassade spelkonfigurationen. - - + + RomFS Extraction Failed! RomFS Extraktion Misslyckades! - + There was an error copying the RomFS files or the user cancelled the operation. Det uppstod ett fel vid kopiering av RomFS filer eller användaren avbröt operationen. - + Full Full - + Skeleton Skelett - + Select RomFS Dump Mode Välj RomFS Dump-Läge - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Välj hur du vill att RomFS ska dumpas. <br>Full kommer att kopiera alla filer i den nya katalogen medan <br>skelett bara skapar katalogstrukturen. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Extraherar RomFS... - - + + Cancel Avbryt - + RomFS Extraction Succeeded! RomFS Extraktion Lyckades! - + The operation completed successfully. Operationen var lyckad. - + Error Opening %1 Fel under öppning av %1 - + Select Directory Välj Katalog - + Properties Egenskaper - + The game properties could not be loaded. Spelegenskaperna kunde inte laddas. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch Körbar (%1);;Alla Filer (*.*) - + Load File Ladda Fil - + Open Extracted ROM Directory Öppna Extraherad ROM-Katalog - + Invalid Directory Selected Ogiltig Katalog Vald - + The directory you have selected does not contain a 'main' file. Katalogen du har valt innehåller inte en 'main'-fil. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Installerbar Switch-fil (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Installera filer - + %n file(s) remaining - + Installing file "%1"... Installerar Fil "%1"... - - + + Install Results Installera resultat - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systemapplikation - + System Archive Systemarkiv - + System Application Update Systemapplikationsuppdatering - + Firmware Package (Type A) Firmwarepaket (Typ A) - + Firmware Package (Type B) Firmwarepaket (Typ B) - + Game Spel - + Game Update Speluppdatering - + Game DLC Spel DLC - + Delta Title Delta Titel - + Select NCA Install Type... Välj NCA-Installationsläge... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Välj vilken typ av titel du vill installera som: (I de flesta fallen, standard 'Spel' är bra.) - + Failed to Install Misslyckades med Installationen - + The title type you selected for the NCA is invalid. Den titeltyp du valt för NCA är ogiltig. - + File not found Filen hittades inte - + File "%1" not found Filen "%1" hittades inte - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account yuzu Konto hittades inte - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. För att skicka ett spelkompatibilitetstest, du måste länka ditt yuzu-konto.<br><br/>För att länka ditt yuzu-konto, gå till Emulering &gt, Konfigurering &gt, Web. - + Error opening URL Fel när URL öppnades - + Unable to open the URL "%1". Oförmögen att öppna URL:en "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - - Error - - - - - - The current game is not looking for amiibos - - - - - + + Amiibo - - + + The current amiibo has been removed - + + Error + Fel + + + + + The current game is not looking for amiibos + + + + Amiibo File (%1);; All Files (*.*) Amiibo Fil (%1);; Alla Filer (*.*) - + Load Amiibo Ladda Amiibo - - Error opening Amiibo data file - Fel öppnar Amiibo-datafilen - - - - Unable to open Amiibo file "%1" for reading. - Kunde inte öppna Amiibo filen "%1" för läsning. - - - - Error reading Amiibo data file - Fel vid läsning av Amiibo-datafil - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Kan inte läsa Amiibo-data helt. Förväntas läsa %1 byte, men kunde bara läsa %2 byte. - - - + Error loading Amiibo data Fel vid laddning av Amiibodata - - Unable to load Amiibo data. - Kan inte ladda Amiibodata. - - - - Capture Screenshot - Skärmdump - - - - PNG Image (*.png) - PNG Bild (*.png) - - - - TAS state: Running %1/%2 + + The selected file is not a valid amiibo - - TAS state: Recording %1 + + The selected file is already on use - - TAS state: Idle %1/%2 + + An unknown error occurred + Capture Screenshot + Skärmdump + + + + PNG Image (*.png) + PNG Bild (*.png) + + + + TAS state: Running %1/%2 + + + + + TAS state: Recording %1 + + + + + TAS state: Idle %1/%2 + + + + TAS State: Invalid - + &Stop Running - + &Start &Start - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Hastighet: %1% / %2% - + Speed: %1% Hastighet: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Spel: %1 FPS - + Frame: %1 ms Ruta: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Spelet du försöker ladda kräver att ytterligare filer dumpas från din Switch innan du spelar.<br/><br/>För mer information om dumpning av dessa filer, se följande wiki sida: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumpning System Arkiv och Delade Teckensnitt från en Switchkonsol</a>.<br/><br/>Vill du avsluta till spellistan? Fortsatt emulering kan leda till kraschar, skadad spara data och andra buggar. - + yuzu was unable to locate a Switch system archive. %1 yuzu kunde inte lokalisera ett Switchsystemarkiv. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 yuzu kunde inte lokalisera ett Switchsystemarkiv: %1. %2 - + System Archive Not Found Systemarkivet Hittades Inte - + System Archive Missing Systemarkiv Saknas - + yuzu was unable to locate the Switch shared fonts. %1 yuzu kunde inte lokalisera Switchens delade fonter. %1 - + Shared Fonts Not Found Delade Teckensnitt Hittades Inte - + Shared Font Missing Delad Font Saknas - + Fatal Error Dödligt Fel - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu stötte på ett dödligt fel, se loggen för mer information. För mer information om åtkomst till loggen, se följande sida: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Hur man Laddar upp Loggfilen</a>.<br/><br/>Vill du avsluta till spellistan? Fortsatt emulering kan leda till kraschar, skadad spara data och andra buggar. - + Fatal Error encountered Allvarligt fel påträffat - + Confirm Key Rederivation Bekräfta Nyckel Rederivering - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5059,37 +5236,37 @@ och eventuellt göra säkerhetskopior. Detta raderar dina autogenererade nyckelfiler och kör nyckelderivationsmodulen. - + Missing fuses Saknade säkringar - + - Missing BOOT0 - Saknar BOOT0 - + - Missing BCPKG2-1-Normal-Main - Saknar BCPKG2-1-Normal-Main - + - Missing PRODINFO - Saknar PRODINFO - + Derivation Components Missing Deriveringsdelar saknas - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5098,39 +5275,39 @@ Detta kan ta upp till en minut beroende på systemets prestanda. - + Deriving Keys Härleda Nycklar - + Select RomFS Dump Target Välj RomFS Dumpa Mål - + Please select which RomFS you would like to dump. Välj vilken RomFS du vill dumpa. - + Are you sure you want to close yuzu? Är du säker på att du vill stänga yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Är du säker på att du vill stoppa emuleringen? Du kommer att förlora osparade framsteg. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5142,38 +5319,38 @@ Vill du strunta i detta och avsluta ändå? GRenderWindow - + OpenGL not available! OpenGL inte tillgängligt! - + yuzu has not been compiled with OpenGL support. yuzu har inte komilerats med OpenGL support. - - + + Error while initializing OpenGL! Fel under initialisering av OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. - + Error while initializing OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 @@ -5181,153 +5358,153 @@ Vill du strunta i detta och avsluta ändå? GameList - + Favorite - + Start Game - + Start Game without Custom Configuration - + Open Save Data Location Öppna Spara Data Destination - + Open Mod Data Location Öppna Mod Data Destination - + Open Transferable Pipeline Cache - + Remove Ta Bort - + Remove Installed Update Ta Bort Installerad Uppdatering - + Remove All Installed DLC Ta Bort Alla Installerade DLC - + Remove Custom Configuration Ta Bort Anpassad Konfiguration - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents Ta Bort Allt Installerat Innehåll - + Dump RomFS Dumpa RomFS - + Dump RomFS to SDMC - + Copy Title ID to Clipboard Kopiera Titel ID till Urklipp - + Navigate to GameDB entry Navigera till GameDB-sida - + Properties Egenskaper - + Scan Subfolders Skanna Underkataloger - + Remove Game Directory Radera Spelkatalog - + ▲ Move Up ▲ Flytta upp - + ▼ Move Down ▼ Flytta ner - + Open Directory Location Öppna Sökvägsplats - + Clear Rensa - + Name Namn - + Compatibility Kompatibilitet - + Add-ons Add-Ons - + File type Filtyp - + Size Storlek @@ -5336,81 +5513,61 @@ Vill du strunta i detta och avsluta ändå? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Perfekt - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - Spelet körs perfekt utan ljud och grafikproblem, alla testade funktioner fungerar som avsett utan -några ändringar som behövs. - - - - Great - Utmärkt - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - Spelet körs från början till slut med ett fåtal ljud eller grafikproblem. Kan kräva några -ändringar. - - Okay - Okej - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - Spelet körs med stora ljud eller grafikproblem men går att spela från början till slut med -några ändringar. + Game can be played without issues. + - Bad - Dålig + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Spelet körs men med stora ljud eller grafikproblem. Går inte att spela klart -även med ändringar. + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu Intro/Meny - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Spelet går inte att köra på grund utav stora ljud eller grafikproblem. Kommer inte förbi -startskärmen. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Startar Inte - + The game crashes when attempting to startup. Spelet kraschar när man försöker starta det. - + Not Tested Inte Testad - + The game has not yet been tested. Spelet har ännu inte testats. @@ -5418,7 +5575,7 @@ startskärmen. GameListPlaceholder - + Double-click to add a new folder to the game list Dubbelklicka för att lägga till en ny mapp i spellistan. @@ -5431,12 +5588,12 @@ startskärmen. - + Filter: Filter: - + Enter pattern to filter Ange mönster för att filtrera @@ -5486,7 +5643,7 @@ startskärmen. Room Description - + Rumsbeskrivning @@ -5512,12 +5669,12 @@ startskärmen. HostRoomWindow - + Error - + Fel - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5526,11 +5683,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5552,112 +5710,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Skärmdump - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Fullskärm - + Load File Ladda Fil - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5771,42 +5928,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Spelare - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5829,232 +5986,237 @@ Debug Message: - + &Emulation &Emulering - + &View &Vyn - + &Reset Window Size - + &Debugging - + Reset Window Size to &720p - + Reset Window Size to 720p - + Reset Window Size to &900p - + Reset Window Size to 900p - + Reset Window Size to &1080p - + Reset Window Size to 1080p - + + &Multiplayer + + + + &Tools - + &TAS - + &Help &Hjälp - + &Install Files to NAND... - + L&oad File... - + Load &Folder... - + E&xit A&vsluta - + &Pause &Paus - + &Stop &Sluta - + &Reinitialize keys... - + &About yuzu - + Single &Window Mode - + Con&figure... - + Display D&ock Widget Headers - + Show &Filter Bar - + Show &Status Bar - + Show Status Bar Visa Statusfält - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room + &Browse Public Game Lobby - - Direct Connect to Room + + &Create Room - - Show Current Room + + &Leave Room - F&ullscreen + &Direct Connect to Room - &Restart + &Show Current Room - Load/Remove &Amiibo... + F&ullscreen - &Report Compatibility + &Restart + Load/Remove &Amiibo... + + + + + &Report Compatibility + + + + Open &Mods Page - + Open &Quickstart Guide - + &FAQ - + Open &yuzu Folder - + &Capture Screenshot - + &Configure TAS... - + Configure C&urrent Game... - + &Start &Start - + &Reset - + R&ecord @@ -6113,52 +6275,47 @@ Debug Message: Refresh - + Ladda om MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Kopplad - - - - Not Connected + Nedkopplad + + + + Connected + Uppkopplad + + + + New Messages Received - + Error - + Fel - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6259,32 +6416,49 @@ They may have left the room. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + Lämna Rum - + You are about to close the room. Any network connections will be closed. - + Du är på väg att stänga detta rum. Alla nätverksuppkopplingar kommer att stängas. - + Disconnect - + Koppla ned - + You are about to leave the room. Any network connections will be closed. - + Du är på väg att lämna detta rum. Alla nätverksuppkopplingar kommer att stängas. NetworkMessage::ErrorManager - + Error - + Fel @@ -6321,50 +6495,50 @@ p, li { white-space: pre-wrap; } START/PAUSE - + START/PAUSE QObject - + %1 is not playing a game - + %1 spelar inte något spel - + %1 is playing %2 - + %1 spelar %2 - + Not playing a game - + Spelar inte något spel - + Installed SD Titles Installerade SD-titlar - + Installed NAND Titles Installerade NAND-titlar - + System Titles Systemtitlar - + Add New Game Directory Lägg till ny spelkatalog - + Favorites - + Favoriter @@ -6392,7 +6566,7 @@ p, li { white-space: pre-wrap; } - + [not set] [inte inställd] @@ -6407,10 +6581,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Axel %1%2 @@ -6424,9 +6598,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [okänd] @@ -6462,7 +6636,7 @@ p, li { white-space: pre-wrap; } Z - + Z @@ -6510,132 +6684,132 @@ p, li { white-space: pre-wrap; } L1 - + L1 L2 - + L2 L3 - + L3 R1 - + R1 R2 - + R2 R3 - + R3 Circle - + Cirkel Cross - + Kors Square - + Fyrkant Triangle - + Triangel Share - + Dela Options - + Val [undefined] - + [odefinerad] %1%2 - + %1%2 - + [invalid] - + [felaktig] - - + + %1%2Hat %3 - + %1%2Hatt %3 - - - + + + %1%2Axis %3 - + %1%2Axel %3 - + %1%2Axis %3,%4,%5 - + %1%2Axel %3,%4%5 - + %1%2Motion %3 - + %1%2Rörelse %3 - - + + %1%2Button %3 - + %1%2Knapp %3 - + [unused] [oanvänd] @@ -6653,31 +6827,144 @@ p, li { white-space: pre-wrap; } Wheel Indicates the mouse wheel - + Hjul Backward - + Bakåt Forward - + Framåt Task - + Åtgärd Extra + Extra + + + + %1%2%3 + %1%2%3 + + + + QtAmiiboSettingsDialog + + + Amiibo Settings - - %1%2%3 + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Namn + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? @@ -6686,27 +6973,27 @@ p, li { white-space: pre-wrap; } Controller Applet - + Kontroll-Applet Supported Controller Types: - + Supporterade Kontrolltyper: Players: - + Spelare: 1 - 8 - + 1 - 8 P4 - + P4 @@ -6770,20 +7057,21 @@ p, li { white-space: pre-wrap; } Use Current Config - + Använd Nuvarande Konfiguration P2 - + P2 P1 - + P1 + Handheld Handhållen @@ -6791,27 +7079,27 @@ p, li { white-space: pre-wrap; } P3 - + P3 P7 - + P7 P8 - + P8 P5 - + P5 P6 - + P6 @@ -6823,11 +7111,6 @@ p, li { white-space: pre-wrap; } Docked Dockad - - - Undocked - Ej Dockad - Vibration @@ -6852,7 +7135,7 @@ p, li { white-space: pre-wrap; } Create - + Skapa @@ -6907,32 +7190,32 @@ p, li { white-space: pre-wrap; } GameCube Controller - + GameCube-kontroll Poke Ball Plus - + Poke Ball Plus NES Controller - + NES-kontroll SNES Controller - + SNES-kontroll N64 Controller - + N64-kontroll Sega Genesis - + Sega Genesis @@ -6942,19 +7225,21 @@ p, li { white-space: pre-wrap; } Error Code: %1-%2 (0x%3) - + Felkod: %1-%2 (0x%3) An error has occurred. Please try again or contact the developer of the software. - + Ett fel har inträffat. +Vänligen försök igen eller kontakta utvecklaren av programvaran. An error occurred on %1 at %2. Please try again or contact the developer of the software. - + Ett fel har inträffat på %1 vid %2. +Vänligen försök igen eller kontakta utvecklaren av programvaran. @@ -6963,7 +7248,11 @@ Please try again or contact the developer of the software. %1 %2 - + Ett fel har inträffat. + +%1 + +%2 @@ -7002,7 +7291,7 @@ Please try again or contact the developer of the software. Enter Text - + Mata in Text @@ -7130,22 +7419,22 @@ p, li { white-space: pre-wrap; } waiting - + väntar initialized - + initialiserad terminated - + avslutad unknown - + okänd diff --git a/dist/languages/tr_TR.ts b/dist/languages/tr_TR.ts index 1e5c846..299e90a 100644 --- a/dist/languages/tr_TR.ts +++ b/dist/languages/tr_TR.ts @@ -25,12 +25,18 @@ p, li { white-space: pre-wrap; } <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">This software should not be used to play games you have not legally obtained.</span></p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Yuzu GPLv3.0+ ile lisanslanmış Nintendo Switch için açık kaynak bir deneysel emülatördür.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Bu yazılım yasal yollarla edinilmemiş oyunları çalıştırmak için kullanılmamalı.</span></p></body></html> <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a>|<a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Kaynak Kodu</span></a>|<a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Katkıda Bulunanlar</span></a>|<a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Lisans</span></a></p></body></html> @@ -76,95 +82,97 @@ p, li { white-space: pre-wrap; } Room Window - + Oda Penceresi Send Chat Message - + Sohbet Mesajı At Send Message - + Mesaj Gönder - + Members - + Üyeler - + %1 has joined - + %1 katıldı - + %1 has left - + %1 ayrıldı - + %1 has been kicked - + %1 atıldı - + %1 has been banned - + %1 yasaklandı - + %1 has been unbanned - + %1'in yasağı kaldırıldı - + View Profile - - - - - - Block Player - - - - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - - - - - Kick - - - - - Ban - + Profili Görüntüle + - Kick Player - + Block Player + Kullanıcıyı Engelle + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + Bir kullanıcıyı engellediğinizde, ondan mesaj alamayacaksınız.<br><br> %1'i engellemek istediğinizden emin misiniz? + + + + Kick + At + + + + Ban + Yasakla + + + + Kick Player + Kullanıcıyı At + + + Are you sure you would like to <b>kick</b> %1? - + %1'i <b>atmak</b> istediğinizden emin misiniz? - + Ban Player - + Kullanıcıyı Yasakla - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - + %1'i <b>kicklemek ve banlamaktan</b> emin misiniz? + +Bu işlem onların hem forum kullanıcı adını hem de IP adresini banlar. @@ -172,22 +180,22 @@ This would ban both their forum username and their IP address. Room Window - + Oda Penceresi Room Description - + Oda Açıklaması Moderation... - + Moderasyon... Leave Room - + Odadan Ayrıl @@ -200,12 +208,12 @@ This would ban both their forum username and their IP address. Disconnected - + Bağlantı kesildi - %1 (%2/%3 members) - connected - + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 oyuncu) - bağlanıldı @@ -218,6 +226,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Oyun Uyumluluğu Bildir @@ -227,92 +240,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p>Eğer<span style=" font-size:10pt;">Citra Uyumluluk Listesi'ne </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;"></span></a><span style=" font-size:10pt;">test çalışması göndermek isterseniz, belirtilen bilgiler toplanacak ve sitede gösterilecektir:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Donanım Bilgisi(CPU/GPU/İşletim Sistemi)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hangi Citra versiyonunun kullanıldığı</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Bağlı Citra hesabı</li></ul></body></html> - - Perfect - Mükemmel + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Oyun grafik veya ses hataları olmadan sorunsuz çalışıyor.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Çok iyi + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Oyun küçük grafik veya ses hatalarıyla çalışıyor ve baştan sona kadar oynanabilir. Bazı geçici çözümler gerektirebilir..</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Yeterli + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Oyun büyük grafik veya ses hatalarıyla çalışıyor fakat geçici çözümler ile baştan sona kadar oynanabilir..</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Kötü + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Oyun çalışıyor fakat büyük grafik veya ses hatalarına sahip. Geçici çözümlerle bile belirli alanlar geçilemiyor.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - İntro/Menü + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Oyun grafik ve ses hatalarından dolayı oynanılamıyor. Başlangıç ekranını geçemiyorsunuz..</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Açılmıyor + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Oyun açılmaya çalışıldığında çöküyor.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Hız ve performanstan bağımsız olarak, bu oyun baştan sona Citra'nın bu versiyonunda ne kadar iyi oynanıyor?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Bildirdiğiniz için teşekkür ederiz! - + Submitting Bildiriliyor - + Communication error Bağlantı hatası - + An error occurred while sending the Testcase Testcase gönderilirken bir hata oldu - + Next İleri @@ -333,7 +381,7 @@ This would ban both their forum username and their IP address. Output Device - + Çıkış Cihazı @@ -372,37 +420,37 @@ This would ban both their forum username and their IP address. Configure Infrared Camera - + Kızılötesi Kamera'yı Ayarla Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. - + Emüle edilmiş kameranın görüntüyü aldığı yeri seçin. Sanal kamera ya da gerçek bir kamera olabilir. Camera Image Source: - + Kamera Görüntü Kaynağı: Input device: - + Giriş cihazı: Preview - + Önizle Resolution: 320*240 - + Çözünürlük: 320*240 Click to preview - + Önizlemek için tıkla @@ -410,7 +458,7 @@ This would ban both their forum username and their IP address. Varsayılana Döndür - + Auto Otomatik @@ -455,7 +503,7 @@ This would ban both their forum username and their IP address. Paranoid (disables most optimizations) - + Paranoya (çoğu optimizasyonu kapatır) @@ -570,7 +618,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Toggle CPU Optimizations - CPU Optimizasyonlarını Ayarla + CPU Optimizasyonlarını Aç/Kapa @@ -747,200 +795,235 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra ConfigureDebug - + Debugger - + Hata Ayıklayıcı - + Enable GDB Stub GDB Stub'ı Etkinleştir - + Port: Port: - + Logging Kütük Tutma - + Global Log Filter Evrensel Kütük Filtresi - + Show Log in Console Konsolda Log'u Göster - + Open Log Location Kütük Konumunu Aç - + When checked, the max size of the log increases from 100 MB to 1 GB Etkinleştirildiğinde log'un boyut sınırı 100 MB'tan 1 GB'a çıkar - + Enable Extended Logging** Uzatılmış Hata Kaydını Etkinleştir. - + Homebrew Homebrew - + Arguments String Arguments String - + Graphics Grafikler - + When checked, the graphics API enters a slower debugging mode Etkinleştirildiğinde, grafik API'ı daha yavaş bir hata ayıklama moduna girer. - + Enable Graphics Debugging Grafik Hata Ayıklama Modunu Etkinleştir - + When checked, it enables Nsight Aftermath crash dumps İşaretlendiğinde Nsight Aftermath çökme dökümlerini etkinleştirir. - + Enable Nsight Aftermath Nsight Aftermath'ı Etkinleştir - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower İşaretlendiğinde Makro JIT derleyicisini devre dışı bırakır. Bu seçeneği etkinleştirmek oyunların yavaş çalışmasına neden olur. - + Disable Macro JIT Macro JIT'i devre dışı bırak - + When checked, yuzu will log statistics about the compiled pipeline cache Etkinleştirildiğinde, yuzu derlenen pipeline cache istatistiklerini log'a kaydeder. - + Enable Shader Feedback Shader Geribildirimini Etkinleştir - + When checked, it executes shaders without loop logic changes İşaretlendiğinde shaderları döngü mantık değişimleri olmaksızın uygular - + Disable Loop safety checks Döngü güvenliği kontrolünü devre dışı bırak - + Debugging Hata ayıklama - - Enable FS Access Log - FS Erişim Kaydını Etkinleştir - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** Detaylı Raporlama Hizmetini Etkinleştir - + + Enable FS Access Log + FS Erişim Kaydını Etkinleştir + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + Konsola Ses Komutlarını Aktar** + + + + Create Minidump After Crash + + + + Advanced Gelişmiş - + Kiosk (Quest) Mode Kiosk (Quest) Modu - + Enable CPU Debugging CPU Hata Ayıklama Modu'nu Etkinleştir - + Enable Debug Asserts Hata Ayıklama Assert'lerini Etkinleştir - + Enable Auto-Stub** Auto-Stub'ı Etkinleştir - + Enable All Controller Types Bütün Kontrolcü Türlerini Etkinleştir - + Disable Web Applet Web Uygulamasını Devre Dışı Bırak - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Bu yuzu kapandığında otomatik olarak eski haline dönecektir. + + + Restart Required + Yeniden Başlatma Gerekli + + + + yuzu is required to restart in order to apply this setting. + yuzu'nun bu ayarı uygulayabilmesi için yeniden başlatılması gereklidir. + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1249,7 +1332,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Pause emulation when in background - Arka plana alındığında emülasyonu durdur + Arka plana alındığında emülasyonu duraklat @@ -1287,7 +1370,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Graphics - Grrafikler + Grafikler @@ -1310,193 +1393,218 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra API: - + Graphics Settings Grafik Ayarları - + Use disk pipeline cache Disk pipeline cache'ini kullan - + Use asynchronous GPU emulation Asenkronize GPU emülasyonu kullan - + Accelerate ASTC texture decoding ASTC kaplama çözümünü hızlandır - + NVDEC emulation: NVDEC emülasyonu: - + No Video Output Video Çıkışı Yok - + CPU Video Decoding CPU Video Decoding - + GPU Video Decoding (Default) GPU Video Decoding (Varsayılan) - + Fullscreen Mode: Tam Ekran Modu: - + Borderless Windowed Kenarlıksız Tam Ekran - + Exclusive Fullscreen Ayrılmış Tam Ekran - + Aspect Ratio: En-Boy Oranı: - + Default (16:9) Varsayılan (16:9) - + Force 4:3 4:3'e Zorla - + Force 21:9 21:9'a Zorla - + + Force 16:10 + 16:10'a Zorla + + + Stretch to Window Ekrana Sığdır - + Resolution: Çözünürlük: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [DENEYSEL] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [DENEYSEL] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: Pencereye Uyarlı Filtre: - + Nearest Neighbor En Yakın Komşu Algoritması - + Bilinear Bilinear - + Bicubic Bicubic - + Gaussian Gausyen - + ScaleForce ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ Super Resolution (Vulkan'a Özel) - + Anti-Aliasing Method: Kenar Yumuşatma Yöntemi: - + None Yok - + FXAA FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Global arka plan rengini kullan - + Set background color: Arka plan rengini ayarla: - + Background Color: Arkaplan Rengi: @@ -1505,6 +1613,12 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra GLASM (Assembly Shaders, NVIDIA Only) GLASM (Assembly Shaderları, Yalnızca NVIDIA için) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1536,7 +1650,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Use VSync - + VSync Kullan @@ -1559,37 +1673,47 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Hızlı GPU Saati Kullan (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Anisotropic Filtering: - + Automatic Otomatik - + Default Varsayılan - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -1634,7 +1758,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Controller Hotkey - + Kontrolcü Kısayolu @@ -1652,7 +1776,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Home+%1 - + Ev+%1 @@ -1682,7 +1806,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra The default button sequence is already assigned to: %1 - + Varsayılan buton dizisi zaten %1'e atanmış. @@ -1984,12 +2108,12 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Ring Controller - + Ring Kontrolcüsü Infrared Camera - + Kızılötesi Kamera @@ -2019,7 +2143,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra Controller navigation - + Kontrolcü navigasyonu @@ -2081,7 +2205,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra - + Left Stick Sol Analog @@ -2175,14 +2299,14 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra - + L L - + ZL ZL @@ -2201,7 +2325,7 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra - + Plus Artı @@ -2214,15 +2338,15 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra - + R R - + ZR ZR @@ -2279,231 +2403,236 @@ Bu seçenek belleğe yazma/okuma işlemlerindeki güvenlik kontrolünü kaldıra - + Right Stick Sağ Analog - - - - + + + + Clear Temizle - - - - - + + + + + [not set] [belirlenmedi] - - - Toggle button - Tuş ayarla - - - - + + Invert button Tuşları ters çevir - - + + + Toggle button + Tuşu Aç/Kapa + + + + Invert axis Ekseni ters çevir - - - + + + Set threshold Alt sınır ayarla - - + + Choose a value between 0% and 100% %0 ve %100 arasında bir değer seçin - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Analog Çubuğu Ayarla - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Tamama bastıktan sonra, joystikinizi önce yatay sonra dikey olarak hareket ettirin. Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak hareket ettirin. - + Center axis - - + + Deadzone: %1% Ölü Bölge: %1% - - + + Modifier Range: %1% Düzenleyici Aralığı: %1% - - + + Pro Controller Pro Controller - + Dual Joycons İkili Joyconlar - + Left Joycon Sol Joycon - + Right Joycon Sağ Joycon - + Handheld Handheld - + GameCube Controller GameCube Kontrolcüsü - + Poke Ball Plus Poke Ball Plus - + NES Controller NES Kontrolcüsü - + SNES Controller SNES Kontrolcüsü - + N64 Controller N64 Kontrolcüsü - + Sega Genesis Sega Genesis - + Start / Pause - Başlat / Durdur + Başlat / Duraklat - + Z Z - + Control Stick Kontrol Çubuğu - + C-Stick C-Çubuğu - + Shake! Salla! - + [waiting] [bekleniyor] - + New Profile Yeni Profil - + Enter a profile name: Bir profil ismi girin: - - + + Create Input Profile Kontrol Profili Oluştur - + The given profile name is not valid! Girilen profil ismi geçerli değil! - + Failed to create the input profile "%1" "%1" kontrol profili oluşturulamadı - + Delete Input Profile Kontrol Profilini Kaldır - + Failed to delete the input profile "%1" "%1" kontrol profili kaldırılamadı - + Load Input Profile Kontrol Profilini Yükle - + Failed to load the input profile "%1" "%1" kontrol profili yüklenemedi - + Save Input Profile Kontrol Profilini Kaydet - + Failed to save the input profile "%1" "%1" kontrol profili kaydedilemedi @@ -2758,42 +2887,42 @@ Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak har Geliştirici - + Add-Ons Eklentiler - + General Genel - + System Sistem - + CPU CPU - + Graphics Grafikler - + Adv. Graphics Gelişmiş Grafikler - + Audio Ses - + Properties Özellikler @@ -2849,37 +2978,37 @@ Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak har Geçerli Kullanıcı - + Username Kullanıcı Adı - + Set Image Resim Belirle - + Add Ekle - + Rename Yeniden Adlandır - + Remove Kaldır - + Profile management is available only when game is not running. Profil ayarlarına sadece oyun çalışmıyorken erişilebilir. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2887,124 +3016,133 @@ Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak har %2 - + Enter Username Kullanıcı Adınızı girin - + Users Kullanıcılar - + Enter a username for the new user: Yeni kullanıcı için yeni bir kullanıcı adı giriniz: - + Enter a new username: Yeni bir kullanıcı adı giriniz: - - Confirm Delete - Silmeyi Onayla - - - - You are about to delete user with name "%1". Are you sure? - "%1" adlı kullanıcıyı silmek istediğinize emin misiniz? - - - + Select User Image Kullanıcı Resmi Seçin - + JPEG Images (*.jpg *.jpeg) JPEG Görüntüler (*.jpg *.jpeg) - + Error deleting image Resim silinirken hata oluştu - + Error occurred attempting to overwrite previous image at: %1. Eski resmin üzerine yazılmaya çalışırken hata oluştu: %1. - + Error deleting file Dosyayı silerken hata oluştu - + Unable to delete existing file: %1. Mevcut %1 dosyası silinemedi - + Error creating user image directory Kullanıcı görüntü klasörünü oluştururken hata - + Unable to create directory %1 for storing user images. Kullanıcı görüntülerini depolamak için %1 klasörü oluşturulamadı. - + Error copying user image Kullanıcı görüntüsünü kopyalarken hata - + Unable to copy image from %1 to %2 Görüntü %1'den %2'ye kopyalanamadı - + Error resizing user image Kullanıcı görüntüsünü yeniden boyutlandırma hatası - + Unable to resize image Görüntü yeniden boyutlandırılamıyor + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Silmeyi Onayla + + + + Name: %1 +UUID: %2 + + + ConfigureRingController Configure Ring Controller - + Ring Kontrolcüsünü Ayarla If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly. - + Eğer bu kontrolcüyü kullanmak istiyorsanız oyunun doğru düzgün kontrolcüyü algılaması için oyunu açmadan önce oyuncu 1'i sağ kontrolcü ve oyuncu 2'yi çift joycon olarak ayarlayın. Ring Sensor Parameters - + Ring Sensör Parametreleri Pull - + Çek Push - + İt @@ -3505,47 +3643,47 @@ Eksenleri ters çevirmek için, önce joystickinizi dikey sonra yatay olarak har <html><head/><body><p> Kontrolcü girdilerini TAS-nx scriptleri ile aynı formatta okur. <br/>Daha detaylı bilgi için lütfen yuzu web sitesindeki <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;"> yardım sayfasına</span></a>bakınız.</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). Hangi kısayolların Yeniden Oynatma/Kayıt fonksiyonunu kontrol ettiğini öğrenmek için Kısayol Ayarlarına bakın. (Yapılandır -> Genel -> Kısayollar) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. UYARI: Bu deneysel bir özelliktir.<br/> Halihazırdaki kusurlu eşzamanlama yöntemi sebebiyle, scriptleri mükemmel olarak oynatmayacaktır. - + Settings Ayarlar - + Enable TAS features TAS özelliklerini Etkinleştir - + Loop script Döngü komut dosyası - + Pause execution during loads Yüklemeler sırasında yürütmeyi duraklat - + Script Directory Script Konumu - + Path Konum - + ... ... @@ -3797,56 +3935,71 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne + Show Compatibility List + + + + Show Add-Ons Column Eklentiler kolonunu göster - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: Oyun Simge Boyutu: - + Folder Icon Size: Dosya Simge Boyutu: - + Row 1 Text: 1. Sıra Yazısı: - + Row 2 Text: 2. Sıra Yazısı: - + Screenshots Ekran Görüntüleri - + Ask Where To Save Screenshots (Windows Only) Ekran Görüntülerinin Nereye Kaydedileceğini Belirle (Windows'a Özel) - + Screenshots Path: Ekran Görüntülerinin Konumu: - + ... ... - + Select Screenshots Path... Ekran Görüntülerinin Konumunu Seçin... - + <System> <System> @@ -3955,7 +4108,7 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne - + Verify Doğrula @@ -4042,7 +4195,7 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne - + Unspecified Belirlenmemiş @@ -4057,17 +4210,36 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne Token doğrulanmadı. Tokeninize yapılan değişiklik kaydedilmedi. - + + Unverified, please click Verify before saving configuration + Tooltip + Doğrulanmadı, lütfen konfigürasyonu kaydetmeden önce Doğrula tuşuna basın + + + + Verifying... Doğrulanıyor... - + + Verified + Tooltip + Doğrulandı + + + + Verification failed + Tooltip + Doğrulanma başarısız oldu + + + Verification failed Doğrulanma başarısız oldu - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. Doğrulanma başarısız oldu. Kullanıcı adı ve tokeninizi doğru girdiğinizden ve internete bağlı olduğunuzdan. @@ -4090,546 +4262,549 @@ Noktanın konumunu değiştirmek için sürükleyin ya da sayıların üstüne Direct Connect - + Direkt Bağlan IP Address - + IP Adresi IP - + IP <html><head/><body><p>IPv4 address of the host</p></body></html> - + <html><head/><body><p>Ana bilgisayarın IPv4 adresi</p></body></html> Port - + Port <html><head/><body><p>Port number the host is listening on</p></body></html> - + <html><head/><body><p>Ana bilgisayarın dinlediği port numarası</p></body></html> Nickname - + Lakap Password - + Şifre Connect - + Bağlan DirectConnectWindow - + Connecting - + Bağlanılıyor - + Connect - + Bağlan GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Yuzuyu geliştirmeye yardımcı olmak için </a> anonim veri toplandı. <br/><br/>Kullanım verinizi bizimle paylaşmak ister misiniz? - + Telemetry Telemetri - + Broken Vulkan Installation Detected - + Bozuk Vulkan Kurulumu Algılandı - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... Web Uygulaması Yükleniyor... - - + + Disable Web Applet Web Uygulamasını Devre Dışı Bırak - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built Şu anda derlenen shader miktarı - + The current selected resolution scaling multiplier. Geçerli seçili çözünürlük ölçekleme çarpanı. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Geçerli emülasyon hızı. %100'den yüksek veya düşük değerler emülasyonun bir Switch'den daha hızlı veya daha yavaş çalıştığını gösterir. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Oyunun şuanda saniye başına kaç kare gösterdiği. Bu oyundan oyuna ve sahneden sahneye değişiklik gösterir. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Bir Switch karesini emüle etmekte geçen zaman, karelimitleme ve v-sync hariç. Tam hız emülasyon için bu en çok 16,67 ms olmalı. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files &Son Dosyaları Temizle - + &Continue &Devam Et - + &Pause - &Durdur + &Duraklat - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu şu anda bir oyun çalıştırıyor - + Warning Outdated Game Format Uyarı, Eski Oyun Formatı - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Bu oyun için dekonstrükte ROM formatı kullanıyorsunuz, bu fromatın yerine NCA, NAX, XCI ve NSP formatları kullanılmaktadır. Dekonstrükte ROM formatları ikon, üst veri ve güncelleme desteği içermemektedir.<br><br>Yuzu'nun desteklediği çeşitli Switch formatları için<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>Wiki'yi ziyaret edin</a>. Bu mesaj yeniden gösterilmeyecektir. - - + + Error while loading ROM! ROM yüklenirken hata oluştu! - + The ROM format is not supported. Bu ROM biçimi desteklenmiyor. - + An error occurred initializing the video core. Video çekirdeğini başlatılırken bir hata oluştu. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu video çekirdeğini çalıştırırken bir hatayla karşılaştı. Bu sorun genellikle eski GPU sürücüleri sebebiyle ortaya çıkar. Daha fazla detay için lütfen log dosyasına bakın. Log dosyasını incelemeye dair daha fazla bilgi için lütfen bu sayfaya ulaşın: <a href='https://yuzu-emu.org/help/reference/log-files/'>Log dosyası nasıl yüklenir</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. ROM yüklenirken hata oluştu! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>Lütfen dosyalarınızı yeniden dump etmek için<a href='https://yuzu-emu.org/help/quickstart/'>yuzu hızlı başlangıç kılavuzu'nu</a> takip edin.<br> Yardım için yuzu wiki</a>veya yuzu Discord'una</a> bakabilirsiniz. - + An unknown error occurred. Please see the log for more details. Bilinmeyen bir hata oluştu. Lütfen daha fazla detay için kütüğe göz atınız. - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data Kayıt Verisi - + Mod Data Mod Verisi - + Error Opening %1 Folder %1 klasörü açılırken hata - - + + Folder does not exist! Klasör mevcut değil! - + Error Opening Transferable Shader Cache Transfer Edilebilir Shader Cache'ini Açarken Bir Hata Oluştu - + Failed to create the shader cache directory for this title. Bu oyun için shader cache konumu oluşturulamadı. - - Contents - İçerikler + + Error Removing Contents + - - Update - Güncelleme + + Error Removing Update + - - DLC - DLC + + Error Removing DLC + - + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + Remove Entry Girdiyi Kaldır - - Remove Installed Game %1? - %1 Adlı Oyunu Kaldırmak İstediğinize Emin Misiniz? - - - - - - - - + + + + + + Successfully Removed Başarıyla Kaldırıldı - + Successfully removed the installed base game. Yüklenmiş oyun başarıyla kaldırıldı. - - - - Error Removing %1 - %1 Adlı Oyun Kaldırılırken Bir Hata Oluştu - - - + The base game is not installed in the NAND and cannot be removed. Asıl oyun NAND'de kurulu değil ve kaldırılamaz. - + Successfully removed the installed update. Yüklenmiş güncelleme başarıyla kaldırıldı. - + There is no update installed for this title. Bu oyun için yüklenmiş bir güncelleme yok. - + There are no DLC installed for this title. Bu oyun için yüklenmiş bir DLC yok. - + Successfully removed %1 installed DLC. %1 yüklenmiş DLC başarıyla kaldırıldı. - + Delete OpenGL Transferable Shader Cache? OpenGL Transfer Edilebilir Shader Cache'ini Kaldırmak İstediğinize Emin Misiniz? - + Delete Vulkan Transferable Shader Cache? Vulkan Transfer Edilebilir Shader Cache'ini Kaldırmak İstediğinize Emin Misiniz? - + Delete All Transferable Shader Caches? Tüm Transfer Edilebilir Shader Cache'leri Kaldırmak İstediğinize Emin Misiniz? - + Remove Custom Game Configuration? Oyuna Özel Yapılandırmayı Kaldırmak İstediğinize Emin Misiniz? - + Remove File Dosyayı Sil - - + + Error Removing Transferable Shader Cache Transfer Edilebilir Shader Cache Kaldırılırken Bir Hata Oluştu - - + + A shader cache for this title does not exist. Bu oyun için oluşturulmuş bir shader cache yok. - + Successfully removed the transferable shader cache. Transfer edilebilir shader cache başarıyla kaldırıldı. - + Failed to remove the transferable shader cache. Transfer edilebilir shader cache kaldırılamadı. - - + + Error Removing Transferable Shader Caches Transfer Edilebilir Shader Cache'ler Kaldırılırken Bir Hata Oluştu - + Successfully removed the transferable shader caches. Transfer edilebilir shader cacheler başarıyla kaldırıldı. - + Failed to remove the transferable shader cache directory. Transfer edilebilir shader cache konumu kaldırılamadı. - - + + Error Removing Custom Configuration Oyuna Özel Yapılandırma Kaldırılırken Bir Hata Oluştu. - + A custom configuration for this title does not exist. Bu oyun için bir özel yapılandırma yok. - + Successfully removed the custom game configuration. Oyuna özel yapılandırma başarıyla kaldırıldı. - + Failed to remove the custom game configuration. Oyuna özel yapılandırma kaldırılamadı. - - + + RomFS Extraction Failed! RomFS Çıkartımı Başarısız! - + There was an error copying the RomFS files or the user cancelled the operation. RomFS dosyaları kopyalanırken bir hata oluştu veya kullanıcı işlemi iptal etti. - + Full Full - + Skeleton Çerçeve - + Select RomFS Dump Mode RomFS Dump Modunu Seçiniz - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Lütfen RomFS'in nasıl dump edilmesini istediğinizi seçin.<br>"Full" tüm dosyaları yeni bir klasöre kopyalarken <br>"skeleton" sadece klasör yapısını oluşturur. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1 konumunda RomFS çıkarmaya yetecek alan yok. Lütfen yer açın ya da Emülasyon > Yapılandırma > Sistem > Dosya Sistemi > Dump konumu kısmından farklı bir çıktı konumu belirleyin. - + Extracting RomFS... RomFS çıkartılıyor... - - + + Cancel İptal - + RomFS Extraction Succeeded! RomFS Çıkartımı Başarılı! - + The operation completed successfully. İşlem başarıyla tamamlandı. - + Error Opening %1 %1 Açılırken Bir Hata Oluştu - + Select Directory Klasör Seç - + Properties Özellikler - + The game properties could not be loaded. Oyun özellikleri yüklenemedi. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch Çalıştırılabilir Dosyası (%1);;Tüm Dosyalar (*.*) - + Load File Dosya Aç - + Open Extracted ROM Directory Çıkartılmış ROM klasörünü aç - + Invalid Directory Selected Geçersiz Klasör Seçildi - + The directory you have selected does not contain a 'main' file. Seçtiğiniz klasör bir "main" dosyası içermiyor. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Yüklenilebilir Switch Dosyası (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Dosya Kur - + %n file(s) remaining %n dosya kaldı%n dosya kaldı - + Installing file "%1"... "%1" dosyası kuruluyor... - - + + Install Results Kurulum Sonuçları - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Olası çakışmaları önlemek için oyunları NAND'e yüklememenizi tavsiye ediyoruz. Lütfen bu özelliği sadece güncelleme ve DLC yüklemek için kullanın. - + %n file(s) were newly installed %n dosya güncel olarak yüklendi @@ -4637,7 +4812,7 @@ Lütfen bu özelliği sadece güncelleme ve DLC yüklemek için kullanın. - + %n file(s) were overwritten %n dosyanın üstüne yazıldı @@ -4645,7 +4820,7 @@ Lütfen bu özelliği sadece güncelleme ve DLC yüklemek için kullanın. - + %n file(s) failed to install %n dosya yüklenemedi @@ -4653,411 +4828,410 @@ Lütfen bu özelliği sadece güncelleme ve DLC yüklemek için kullanın. - + System Application Sistem Uygulaması - + System Archive Sistem Arşivi - + System Application Update Sistem Uygulama Güncellemesi - + Firmware Package (Type A) Yazılım Paketi (Tür A) - + Firmware Package (Type B) Yazılım Paketi (Tür B) - + Game Oyun - + Game Update Oyun Güncellemesi - + Game DLC Oyun DLC'si - + Delta Title Delta Başlık - + Select NCA Install Type... NCA Kurulum Tipi Seçin... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Lütfen bu NCA dosyası için belirlemek istediğiniz başlık türünü seçiniz: (Çoğu durumda, varsayılan olan 'Oyun' kullanılabilir.) - + Failed to Install Kurulum Başarısız Oldu - + The title type you selected for the NCA is invalid. NCA için seçtiğiniz başlık türü geçersiz - + File not found Dosya Bulunamadı - + File "%1" not found Dosya "%1" Bulunamadı - + OK Tamam - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Kayıp yuzu Hesabı - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Oyun uyumluluk test çalışması göndermek için öncelikle yuzu hesabınla giriş yapmanız gerekiyor.<br><br/>Yuzu hesabınızla giriş yapmak için, Emülasyon &gt; Yapılandırma &gt; Web'e gidiniz. - + Error opening URL URL açılırken bir hata oluştu - + Unable to open the URL "%1". URL "%1" açılamıyor. - + TAS Recording TAS kayıtta - + Overwrite file of player 1? Oyuncu 1'in dosyasının üstüne yazılsın mı? - + Invalid config detected Geçersiz yapılandırma tespit edildi - + Handheld controller can't be used on docked mode. Pro controller will be selected. Handheld kontrolcü dock modunda kullanılamaz. Pro kontrolcü seçilecek. - - - Error - Hata - - - - - The current game is not looking for amiibos - Aktif oyun amiibo beklemiyor - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed Amiibo kaldırıldı - + + Error + Hata + + + + + The current game is not looking for amiibos + Aktif oyun amiibo beklemiyor + + + Amiibo File (%1);; All Files (*.*) Amiibo Dosyası (%1);; Tüm Dosyalar (*.*) - + Load Amiibo Amiibo Yükle - - Error opening Amiibo data file - Amiibo veri dosyasını açarken hata - - - - Unable to open Amiibo file "%1" for reading. - "%1" Amiibo dosyası okunamadı - - - - Error reading Amiibo data file - Amiibo veri dosyasını okurken hata - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Amiibo verisi tamamen okunamadı. %1 byte okunması bekleniyordu, fakat bunun sadece %2'si okunabildi. - - - + Error loading Amiibo data Amiibo verisi yüklenirken hata - - Unable to load Amiibo data. - Amiibo verisi yüklenemedi + + The selected file is not a valid amiibo + Seçtiğiniz dosya geçerli bir amiibo değil - + + The selected file is already on use + Seçtiğiniz dosya hali hazırda kullanılıyor + + + + An unknown error occurred + + + + Capture Screenshot Ekran Görüntüsü Al - + PNG Image (*.png) PNG görüntüsü (*.png) - + TAS state: Running %1/%2 TAS durumu: %1%2 çalışıyor - + TAS state: Recording %1 TAS durumu: %1 kaydediliyor - + TAS state: Idle %1/%2 TAS durumu: %1%2 boşta - + TAS State: Invalid TAS durumu: Geçersiz - + &Stop Running &Çalıştırmayı durdur - + &Start &Başlat - + Stop R&ecording K&aydetmeyi Durdur - + R&ecord K&aydet - + Building: %n shader(s) Oluşturuluyor: %n shaderOluşturuluyor: %n shader - + Scale: %1x %1 is the resolution scaling factor Ölçek: %1x - + Speed: %1% / %2% Hız %1% / %2% - + Speed: %1% Hız: %1% - + Game: %1 FPS (Unlocked) Oyun: %1 FPS (Sınırsız) - + Game: %1 FPS Oyun: %1 FPS - + Frame: %1 ms Kare: %1 ms - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU YÜKSEK - + GPU EXTREME GPU EKSTREM - + GPU ERROR GPU HATASI - + DOCKED - + HANDHELD - + NEAREST EN YAKIN - - + + BILINEAR BILINEAR - + BICUBIC BICUBIC - + GAUSSIAN GAUSYEN - + SCALEFORCE SCALEFORCE - + FSR FSR - - + + NO AA - NO AA + AA YOK - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Yüklemeye çalıştığınız oyun oynanmadan önce Switch'inizden ek dosyaların alınmasını gerektiriyor.<br/><br/>Bu dosyaları nasıl alacağınız hakkında daha fazla bilgi için, lütfen bu wiki sayfasına göz atınız: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Konsolunuzdan Sistem Arşivleri ve Shared Fontları Almak</a>.<br/><br/>oyun listesine geri dönmek ister misiniz? Emülasyona devam etmek çökmelere, kayıt dosyalarının bozulmasına veya başka hatalara sebep verebilir. - + yuzu was unable to locate a Switch system archive. %1 Yuzu bir Switch sistem arşivi bulamadı. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 Yuzu bir Switch sistem arşivi bulamadı: %1. %2 - + System Archive Not Found Sistem Arşivi Bulunamadı - + System Archive Missing Sistem Arşivi Kayıp - + yuzu was unable to locate the Switch shared fonts. %1 Yuzu Switch shared fontlarını bulamadı. %1 - + Shared Fonts Not Found Shared Font'lar Bulunamadı - + Shared Font Missing Shared Font Kayıp - + Fatal Error Önemli Hata - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Yuzu önemli bir hatayla karşılaştı, lütfen daha fazla detay için kütüğe bakınız. Kütüğe erişmek hakkında daha fazla bilgi için, lütfen bu sayfaya göz atınız: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Log Dosyası Nasıl Yüklenir</a>.<br/><br/>Oyun listesine geri dönmek ister misiniz? Emülasyona devam etmek çökmelere, kayıt dosyalarının bozulmasına veya başka hatalara sebep olabilir. - + Fatal Error encountered Önemli Bir Hatayla Karşılaşıldı - + Confirm Key Rederivation Anahtar Yeniden Türetimini Onayla - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5074,37 +5248,37 @@ ve opsiyonel olarak yedekler alın. Bu sizin otomatik oluşturulmuş anahtar dosyalarınızı silecek ve anahtar türetme modülünü tekrar çalıştıracak. - + Missing fuses Anahtarlar Kayıp - + - Missing BOOT0 - BOOT0 Kayıp - + - Missing BCPKG2-1-Normal-Main - BCPKG2-1-Normal-Main Kayıp - + - Missing PRODINFO - PRODINFO Kayıp - + Derivation Components Missing Türeten Bileşenleri Kayıp - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> Şifreleme anahtarları eksik. <br>Lütfen takip edin<a href='https://yuzu-emu.org/help/quickstart/'>yuzu hızlı başlangıç kılavuzunu</a>tüm anahtarlarınızı, aygıt yazılımınızı ve oyunlarınızı almada.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5113,39 +5287,39 @@ Bu sistem performansınıza bağlı olarak bir dakika kadar zaman alabilir. - + Deriving Keys Anahtarlar Türetiliyor - + Select RomFS Dump Target RomFS Dump Hedefini Seçiniz - + Please select which RomFS you would like to dump. Lütfen dump etmek istediğiniz RomFS'i seçiniz. - + Are you sure you want to close yuzu? yuzu'yu kapatmak istediğinizden emin misiniz? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Emülasyonu durdurmak istediğinizden emin misiniz? Kaydedilmemiş veriler kaybolur. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5157,38 +5331,38 @@ Görmezden gelip kapatmak ister misiniz? GRenderWindow - + OpenGL not available! OpenGL kullanıma uygun değil! - + yuzu has not been compiled with OpenGL support. Yuzu OpenGL desteklememektedir. - - + + Error while initializing OpenGL! OpenGl başlatılırken bir hata oluştu! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. GPU'nuz OpenGL desteklemiyor veya güncel bir grafik sürücüsüne sahip değilsiniz. - + Error while initializing OpenGL 4.6! OpenGl 4.6 başlatılırken bir hata oluştu! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 GPU'nuz OpenGL 4.6'yı desteklemiyor veya güncel bir grafik sürücüsüne sahip değilsiniz.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 GPU'nuz gereken bir yada daha fazla OpenGL eklentisini desteklemiyor Lütfen güncel bir grafik sürücüsüne sahip olduğunuzdan emin olun.<br><br>GL Renderer:<br>%1<br><br> Desteklenmeyen Eklentiler:<br>%2 @@ -5196,153 +5370,153 @@ Görmezden gelip kapatmak ister misiniz? GameList - + Favorite Favori - + Start Game Oyunu Başlat - + Start Game without Custom Configuration Oyunu Özel Yapılandırma Olmadan Başlat - + Open Save Data Location Kayıt Dosyası Konumunu Aç - + Open Mod Data Location Mod Dosyası Konumunu Aç - + Open Transferable Pipeline Cache Transfer Edilebilir Pipeline Cache'ini Aç - + Remove Kaldır - + Remove Installed Update Yüklenmiş Güncellemeleri Kaldır - + Remove All Installed DLC Yüklenmiş DLC'leri Kaldır - + Remove Custom Configuration Oyuna Özel Yapılandırmayı Kaldır - + Remove OpenGL Pipeline Cache OpenGL Pipeline Cache'ini Kaldır - + Remove Vulkan Pipeline Cache Vulkan Pipeline Cache'ini Kaldır - + Remove All Pipeline Caches Bütün Pipeline Cache'lerini Kaldır - + Remove All Installed Contents Tüm Yüklenmiş İçeriği Kaldır - + Dump RomFS RomFS Dump Et - + Dump RomFS to SDMC RomFS'i SDMC'ye çıkar. - + Copy Title ID to Clipboard Title ID'yi Panoya Kopyala - + Navigate to GameDB entry GameDB sayfasına yönlendir - + Properties Özellikler - + Scan Subfolders Alt Klasörleri Tara - + Remove Game Directory Oyun Konumunu Kaldır - + ▲ Move Up ▲Yukarı Git - + ▼ Move Down ▼Aşağı Git - + Open Directory Location Oyun Dosyası Konumunu Aç - + Clear Temizle - + Name İsim - + Compatibility Uyumluluk - + Add-ons Eklentiler - + File type Dosya türü - + Size Boyut @@ -5351,84 +5525,61 @@ Görmezden gelip kapatmak ister misiniz? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Mükemmel - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - Oyun grafik veya ses hataları olmadan sorunsuz çalışıyor, tüm test edilmiş özellikler -geçici çözümler gerektirmeden -beklendiği gibi çalışıyor. - - - - Great - Çok iyi - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - Oyun küçük grafik veya ses hatalarıyla çalışıyor ve baştan sona kadar oynanabilir. Bazı -geçici çözümler -gerektirebilir. - - Okay - Yeterli - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - Oyun büyük grafik veya ses hatalarıyla çalışıyor fakat geçici çözümler ile baştan sona -kadar -oynanabilir. + Game can be played without issues. + - Bad - Kötü + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Oyun çalışıyor fakat büyük grafik veya ses hatalarına sahip. Geçici çözümlerle bile -hatalardan dolayı -bazı alanlar geçilemiyor. + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu İntro/Menü - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Büyük grafik ve ses sorunlardan dolayı oyun oynanamaz durumda. Başlangıç ekranından ileriye geçilmiyor. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Açılmıyor - + The game crashes when attempting to startup. Oyun açılmaya çalışıldığında çöküyor. - + Not Tested Test Edilmedi - + The game has not yet been tested. Bu oyun henüz test edilmedi. @@ -5436,7 +5587,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list Oyun listesine yeni bir klasör eklemek için çift tıklayın. @@ -5449,12 +5600,12 @@ Screen. %n sonucun %1'i%n sonucun %1'i - + Filter: Filtre: - + Enter pattern to filter Filtrelemek için bir düzen giriniz @@ -5464,22 +5615,22 @@ Screen. Create Room - + Oda Oluştur Room Name - + Oda Adı Preferred Game - + Tercih Edilen Oyun Max Players - + Maksimum Oyuncular @@ -5489,66 +5640,67 @@ Screen. (Leave blank for open game) - + (Açık oyun için boş bırakın) Password - + Şifre Port - + Port Room Description - + Oda Açıklaması Load Previous Ban List - + Önceki Yasak Listesini Yükle Public - + Herkese Açık Unlisted - + Gizli Host Room - + Oda Aç HostRoomWindow - + Error Hata - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - + Oda herkese açık yapılamadı. Eğer odayı herkese açık yapmak istiyorsanız, geçerli bir yuzu hesabını Emülasyon -> Yapılandır -> Web'den ayarlamalısınız. Eğer odayı herkese açık yapmak istemiyorsanız, lütfen Gizli seçeneğini seçin. Hotkeys - + Audio Mute/Unmute - + Sesi Sustur/Aç + @@ -5570,114 +5722,113 @@ Debug Message: - Main Window - + Ana Pencere + + + + Audio Volume Down + Ses Kapa - Audio Volume Down - + Audio Volume Up + Ses Aç - Audio Volume Up - - - - Capture Screenshot Ekran Görüntüsü Al - + Change Adapting Filter - + Uyarlanan Filtreyi Değiştir - + Change Docked Mode - + Change GPU Accuracy - + GPU Doğruluğunu Değiştir + + + + Continue/Pause Emulation + Sürdür/Emülasyonu duraklat - Continue/Pause Emulation - + Exit Fullscreen + Tam Ekrandan Çık - Exit Fullscreen - + Exit yuzu + Yuzu'dan çık - Exit yuzu - - - - Fullscreen Tam Ekran - + Load File Dosya Aç - + Load/Remove Amiibo - + Amiibo Yükle/Kaldır + + + + Restart Emulation + Emülasyonu Yeniden Başlat - Restart Emulation - + Stop Emulation + Emülasyonu Durdur - Stop Emulation - + TAS Record + TAS Kaydet - TAS Record - + TAS Reset + TAS Sıfırla - TAS Reset - + TAS Start/Stop + TAS Başlat/Durdur - TAS Start/Stop - + Toggle Filter Bar + Filtre Çubuğunu Aç/Kapa - Toggle Filter Bar - + Toggle Framerate Limit + FPS Limitini Aç/Kapa - Toggle Framerate Limit - + Toggle Mouse Panning + Mouse ile Kaydırmayı Aç/Kapa - Toggle Mouse Panning - - - - Toggle Status Bar - + Durum Çubuğunu Aç/Kapa @@ -5756,78 +5907,78 @@ Debug Message: Public Room Browser - + Herkese Açık Oda Listesi Nickname - + Lakap Filters - + Filtreler Search - + Ara Games I Own - + Sahip Olduğum Oyunlar Hide Full Rooms - + Dolu Odaları Gizle Refresh Lobby - + Lobiyi Yenile - + Password Required to Join - + Katılmak için Gereken Şifre - + Password: - + Şifre: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Oyuncular - - Refreshing - + + Room Name + Oda Adı - + + Preferred Game + Tercih Edilen Oyun + + + + Host + Ana bilgisayar + + + + Refreshing + Yenileniyor + + + Refresh List - + Listeyi Yenile @@ -5848,232 +5999,237 @@ Debug Message: &Son kullanılan Dosyalar - + &Emulation &Emülasyon - + &View &Görünüm - + &Reset Window Size &Pencere Boyutunu Sıfırla - + &Debugging &Hata Ayıklama - + Reset Window Size to &720p Pencere Boyutunu &720p'ye Sıfırla - + Reset Window Size to 720p Pencere Boyutunu 720p'ye Sıfırla - + Reset Window Size to &900p Pencere Boyutunu &900p'ye Sıfırla - + Reset Window Size to 900p Pencere Boyutunu 900p'ye Sıfırla - + Reset Window Size to &1080p Pencere Boyutunu &1080p'ye Sıfırla - + Reset Window Size to 1080p Pencere Boyutunu 1080p'ye Sıfırla - + + &Multiplayer + &Çok Oyunculu + + + &Tools &Aletler - + &TAS &TAS - + &Help &Yardım - + &Install Files to NAND... &NAND'e Dosya Kur... - + L&oad File... &Dosyayı Yükle... - + Load &Folder... &Klasörü Yükle... - + E&xit &Çıkış - + &Pause &Duraklat - + &Stop Du&rdur - + &Reinitialize keys... &Anahtarları Yeniden Kur... - + &About yuzu &Yuzu Hakkında - + Single &Window Mode &Tek Pencere Modu - + Con&figure... &Yapılandır... - + Display D&ock Widget Headers D&ock Widget Başlıkları'nı Göster - + Show &Filter Bar &Filtre Çubuğu'nu Göster - + Show &Status Bar &Durum Çubuğu'nu Göster - + Show Status Bar Durum Çubuğunu Göster - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room - + &Browse Public Game Lobby + &Herkese Açık Oyun Lobilerine Göz At - - Direct Connect to Room - + + &Create Room + &Oda Oluştur - - Show Current Room - + + &Leave Room + &Odadan Ayrıl + &Direct Connect to Room + &Odaya Direkt Bağlan + + + + &Show Current Room + &Şu Anki Odayı Göster + + + F&ullscreen &Tam Ekran - + &Restart &Yeniden Başlat - + Load/Remove &Amiibo... &Amiibo Yükle/Kaldır - + &Report Compatibility &Uyumluluk Bildir - + Open &Mods Page &Mod Sayfasını Aç - + Open &Quickstart Guide &Hızlı Başlangıç Kılavuzunu Aç - + &FAQ &SSS - + Open &yuzu Folder &yuzu Klasörünü Aç - + &Capture Screenshot &Ekran Görüntüsü Al - + &Configure TAS... &TAS'i Ayarla... - + Configure C&urrent Game... &Geçerli Oyunu Yapılandır... - + &Start B&aşlat - + &Reset &Sıfırla - + R&ecord K&aydet @@ -6091,92 +6247,88 @@ Debug Message: Moderation - + Moderasyon Ban List - + Ban Listesi Refreshing - + Yenileniyor Unban - + Yasağı kaldır Subject - + Konu Type - + Tip Forum Username - + Forum Kullanıcı Adı IP Address - + IP Adresi Refresh - + Yenile MultiplayerState - - + Current connection status - + Anlık bağlantı durumu - - + Not Connected. Click here to find a room! - + Bağlantı Yok. Oda bulmak için buraya basın! - - + Not Connected + Bağlantı Yok + + + Connected Bağlandı - - - Not Connected - + + New Messages Received + Yeni Mesaj Alındı - + Error Hata - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - - - New Messages Received - + Oda bilgisi güncellenemedi. Lütfen İnternet bağlantınızı kontrol edin ve yeniden bir oda açmayı deneyin. +Debug Bilgisi: @@ -6184,37 +6336,37 @@ Debug Message: Username is not valid. Must be 4 to 20 alphanumeric characters. - + Kullanıcı adı geçersiz. 4 ile 20 alfasayısal karakter arasında olmalı. Room name is not valid. Must be 4 to 20 alphanumeric characters. - + Oda adı geçersiz. 4 ile 20 alfasayısal karakter arasında olmalı. Username is already in use or not valid. Please choose another. - + Kullanıcı adı halihazırda kullanılıyor. Lütfen başka bir kullanıcı adı seçin. IP is not a valid IPv4 address. - + IP geçerli bir IPv4 adresi değil. Port must be a number between 0 to 65535. - + Port 0 ile 65535 arasında bir numara olmalı. You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list. - + Bir oda açabilmeniz için bir Tercih Edilen Oyun seçmeniz gerekir. Eğer oyun listenizde hiçbir oyun yoksa, oyun listesindeki artı ikonuna basarak yeni bir oyun klasörü ekleyebilirsiniz. Unable to find an internet connection. Check your internet settings. - + İnternet bağlantısı bulunamadı. İnternet ayarlarınızı kontrol edin. @@ -6224,84 +6376,104 @@ Debug Message: Unable to connect to the room because it is already full. - + Oda halihazırda dolu olduğundan dolayı katılınamadı. Creating a room failed. Please retry. Restarting yuzu might be necessary. - + Odayı oluşturma başarısız oldu. Lütfen tekrar deneyin. Yuzu'yu yeniden başlatmak gerekebilir. The host of the room has banned you. Speak with the host to unban you or try a different room. - + Oda yöneticisi sizi odadan yasakladı. Yasağı kaldırmak için yönetici ile konuşun ya da başka bir oda deneyin. Version mismatch! Please update to the latest version of yuzu. If the problem persists, contact the room host and ask them to update the server. - + Sürümler uyuşmuyor! Lütfen yuzu'yu son sürüme güncelleyin. Eğer sorun devam ediyorsa, oda yöneticisine ulaşın ve sunucuyu güncellemelerini isteyin. Incorrect password. - + Yanlış şifre. An unknown error occurred. If this error continues to occur, please open an issue - + Bilinmeyen bir hata oluştu. Eğer bu hata devam ederse, lütfen bir hata raporu açın. Connection to room lost. Try to reconnect. - + Odaya bağlantı kesildi. Yeniden bağlanmayı dene. You have been kicked by the room host. - + Oda yöneticisi seni odadan çıkardı. IP address is already in use. Please choose another. - + IP adresi halihazırda kullanılıyor. Lütfen başka bir IP adresi seçin. You do not have enough permission to perform this action. - + Bu işlemi yapabilmek için yeterli yetkiye sahip değilsiniz. The user you are trying to kick/ban could not be found. They may have left the room. - + Kicklemeye/banlamaya çalıştığınız kullanıcı bulunamadı. +Odadan çıkmış olabilir. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + Geçerli bir ağ arayüzü seçilmedi. +Lütfen Yapılandır -> Sistem -> Ağ'a gidip bir seçim yapınız. + + + + Game already running + Oyun halihazırda çalışıyor + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + Bir oyun çalışırken odaya katılmak tavsiye edilmez ve oda özelliğinin çalışmamasına sebep olabilir. +Devam etmek istiyor musunuz? + + + Leave Room - + Odadan Ayrıl - + You are about to close the room. Any network connections will be closed. - + Odayı kapatmak üzeresiniz. Herhangi bir ağ bağlantısı kapanacaktır. - + Disconnect - + Bağlantı kesildi - + You are about to leave the room. Any network connections will be closed. - + Odadan çıkmak üzeresiniz. Herhangi bir ağ bağlantısı kapanacaktır. NetworkMessage::ErrorManager - + Error Hata @@ -6344,48 +6516,48 @@ p, li { white-space: pre-wrap; } START/PAUSE - BAŞLAT/DURDUR + BAŞLAT/DURAKLAT QObject - + %1 is not playing a game - + %1 şu anda oyun oynamıyor - + %1 is playing %2 - + %1 %2'yi oynuyor - + Not playing a game - + Şu anda oyun oynamıyor - + Installed SD Titles Yüklenmiş SD Oyunları - + Installed NAND Titles Yüklenmiş NAND Oyunları - + System Titles Sistemde Yüklü Oyunlar - + Add New Game Directory Yeni Oyun Konumu Ekle - + Favorites Favoriler @@ -6415,7 +6587,7 @@ p, li { white-space: pre-wrap; } - + [not set] [belirlenmedi] @@ -6430,10 +6602,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Eksen %1%2 @@ -6447,9 +6619,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [bilinmeyen] @@ -6614,15 +6786,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [geçersiz] - - + + %1%2Hat %3 %1%2Hat %3 @@ -6630,35 +6802,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2Eksen %3 - + %1%2Axis %3,%4,%5 %1%2Eksen %3,%4,%5 - + %1%2Motion %3 %1%2Hareket %3 - - + + %1%2Button %3 %1%2Tuş %3 - + [unused] [kullanılmayan] @@ -6676,7 +6848,7 @@ p, li { white-space: pre-wrap; } Wheel Indicates the mouse wheel - + Fare Tekerleği @@ -6699,11 +6871,124 @@ p, li { white-space: pre-wrap; } Ekstra - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + Tip + + + + Name + İsim + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6807,6 +7092,7 @@ p, li { white-space: pre-wrap; } + Handheld Handheld @@ -6846,11 +7132,6 @@ p, li { white-space: pre-wrap; } Docked Dock Modu Aktif - - - Undocked - Dock Modu Devre Dışı - Vibration diff --git a/dist/languages/uk.ts b/dist/languages/uk.ts new file mode 100644 index 0000000..8cd8e2e --- /dev/null +++ b/dist/languages/uk.ts @@ -0,0 +1,7513 @@ + + + AboutDialog + + + About yuzu + Про yuzu + + + + <html><head/><body><p><span style=" font-size:28pt;">yuzu</span></p></body></html> + <html><head/><body><p><span style=" font-size:28pt;">yuzu</span></p></body></html> + + + + <html><head/><body><p>%1 (%2)</p></body></html> + <html><head/><body><p>%1 (%2)</p></body></html> + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">This software should not be used to play games you have not legally obtained.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">yuzu є експериментальним емулятором Nintendo Switch з відкритим кодом ліцензований під GPLv3.0+.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Це програмне забезпечення не слід використовувати для ігор, які ви отримали незаконним шляхом.</span></p></body></html> + + + + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Веб-сайт</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Вихідний код</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Вкладники</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">Ліцензія</span></a></p></body></html> + + + + <html><head/><body><p><span style=" font-size:7pt;">&quot;Nintendo Switch&quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.</span></p></body></html> + <html><head/><body><p><span style=" font-size:7pt;">&quot;Nintendo Switch&quot; є торговою маркою Nintendo. yuzu не пов'язаний з Nintendo у будь-якому вигляді.</span></p></body></html> + + + + CalibrationConfigurationDialog + + + Communicating with the server... + Зв'язок із сервером... + + + + Cancel + Скасувати + + + + Touch the top left corner <br>of your touchpad. + Торкніться верхнього лівого кута <br> вашого тачпаду. + + + + Now touch the bottom right corner <br>of your touchpad. + Тепер торкніться правого нижнього кута <br> вашого тачпаду. + + + + Configuration completed! + Налаштування завершено! + + + + OK + ОК + + + + ChatRoom + + + Room Window + Вікно кімнати + + + + Send Chat Message + Надіслати повідомлення в чат + + + + Send Message + Надіслати повідомлення + + + + Members + Члени + + + + %1 has joined + %1 приєднався + + + + %1 has left + %1 вийшов + + + + %1 has been kicked + %1 вигнано + + + + %1 has been banned + %1 заблоковано + + + + %1 has been unbanned + %1 розблоковано + + + + View Profile + Переглянути профіль + + + + + Block Player + Заблокувати гравця + + + + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + Коли ви блокуєте гравця, ви більше не отримуватиме від нього повідомлення у чаті. <br><br>Ви впевнені що бажаєте заблокувати %1? + + + + Kick + Вигнати + + + + Ban + Заблокувати + + + + Kick Player + Вигнати гравця + + + + Are you sure you would like to <b>kick</b> %1? + Ви впевнені що бажаєте <b>вигнати</b> %1? + + + + Ban Player + Заблокувати гравця + + + + Are you sure you would like to <b>kick and ban</b> %1? + +This would ban both their forum username and their IP address. + Ви впевнені що бажаєте <b>вигнати і заблокувати</b> %1? + +Ця дія заблокує ім'я користувача на форумі та IP-адресу. + + + + ClientRoom + + + Room Window + Вікно кімнати + + + + Room Description + Опис кімнати + + + + Moderation... + Модерація... + + + + Leave Room + Залишити кімнату + + + + ClientRoomWindow + + + Connected + З'єднано + + + + Disconnected + Роз'єднано + + + + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 члени) - з'єднано + + + + CompatDB + + + Report Compatibility + Повідомити про сумісність + + + + + + + + + + Report Game Compatibility + Повідомити про сумісність гри + + + + <html><head/><body><p><span style=" font-size:10pt;">Should you choose to submit a test case to the </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">yuzu Compatibility List</span></a><span style=" font-size:10pt;">, The following information will be collected and displayed on the site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware Information (CPU / GPU / Operating System)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Which version of yuzu you are running</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The connected yuzu account</li></ul></body></html> + <html><head/><body><p><span style=" font-size:10pt;">Якщо ви бажаєте надіслати звіт до </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">списку сумісності yuzu</span></a><span style=" font-size:10pt;">, наступна інформація буде зібрана та відображена на сайті:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Інформація про залізо (ЦП / ГП / Операційна система)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Версія yuzu</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Підключений акаунт yuzu</li></ul></body></html> + + + + <html><head/><body><p>Does the game boot?</p></body></html> + + + + + Yes The game starts to output video or audio + + + + + No The game doesn't get past the "Launching..." screen + + + + + Yes The game gets past the intro/menu and into gameplay + + + + + No The game crashes or freezes while loading or using the menu + + + + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + + + + + Yes The game works without crashes + + + + + No The game crashes or freezes during gameplay + + + + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + + + + + Yes The game can be finished without any workarounds + + + + + No The game can't progress past a certain area + + + + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + + + + + Major The game has major graphical errors + + + + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + + Thank you for your submission! + Дякуємо за ваш звіт! + + + + Submitting + Надсилання + + + + Communication error + Помилка з'єднання + + + + An error occurred while sending the Testcase + Сталася помилка під час надсилання звіту + + + + Next + Далі + + + + ConfigureAudio + + + + Audio + Аудіо + + + + Output Engine: + Рушій виводу: + + + + Output Device + Пристрій виводу + + + + Input Device + Пристрій вводу + + + + Use global volume + Використовувати загальну гучність + + + + Set volume: + Встановити гучність: + + + + Volume: + Гучність + + + + 0 % + 0 % + + + + %1% + Volume percentage (e.g. 50%) + %1% + + + + ConfigureCamera + + + Configure Infrared Camera + Налаштування інфрачервоної камери + + + + Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera. + Виберіть, звідки береться зображення емульованої камери. Це може бути віртуальна або реальна камера. + + + + Camera Image Source: + Джерело зображення камери: + + + + Input device: + Пристрій вводу: + + + + Preview + Попередній перегляд + + + + Resolution: 320*240 + Роздільна здатність: 320*240 + + + + Click to preview + Натисніть для попереднього перегляду + + + + Restore Defaults + Значення за замовчуванням + + + + Auto + Авто + + + + ConfigureCpu + + + Form + Форма + + + + CPU + ЦП + + + + General + Загальні + + + + Accuracy: + Точність: + + + + Auto + Авто + + + + Accurate + Точно + + + + Unsafe + Небезпечно + + + + Paranoid (disables most optimizations) + Параноїк (відключає більшість оптимізацій) + + + + We recommend setting accuracy to "Auto". + Ми рекомендуємо встановити точність на "Авто". + + + + Unsafe CPU Optimization Settings + Небезпечні налаштування оптимізації ЦП + + + + These settings reduce accuracy for speed. + Ці налаштування зменшують точність заради швидкості. + + + + + <div>This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.</div> + + + + + + Unfuse FMA (improve performance on CPUs without FMA) + Не використовувати FMA (покращує продуктивність на ЦП без FMA) + + + + + <div>This option improves the speed of some approximate floating-point functions by using less accurate native approximations.</div> + + + + + + Faster FRSQRTE and FRECPE + Прискорені FRSQRTE та FRECPE + + + + + <div>This option improves the speed of 32 bits ASIMD floating-point functions by running with incorrect rounding modes.</div> + + + + + + Faster ASIMD instructions (32 bits only) + Швидші інструкції ASIMD (лише 32 біт) + + + + + <div>This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.</div> + + + + + + Inaccurate NaN handling + Неправильна обробка NaN + + + + + <div>This option improves speed by eliminating a safety check before every memory read/write in guest. Disabling it may allow a game to read/write the emulator's memory.</div> + + + + + + Disable address space checks + + + + + + <div>This option improves speed by relying only on the semantics of cmpxchg to ensure safety of exclusive access instructions. Please note this may result in deadlocks and other race conditions.</div> + + + + + + Ignore global monitor + + + + + CPU settings are available only when game is not running. + Налаштування ЦП недоступні, поки запущена гра. + + + + ConfigureCpuDebug + + + Form + Форма + + + + CPU + ЦП + + + + Toggle CPU Optimizations + Увімкнути оптимізації ЦП + + + + <html><head/><body><p><span style=" font-weight:600;">For debugging only.</span><br/>If you're not sure what these do, keep all of these enabled. <br/>These settings, when disabled, only take effect when CPU Debugging is enabled. </p></body></html> + <html><head/><body><p><span style=" font-weight:600;">Тільки для налагодження.</span><br/>Якщо ви не впевнені в тому, що вони роблять, залиште всі ці параметри увімкненими. <br/>Коли їх вимкнено, ці параметри набувають чинності лише за увімкненого налагодження ЦП. </p></body></html> + + + + + <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> + <div style="white-space: nowrap">Enabling it inlines accesses to PageTable::pointers into emitted code.</div> + <div style="white-space: nowrap">Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.</div> + + + + + + Enable inline page tables + + + + + + <div>This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.</div> + + + + + + Enable block linking + + + + + + <div>This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.</div> + + + + + + Enable return stack buffer + + + + + + <div>Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.</div> + + + + + + Enable fast dispatcher + + + + + + <div>Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.</div> + + + + + + Enable context elimination + + + + + + <div>Enables IR optimizations that involve constant propagation.</div> + + + + + + Enable constant propagation + + + + + + <div>Enables miscellaneous IR optimizations.</div> + + + + + + Enable miscellaneous optimizations + Увімкнути різні оптимізації + + + + + <div style="white-space: nowrap">When enabled, a misalignment is only triggered when an access crosses a page boundary.</div> + <div style="white-space: nowrap">When disabled, a misalignment is triggered on all misaligned accesses.</div> + + + + + + Enable misalignment check reduction + + + + + + <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> + <div style="white-space: nowrap">Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU.</div> + <div style="white-space: nowrap">Disabling this forces all memory accesses to use Software MMU Emulation.</div> + + + + + + Enable Host MMU Emulation (general memory instructions) + + + + + + <div style="white-space: nowrap">This optimization speeds up exclusive memory accesses by the guest program.</div> + <div style="white-space: nowrap">Enabling it causes guest exclusive memory reads/writes to be done directly into memory and make use of Host's MMU.</div> + <div style="white-space: nowrap">Disabling this forces all exclusive memory accesses to use Software MMU Emulation.</div> + + + + + + Enable Host MMU Emulation (exclusive memory instructions) + + + + + + <div style="white-space: nowrap">This optimization speeds up exclusive memory accesses by the guest program.</div> + <div style="white-space: nowrap">Enabling it reduces the overhead of fastmem failure of exclusive memory accesses.</div> + + + + + + Enable recompilation of exclusive memory instructions + + + + + CPU settings are available only when game is not running. + Налаштування ЦП доступні тільки тоді, коли гру не запущено. + + + + ConfigureDebug + + + Debugger + Налагоджувач + + + + Enable GDB Stub + + + + + Port: + Порт: + + + + Logging + Журналювання + + + + Global Log Filter + Глобальний фільтр журналів + + + + Show Log in Console + Показувати журнал у консолі + + + + Open Log Location + Відкрити папку для журналів + + + + When checked, the max size of the log increases from 100 MB to 1 GB + Якщо увімкнено, максимальний розмір журналу збільшується зі 100 МБ до 1 ГБ + + + + Enable Extended Logging** + Увімкнути розширене ведення журналу** + + + + Homebrew + Homebrew + + + + Arguments String + Рядок аргументів + + + + Graphics + Графіка + + + + When checked, the graphics API enters a slower debugging mode + Якщо увімкнено, графічний API переходить у повільніший режим налагодження + + + + Enable Graphics Debugging + Увімкнути налагодження графіки + + + + When checked, it enables Nsight Aftermath crash dumps + + + + + Enable Nsight Aftermath + + + + + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found + Якщо ввімкнено, буде дампити всі оригінальні шейдери асемблера з кешу шейдерів на диску або гри як знайдені + + + + Dump Game Shaders + Дамп ігрових шейдерів + + + + When checked, it will dump all the macro programs of the GPU + + + + + Dump Maxwell Macros + + + + + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower + + + + + Disable Macro JIT + + + + + When checked, yuzu will log statistics about the compiled pipeline cache + Якщо увімкнено, yuzu записуватиме статистику про скомпільований кеш конвеєра + + + + Enable Shader Feedback + Увімкнути зворотний зв'язок про шейдери + + + + When checked, it executes shaders without loop logic changes + + + + + Disable Loop safety checks + + + + + Debugging + Налагодження + + + + Enable Verbose Reporting Services** + + + + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + + Advanced + Розширені + + + + Kiosk (Quest) Mode + Режим кіоску (Квест) + + + + Enable CPU Debugging + Увімкнути налагодження ЦП + + + + Enable Debug Asserts + + + + + Enable Auto-Stub** + + + + + Enable All Controller Types + Увімкнути всі типи контролерів + + + + Disable Web Applet + Вимкнути веб-аплет + + + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + Дозволяє yuzu перевіряти наявність робочого середовища Vulkan під час запуску програми. Вимкніть цю опцію, якщо це викликає проблеми з тим, що зовнішні програми бачать yuzu. + + + + Perform Startup Vulkan Check + Виконувати перевірку Vulkan під час запуску + + + + **This will be reset automatically when yuzu closes. + **Це буде автоматично скинуто після закриття yuzu. + + + + Restart Required + Потрібен перезапуск + + + + yuzu is required to restart in order to apply this setting. + yuzu потрібно перезапустити, щоб застосувати це налаштування. + + + + Web applet not compiled + Веб-аплет не скомпільовано + + + + MiniDump creation not compiled + + + + + ConfigureDebugController + + + Configure Debug Controller + Налаштування налагоджувального контролера + + + + Clear + Очистити + + + + Defaults + За замовчуванням + + + + ConfigureDebugTab + + + Form + Форма + + + + + Debug + Налагодження + + + + CPU + ЦП + + + + ConfigureDialog + + + yuzu Configuration + Налаштування yuzu + + + + + Audio + Аудіо + + + + + CPU + ЦП + + + + Debug + Налагодження + + + + Filesystem + Файлова система + + + + + General + Загальні + + + + + Graphics + Графіка + + + + GraphicsAdvanced + ГрафікаРозширені + + + + Hotkeys + Гарячі клавіші + + + + + Controls + Керування + + + + Profiles + Профілі + + + + Network + Мережа + + + + + System + Система + + + + Game List + Список ігор + + + + Web + Мережа + + + + ConfigureFilesystem + + + Form + Форма + + + + Filesystem + Файлова система + + + + Storage Directories + Каталоги зберігання + + + + NAND + NAND + + + + + + + + ... + ... + + + + SD Card + SD карта + + + + Gamecard + Картридж + + + + Path + Шлях + + + + Inserted + Вставлений + + + + Current Game + Поточна гра + + + + Patch Manager + Керування патчами + + + + Dump Decompressed NSOs + Дамп розпакованих NSO + + + + Dump ExeFS + Дамп ExeFS + + + + Mod Load Root + Папка з модами + + + + Dump Root + Корінь дампу + + + + Caching + Кешування + + + + Cache Game List Metadata + Кешувати метадані списку ігор + + + + + + + Reset Metadata Cache + Скинути кеш метаданих + + + + Select Emulated NAND Directory... + Виберіть папку для емульованого NAND... + + + + Select Emulated SD Directory... + Виберіть папку для емульованого SD... + + + + Select Gamecard Path... + Оберіть папку для картриджів... + + + + Select Dump Directory... + Оберіть папку для дампів... + + + + Select Mod Load Directory... + Оберіть папку для модів... + + + + The metadata cache is already empty. + Кеш метаданих вже порожній. + + + + The operation completed successfully. + Операція завершилася успішно. + + + + The metadata cache couldn't be deleted. It might be in use or non-existent. + Кеш метаданих не можна видалити. Можливо, він використовується або відсутній. + + + + ConfigureGeneral + + + Form + Форма + + + + + General + Загальні + + + + Limit Speed Percent + Обмеження відсотка швидкості + + + + % + % + + + + Multicore CPU Emulation + Багатоядерна емуляція ЦП + + + + Extended memory layout (6GB DRAM) + Розширене компонування пам'яті (6 ГБ DRAM) + + + + Confirm exit while emulation is running + Підтверджувати вихід під час емуляції + + + + Prompt for user on game boot + Запитувати користувача під час запуску гри + + + + Pause emulation when in background + Призупиняти емуляцію у фоновому режимі + + + + Mute audio when in background + Приглушити звук у фоновому режимі + + + + Hide mouse on inactivity + Приховування миші при бездіяльності + + + + Reset All Settings + Скинути всі налаштування + + + + yuzu + yuzu + + + + This reset all settings and remove all per-game configurations. This will not delete game directories, profiles, or input profiles. Proceed? + Це скине всі налаштування і видалить усі конфігурації під окремі ігри. При цьому не будуть видалені шляхи до ігор, профілів або профілів вводу. Продовжити? + + + + ConfigureGraphics + + + Form + Форма + + + + Graphics + Графіка + + + + API Settings + Налаштування API + + + + Shader Backend: + Бекенд шейдерів: + + + + Device: + Пристрій: + + + + API: + API: + + + + Graphics Settings + Налаштування графіки + + + + Use disk pipeline cache + Використовувати кеш конвеєра на диску + + + + Use asynchronous GPU emulation + Використовувати асинхронну емуляцію ГП + + + + Accelerate ASTC texture decoding + Прискорення декодування текстур ASTC + + + + NVDEC emulation: + Емуляція NVDEC: + + + + No Video Output + Відсутність відеовиходу + + + + CPU Video Decoding + Декодування відео на ЦП + + + + GPU Video Decoding (Default) + Декодування відео на ГП (за замовчуванням) + + + + Fullscreen Mode: + Повноекранний режим: + + + + Borderless Windowed + Вікно без рамок + + + + Exclusive Fullscreen + Ексклюзивний повноекранний + + + + Aspect Ratio: + Співвідношення сторін: + + + + Default (16:9) + За замовчуванням (16:9) + + + + Force 4:3 + Змусити 4:3 + + + + Force 21:9 + Змусити 21:9 + + + + Force 16:10 + Змусити 16:10 + + + + Stretch to Window + Розтягнути до вікна + + + + Resolution: + Роздільна здатність: + + + + 0.5X (360p/540p) [EXPERIMENTAL] + 0.5X (360p/540p) [ЕКСПЕРИМЕНТАЛЬНЕ] + + + + 0.75X (540p/810p) [EXPERIMENTAL] + 0.75X (540p/810p) [ЕКСПЕРИМЕНТАЛЬНЕ] + + + + 1X (720p/1080p) + 1X (720p/1080p) + + + + 2X (1440p/2160p) + 2X (1440p/2160p) + + + + 3X (2160p/3240p) + 3X (2160p/3240p) + + + + 4X (2880p/4320p) + 4X (2880p/4320p) + + + + 5X (3600p/5400p) + 5X (3600p/5400p) + + + + 6X (4320p/6480p) + 6X (4320p/6480p) + + + + Window Adapting Filter: + Фільтр адаптації вікна: + + + + Nearest Neighbor + Найближчий сусід + + + + Bilinear + Білінійне + + + + Bicubic + Бікубічне + + + + Gaussian + Гауса + + + + ScaleForce + ScaleForce + + + + AMD FidelityFX™️ Super Resolution (Vulkan Only) + AMD FidelityFX™️ Super Resolution (Лише для Vulkan) + + + + Anti-Aliasing Method: + Метод згладжування: + + + + None + Вимкнено + + + + FXAA + FXAA + + + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + + Use global background color + Використовувати глобальний фоновий колір + + + + Set background color: + Встановити фоновий колір: + + + + Background Color: + Фоновий колір: + + + + GLASM (Assembly Shaders, NVIDIA Only) + GLASM (асемблерні шейдери, лише для NVIDIA) + + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + + + + ConfigureGraphicsAdvanced + + + Form + Форма + + + + Advanced + Розширені + + + + Advanced Graphics Settings + Розширені налаштування графіки + + + + Accuracy Level: + Рівень точності: + + + + VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference. + Вертикальна синхронізація запобігає розривам екрана, але деякі відеокарти мають нижчу продуктивність при вертикальній синхронізації. Залишайте увімкненим, якщо ви не помічаєте різниці в продуктивності. + + + + Use VSync + Використувати вертикальну сінхронізацію + + + + Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental. + Вмикає асинхронну компіляцію шейдерів, що зменшить зависання через шейдери. Функція є експериментальною. + + + + Use asynchronous shader building (Hack) + Використовувати асинхронну побудову шейдерів (хак) + + + + Enables Fast GPU Time. This option will force most games to run at their highest native resolution. + Вмикає функцію Fast GPU Time. Цей параметр змусить більшість ігор працювати в максимальній рідній роздільній здатності. + + + + Use Fast GPU Time (Hack) + Увімкнути Fast GPU Time (Хак) + + + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + Вмикає песимістичне очищення буферів. Ця опція змушує промивати немодифіковані буфери, що може знизити продуктивність. + + + + Use pessimistic buffer flushes (Hack) + Використовувати песимістичне очищення буферів (Хак) + + + + Anisotropic Filtering: + Анізотропна фільтрація: + + + + Automatic + Автоматично + + + + Default + За замовчуванням + + + + 2x + 2x + + + + 4x + 4x + + + + 8x + 8x + + + + 16x + 16x + + + + ConfigureHotkeys + + + Hotkey Settings + Налаштування гарячих клавіш + + + + Hotkeys + Гарячі клавіші + + + + Double-click on a binding to change it. + Натисніть двічі на прив'язці, щоб змінити її. + + + + Clear All + Очистити все + + + + Restore Defaults + Відновити значення за замовчуванням. + + + + Action + Дія + + + + Hotkey + Гаряча клавіша + + + + Controller Hotkey + Гаряча клавіша контролера + + + + + + Conflicting Key Sequence + Конфліктуюча комбінація клавіш + + + + + The entered key sequence is already assigned to: %1 + Введена комбінація вже призначена до: %1 + + + + Home+%1 + Home+%1 + + + + [waiting] + [очікування] + + + + Invalid + Неприпустимо + + + + Restore Default + Відновити значення за замовчуванням + + + + Clear + Очистити + + + + Conflicting Button Sequence + Конфліктуюче поєднання кнопок + + + + The default button sequence is already assigned to: %1 + Типова комбінація кнопок вже призначена до: %1 + + + + The default key sequence is already assigned to: %1 + Типова комбінація клавіш вже призначена до: %1 + + + + ConfigureInput + + + ConfigureInput + НалаштуванняВводу + + + + + Player 1 + Гравець 1 + + + + + Player 2 + Гравець 2 + + + + + Player 3 + Гравець 3 + + + + + Player 4 + Гравець 4 + + + + + Player 5 + Гравець 5 + + + + + Player 6 + Гравець 6 + + + + + Player 7 + Гравець 7 + + + + + Player 8 + Гравець 8 + + + + + Advanced + Розширені + + + + Console Mode + Режим консолі + + + + Docked + У док-станції + + + + Handheld + Портативний + + + + Vibration + Вібрація + + + + + Configure + Налаштувати + + + + Motion + Рух + + + + Controllers + Контролери + + + + 1 + 1 + + + + 2 + 2 + + + + 3 + 3 + + + + 4 + 4 + + + + 5 + 5 + + + + 6 + 6 + + + + 7 + 7 + + + + 8 + 8 + + + + Connected + З'єднано + + + + Defaults + За замовчуванням + + + + Clear + Очистити + + + + ConfigureInputAdvanced + + + Configure Input + Налаштування вводу + + + + Joycon Colors + Кольори Joy-Con'ів + + + + Player 1 + Гравець 1 + + + + + + + + + + + L Body + Лівий контролер + + + + + + + + + + + L Button + Кнопка L + + + + + + + + + + + R Body + Правий контролер + + + + + + + + + + + R Button + Кнопка R + + + + Player 2 + Гравець 2 + + + + Player 3 + Гравець 3 + + + + Player 4 + Гравець 4 + + + + Player 5 + Гравець 5 + + + + Player 6 + Гравець 6 + + + + Player 7 + Гравець 7 + + + + Player 8 + Гравець 8 + + + + Emulated Devices + Емульовані пристрої + + + + Keyboard + Клавіатура + + + + Mouse + Миша + + + + Touchscreen + Сенсорний екран + + + + Advanced + Розширені + + + + Debug Controller + Налагоджувальний контролер + + + + + + + Configure + Налаштувати + + + + Ring Controller + Контролер Ring + + + + Infrared Camera + Інфрачервона камера + + + + Other + Інше + + + + Emulate Analog with Keyboard Input + Емуляція аналогового вводу з клавіатури + + + + Requires restarting yuzu + Потребує перезапуску yuzu + + + + Enable XInput 8 player support (disables web applet) + Увімкнути підтримку 8-ми гравців на XInput (відключає веб-аплет) + + + + Enable UDP controllers (not needed for motion) + Увімкнути UDP контролери (не обов'язково для руху) + + + + Controller navigation + Навігація контролера + + + + Enable mouse panning + Увімкнути панорамування миші + + + + Mouse sensitivity + Чутливість миші + + + + % + % + + + + Motion / Touch + Рух і сенсор + + + + ConfigureInputPlayer + + + Configure Input + Налаштування вводу + + + + Connect Controller + Підключити контролер + + + + Input Device + Пристрій вводу + + + + Profile + Профіль + + + + Save + Зберегти + + + + New + Новий + + + + Delete + Видалити + + + + + Left Stick + Лівий міні-джойстик + + + + + + + + + Up + Вгору + + + + + + + + + + Left + Вліво + + + + + + + + + + Right + Вправо + + + + + + + + + Down + Вниз + + + + + + + Pressed + Натиснення + + + + + + + Modifier + Модифікатор + + + + + Range + Діапазон + + + + + % + % + + + + + Deadzone: 0% + Мертва зона: 0% + + + + + Modifier Range: 0% + Діапазон модифікатора: 0% + + + + D-Pad + Кнопки напрямків + + + + + + L + L + + + + + + ZL + ZL + + + + + Minus + Мінус + + + + + Capture + Захоплення + + + + + + Plus + Плюс + + + + + Home + Home + + + + + + + R + R + + + + + + ZR + ZR + + + + + SL + SL + + + + + SR + SR + + + + Motion 1 + Рух 1 + + + + Motion 2 + Рух 2 + + + + Face Buttons + Основні кнопки + + + + + X + X + + + + + Y + Y + + + + + A + A + + + + + B + B + + + + + Right Stick + Правий міні-джойстик + + + + + + + Clear + Очистити + + + + + + + + [not set] + [не задано] + + + + + Invert button + Інвертувати кнопку + + + + + Toggle button + Переключити кнопку + + + + + Invert axis + Інвертувати осі + + + + + + Set threshold + Встановити поріг + + + + + Choose a value between 0% and 100% + Оберіть значення між 0% і 100% + + + + Toggle axis + Переключити осі + + + + Set gyro threshold + Встановити поріг гіроскопа + + + + Map Analog Stick + Задати аналоговий міні-джойстик + + + + After pressing OK, first move your joystick horizontally, and then vertically. +To invert the axes, first move your joystick vertically, and then horizontally. + Після натискання на ОК, рухайте ваш міні-джойстик горизонтально, а потім вертикально. +Щоб інвертувати осі, спочатку рухайте ваш міні-джойстик вертикально, а потім горизонтально. + + + + Center axis + Центрувати осі + + + + + Deadzone: %1% + Мертва зона: %1% + + + + + Modifier Range: %1% + Діапазон модифікатора: %1% + + + + + Pro Controller + Контролер Pro + + + + Dual Joycons + Подвійні Joy-Con'и + + + + Left Joycon + Лівий Joy-Con + + + + Right Joycon + Правий Joy-Con + + + + Handheld + Портативний + + + + GameCube Controller + Контролер GameCube + + + + Poke Ball Plus + Poke Ball Plus + + + + NES Controller + Контролер NES + + + + SNES Controller + Контролер SNES + + + + N64 Controller + Контролер N64 + + + + Sega Genesis + Sega Genesis + + + + Start / Pause + Старт / Пауза + + + + Z + Z + + + + Control Stick + Міні-джойстик керування + + + + C-Stick + C-Джойстик + + + + Shake! + Потрусіть! + + + + [waiting] + [очікування] + + + + New Profile + Новий профіль + + + + Enter a profile name: + Введіть ім'я профілю: + + + + + Create Input Profile + Створити профіль контролю + + + + The given profile name is not valid! + Задане ім'я профілю недійсне! + + + + Failed to create the input profile "%1" + Не вдалося створити профіль контролю "%1" + + + + Delete Input Profile + Видалити профіль контролю + + + + Failed to delete the input profile "%1" + Не вдалося видалити профіль контролю "%1" + + + + Load Input Profile + Завантажити профіль контролю + + + + Failed to load the input profile "%1" + Не вдалося завантажити профіль контролю "%1" + + + + Save Input Profile + Зберегти профіль контролю + + + + Failed to save the input profile "%1" + Не вдалося зберегти профіль контролю "%1" + + + + ConfigureInputProfileDialog + + + Create Input Profile + Створити профіль контролю + + + + Clear + Очистити + + + + Defaults + За замовчуванням + + + + ConfigureMotionTouch + + + Configure Motion / Touch + Налаштування руху та сенсора + + + + Touch + Сенсор + + + + UDP Calibration: + Калібрація UDP: + + + + (100, 50) - (1800, 850) + (100, 50) - (1800, 850) + + + + + + Configure + Налаштувати + + + + Touch from button profile: + Торкніться з профілю кнопки: + + + + CemuhookUDP Config + Налаштування CemuhookUDP + + + + You may use any Cemuhook compatible UDP input source to provide motion and touch input. + Ви можете використовувати будь-яке сумісне з Cemuhook джерело UDP сигналу для руху і сенсора. + + + + Server: + Сервер: + + + + Port: + Порт: + + + + Learn More + Дізнатися більше + + + + + Test + Тест + + + + Add Server + Додати сервер + + + + Remove Server + Видалити сервер + + + + <a href='https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Learn More</span></a> + <a href='https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Дізнатися більше</span></a> + + + + %1:%2 + %1:%2 + + + + + + + + + yuzu + yuzu + + + + Port number has invalid characters + Номер порту містить неприпустимі символи + + + + Port has to be in range 0 and 65353 + Порт повинен бути в районі від 0 до 65353 + + + + IP address is not valid + IP-адреса недійсна + + + + This UDP server already exists + Цей UDP сервер уже існує + + + + Unable to add more than 8 servers + Неможливо додати більше 8 серверів + + + + Testing + Тестування + + + + Configuring + Налаштування + + + + Test Successful + Тест успішний + + + + Successfully received data from the server. + Успішно отримано інформацію із сервера + + + + Test Failed + Тест провалено + + + + Could not receive valid data from the server.<br>Please verify that the server is set up correctly and the address and port are correct. + Не вдалося отримати дійсні дані з сервера.<br>Переконайтеся, що сервер правильно налаштований, а також перевірте адресу та порт. + + + + UDP Test or calibration configuration is in progress.<br>Please wait for them to finish. + Тест UDP або калібрація в процесі.<br>Будь ласка, зачекайте завершення. + + + + ConfigureNetwork + + + Form + Форма + + + + Network + Мережа + + + + General + Загальні + + + + Network Interface + Інтерфейс мережі + + + + None + Нічого + + + + ConfigurePerGame + + + Dialog + Діалог + + + + Info + Інформація + + + + Name + Назва + + + + Title ID + Ідентифікатор гри + + + + Filename + Ім'я файлу + + + + Format + Формат + + + + Version + Версія + + + + Size + Розмір + + + + Developer + Розробник + + + + Add-Ons + Доповнення + + + + General + Загальні + + + + System + Система + + + + CPU + ЦП + + + + Graphics + Графіка + + + + Adv. Graphics + Розш. Графіка + + + + Audio + Аудіо + + + + Properties + Властивості + + + + Use global configuration (%1) + Використовувати глобальне налаштування (%1) + + + + ConfigurePerGameAddons + + + Form + Форма + + + + Add-Ons + Доповнення + + + + Patch Name + Назва патчу + + + + Version + Версія + + + + ConfigureProfileManager + + + Form + Форма + + + + Profiles + Профілі + + + + Profile Manager + Керування профілями + + + + Current User + Поточний користувач + + + + Username + Ім'я користувача + + + + Set Image + Обрати зображення + + + + Add + Додати + + + + Rename + Перейменувати + + + + Remove + Видалити + + + + Profile management is available only when game is not running. + Керування профілями недоступне, поки запущена гра. + + + + %1 +%2 + %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) + %1 +%2 + + + + Enter Username + Введіть ім'я користувача + + + + Users + Користувачі + + + + Enter a username for the new user: + Введіть ім'я користувача для нового профілю: + + + + Enter a new username: + Введіть нове ім'я користувача: + + + + Select User Image + Оберіть зображення користувача + + + + JPEG Images (*.jpg *.jpeg) + Зображення JPEG (*.jpg *.jpeg) + + + + Error deleting image + Помилка під час видалення зображення + + + + Error occurred attempting to overwrite previous image at: %1. + Помилка під час спроби перезапису попереднього зображення в: %1. + + + + Error deleting file + Помилка під час видалення файлу + + + + Unable to delete existing file: %1. + Не вдалося видалити наявний файл: %1. + + + + Error creating user image directory + Помилка під час створення папки користувацьких зображень + + + + Unable to create directory %1 for storing user images. + Не вийшло створити папку %1 для зберігання зображень користувача. + + + + Error copying user image + Помилка під час копіювання зображення користувача + + + + Unable to copy image from %1 to %2 + Не вийшло скопіювати зображення з %1 у %2 + + + + Error resizing user image + Помилка під час зміни розміру зображення користувача + + + + Unable to resize image + Неможливо змінити розмір зображення + + + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Підтвердити видалення + + + + Name: %1 +UUID: %2 + + + + + ConfigureRingController + + + Configure Ring Controller + Налаштування контролера Ring + + + + If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly. + Якщо ви хочете використовувати цей контролер, налаштуйте гравця 1 як правий контролер, а гравця 2 як подвійний Joy-Соп перед початком гри, щоб цей контролер був виявлений правильно. + + + + Ring Sensor Parameters + Параметри сенсора Ring + + + + + Pull + Потягнути + + + + + Push + Натиснути + + + + Deadzone: 0% + Мертва зона: 0% + + + + Restore Defaults + За замовчуванням + + + + Clear + Очистити + + + + [not set] + [не задано] + + + + Invert axis + Інвертувати осі + + + + + Deadzone: %1% + Мертва зона: %1% + + + + [waiting] + [очікування] + + + + ConfigureSystem + + + Form + Форма + + + + System + Система + + + + System Settings + Налаштування системи + + + + Region: + Регіон: + + + + Auto + Авто + + + + Default + За замовчуванням + + + + CET + CET + + + + CST6CDT + CST6CDT + + + + Cuba + Куба + + + + EET + EET + + + + Egypt + Єгипет + + + + Eire + Ейре + + + + EST + EST + + + + EST5EDT + EST5EDT + + + + GB + GB + + + + GB-Eire + GB-Ейре + + + + GMT + GMT + + + + GMT+0 + GMT+0 + + + + GMT-0 + GMT-0 + + + + GMT0 + GMT0 + + + + Greenwich + Гринвіч + + + + Hongkong + Гонконг + + + + HST + HST + + + + Iceland + Ісландія + + + + Iran + Іран + + + + Israel + Ізраїль + + + + Jamaica + Ямайка + + + + + Japan + Японія + + + + Kwajalein + Кваджалейн + + + + Libya + Лівія + + + + MET + MET + + + + MST + MST + + + + MST7MDT + MST7MDT + + + + Navajo + Навахо + + + + NZ + NZ + + + + NZ-CHAT + NZ-CHAT + + + + Poland + Польща + + + + Portugal + Португалія + + + + PRC + PRC + + + + PST8PDT + PST8PDT + + + + ROC + ROC + + + + ROK + ROK + + + + Singapore + Сінгапур + + + + Turkey + Туреччина + + + + UCT + UCT + + + + Universal + Універсальний + + + + UTC + UTC + + + + W-SU + W-SU + + + + WET + WET + + + + Zulu + Зулуси + + + + USA + США + + + + Europe + Європа + + + + Australia + Австралія + + + + China + Китай + + + + Korea + Корея + + + + Taiwan + Тайвань + + + + Time Zone: + Часовий пояс: + + + + Note: this can be overridden when region setting is auto-select + Примітка: може бути перезаписано якщо регіон вибирається автоматично + + + + Japanese (日本語) + Японська (日本語) + + + + English + Англійська (English) + + + + French (français) + Французька (français) + + + + German (Deutsch) + Німецька (Deutsch) + + + + Italian (italiano) + Італійська (italiano) + + + + Spanish (español) + Іспанська (español) + + + + Chinese + Китайська + + + + Korean (한국어) + Корейська (한국어) + + + + Dutch (Nederlands) + Голландська (Nederlands) + + + + Portuguese (português) + Португальська (português) + + + + Russian (Русский) + Російська (Русский) + + + + Taiwanese + Тайванська + + + + British English + Британська Англійська + + + + Canadian French + Канадська Французька + + + + Latin American Spanish + Латиноамериканська Іспанська + + + + Simplified Chinese + Спрощена Китайська + + + + Traditional Chinese (正體中文) + Традиційна Китайська (正體中文) + + + + Brazilian Portuguese (português do Brasil) + Бразильська Португальська (português do Brasil) + + + + Custom RTC + Користувацький RTC + + + + Language + Мова + + + + RNG Seed + Сід RNG + + + + Mono + Моно + + + + Stereo + Стерео + + + + Surround + Об'ємний звук + + + + Console ID: + Ідентифікатор консолі: + + + + Sound output mode + Режим виводу звуку + + + + Regenerate + Перегенерувати + + + + System settings are available only when game is not running. + Налаштування системи доступні тільки тоді, коли гру не запущено. + + + + This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue? + Це замінить ваш поточний віртуальний Switch новим. Ваш поточний віртуальний Switch буде безповоротно втрачено. Це може мати несподівані наслідки в іграх. Може не спрацювати, якщо ви використовуєте застарілу конфігурацію збережених ігор. Продовжити? + + + + Warning + Увага + + + + Console ID: 0x%1 + Ідентифікатор консолі: 0x%1 + + + + ConfigureTas + + + TAS + TAS + + + + <html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">help page</span></a> on the yuzu website.</p></body></html> + <html><head/><body><p>Зчитує вхідні дані контролера зі скриптів у тому ж форматі, що і скрипти TAS-nx.<br/>Для більш детального пояснення зверніться до <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">сторінки допомоги</span></a> на сайті yuzu.</p></body></html> + + + + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). + Щоб перевірити, які гарячі клавіші керують відтворенням/записом, зверніться до налаштувань гарячих клавіш (Налаштування - Загальні -> Гарячі клавіші). + + + + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. + ПОПЕРЕДЖЕННЯ: Це експериментальна функція.<br/>Вона не буде ідеально відтворювати кадри сценаріїв за поточного недосконалого методу синхронізації. + + + + Settings + Налаштування + + + + Enable TAS features + Увімкнути функції TAS + + + + Loop script + Зациклити скрипт + + + + Pause execution during loads + Призупинити виконання під час завантаження + + + + Script Directory + Папка для скриптів + + + + Path + Шлях + + + + ... + ... + + + + ConfigureTasDialog + + + TAS Configuration + Налаштування TAS + + + + Select TAS Load Directory... + Обрати папку завантаження TAS... + + + + ConfigureTouchFromButton + + + Configure Touchscreen Mappings + Налаштування відображення сенсорного екрана + + + + Mapping: + Прив'язки: + + + + New + Новий + + + + Delete + Видалити + + + + Rename + Перейменувати + + + + Click the bottom area to add a point, then press a button to bind. +Drag points to change position, or double-click table cells to edit values. + Натисніть на нижній області, щоб додати точку, після чого натисніть кнопку для прив'язки. +Перетягніть точки, щоб змінити позицію, або натисніть двічі на комірки таблиці для зміни значень. + + + + Delete Point + Видалити точку + + + + Button + Кнопка + + + + X + X axis + X + + + + Y + Y axis + Y + + + + New Profile + Новий профіль + + + + Enter the name for the new profile. + Введіть ім'я вашого нового профілю. + + + + Delete Profile + Видалити профіль + + + + Delete profile %1? + Видалити профіль %1? + + + + Rename Profile + Перейменувати профіль + + + + New name: + Нова назва: + + + + [press key] + [натисніть клавішу] + + + + ConfigureTouchscreenAdvanced + + + Configure Touchscreen + Налаштування сенсорного екрана + + + + Warning: The settings in this page affect the inner workings of yuzu's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing. + Увага: Налаштування на цій сторінці впливають на внутрішню роботу емульованого сенсорного екрана yuzu. Їх зміна може призвести до небажаної поведінки, як часткова або повна непрацездатність сенсорного екрана. Використовуйте цю сторінку лише якщо ви знаєте, що робите. + + + + Touch Parameters + Параметри сенсора + + + + Touch Diameter Y + Діаметр сенсора Y + + + + Touch Diameter X + Діаметр сенсора X + + + + Rotational Angle + Кут повороту + + + + Restore Defaults + За замовчуванням + + + + ConfigureUI + + + + + None + Нічого + + + + Small (32x32) + Маленький (32х32) + + + + Standard (64x64) + Стандартний (64х64) + + + + Large (128x128) + Великий (128х128) + + + + Full Size (256x256) + Повнорозмірний (256х256) + + + + Small (24x24) + Маленький (24х24) + + + + Standard (48x48) + Стандартний (48х48) + + + + Large (72x72) + Великий (72х72) + + + + Filename + Ім'я файлу + + + + Filetype + Тип файлу + + + + Title ID + Ідентифікатор гри + + + + Title Name + Назва гри + + + + ConfigureUi + + + Form + Форма + + + + UI + Інтерфейс + + + + General + Загальні + + + + Note: Changing language will apply your configuration. + Примітка: Зміна мови призведе до застосування налаштувань. + + + + Interface language: + Мова інтерфейсу: + + + + Theme: + Тема: + + + + Game List + Список ігор + + + + Show Compatibility List + Показати список сумісності + + + + Show Add-Ons Column + Показувати стовпець доповнень + + + + Show Size Column + + + + + Show File Types Column + + + + + Game Icon Size: + Розмір іконки гри: + + + + Folder Icon Size: + Розмір іконки папки: + + + + Row 1 Text: + Текст 1-го рядку: + + + + Row 2 Text: + Текст 2-го рядку: + + + + Screenshots + Знімки екрану + + + + Ask Where To Save Screenshots (Windows Only) + Запитувати куди зберігати знімки екрану (Тільки для Windows) + + + + Screenshots Path: + Папка для знімків екрану: + + + + ... + ... + + + + Select Screenshots Path... + Виберіть папку для знімків екрану... + + + + <System> + <System> + + + + ConfigureVibration + + + Configure Vibration + Налаштування вібрації + + + + Press any controller button to vibrate the controller. + Натисніть будь-яку кнопку контролера, щоб викликати вібрацію. + + + + Vibration + Вібрація + + + + Player 1 + Гравець 1 + + + + + + + + + + + % + % + + + + Player 2 + Гравець 2 + + + + Player 3 + Гравець 3 + + + + Player 4 + Гравець 4 + + + + Player 5 + Гравець 5 + + + + Player 6 + Гравець 6 + + + + Player 7 + Гравець 7 + + + + Player 8 + Гравець 8 + + + + Settings + Налаштування + + + + Enable Accurate Vibration + Увімкнути точну вібрацію + + + + ConfigureWeb + + + Form + Форма + + + + Web + Мережа + + + + yuzu Web Service + Веб-сервіс yuzu + + + + By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information. + Надаючи своє ім'я користувача і токен, ви погоджуєтеся дозволити yuzu збирати додаткові дані про використання, які можуть включати інформацію, що ідентифікує користувача. + + + + + Verify + Підтвердити + + + + Sign up + Реєстрація + + + + Token: + Токен: + + + + Username: + Ім'я користувача: + + + + What is my token? + Що таке токен і де його знайти? + + + + Web Service configuration can only be changed when a public room isn't being hosted. + Налаштування веб-служби можуть бути змінені тільки в тому випадку, коли не хоститься публічна кімната. + + + + Telemetry + Телеметрія + + + + Share anonymous usage data with the yuzu team + Ділитися анонімною інформацією про використання з командою yuzu + + + + Learn more + Дізнатися більше + + + + Telemetry ID: + Ідентифікатор телеметрії: + + + + Regenerate + Перегенерувати + + + + Discord Presence + Discord Presence + + + + Show Current Game in your Discord Status + Показувати поточну гру у вашому статусі Discord + + + + <a href='https://yuzu-emu.org/help/feature/telemetry/'><span style="text-decoration: underline; color:#039be5;">Learn more</span></a> + <a href='https://yuzu-emu.org/help/feature/telemetry/'><span style="text-decoration: underline; color:#039be5;">Дізнатися більше</span></a> + + + + <a href='https://profile.yuzu-emu.org/'><span style="text-decoration: underline; color:#039be5;">Sign up</span></a> + <a href='https://profile.yuzu-emu.org/'><span style="text-decoration: underline; color:#039be5;">Реєстрація</span></a> + + + + <a href='https://yuzu-emu.org/wiki/yuzu-web-service/'><span style="text-decoration: underline; color:#039be5;">What is my token?</span></a> + <a href='https://yuzu-emu.org/wiki/yuzu-web-service/'><span style="text-decoration: underline; color:#039be5;">Що таке токен і де його знайти?</span></a> + + + + + Telemetry ID: 0x%1 + Ідентифікатор телеметрії: 0x%1 + + + + + Unspecified + Відсутній + + + + Token not verified + Токен не підтверджено + + + + Token was not verified. The change to your token has not been saved. + Токен не було підтверджено. Зміну вашого токена не було збережено. + + + + Unverified, please click Verify before saving configuration + Tooltip + Не підтверджено, будь ласка, натисніть кнопку Підтвердити, перш ніж зберігати конфігурацію. + + + + + Verifying... + Підтверждення... + + + + Verified + Tooltip + Підтверджено + + + + Verification failed + Tooltip + Підтверждення не було успішним + + + + Verification failed + Підтверждення не було успішним + + + + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. + Підтверждення не було успішним. Переконайтеся, що ви правильно ввели свій токен і що ваше інтернет-з'єднання працює. + + + + ControllerDialog + + + Controller P1 + Контролер P1 + + + + &Controller P1 + [&C] Контролер P1 + + + + DirectConnect + + + Direct Connect + Пряме підключення + + + + IP Address + IP-адреса + + + + IP + IP + + + + <html><head/><body><p>IPv4 address of the host</p></body></html> + <html><head/><body><p>IPv4 адреса хоста</p></body></html> + + + + Port + Порт + + + + <html><head/><body><p>Port number the host is listening on</p></body></html> + <html><head/><body><p>Номер порту, який прослуховується хостом</p></body></html> + + + + Nickname + Псевдонім + + + + Password + Пароль + + + + Connect + Підключитися + + + + DirectConnectWindow + + + Connecting + Підключення + + + + Connect + Підключитися + + + + GMainWindow + + + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Анонімні дані збираються для того,</a> щоб допомогти поліпшити роботу yuzu. <br/><br/>Хотіли б ви ділитися даними про використання з нами? + + + + Telemetry + Телеметрія + + + + Broken Vulkan Installation Detected + Виявлено пошкоджену інсталяцію Vulkan + + + + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. + Не вдалося виконати ініціалізацію Vulkan під час завантаження.<br><br>Натисніть <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>тут для отримання інструкцій щодо усунення проблеми</a>. + + + + Loading Web Applet... + Завантаження веб-аплета... + + + + + Disable Web Applet + Вимкнути веб-аплет + + + + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? +(This can be re-enabled in the Debug settings.) + Вимкнення веб-апплета може призвести до несподіваної поведінки, і його слід вимикати лише заради Super Mario 3D All-Stars. Ви впевнені, що хочете вимкнути веб-апплет? +(Його можна знову ввімкнути в налаштуваннях налагодження.) + + + + The amount of shaders currently being built + Кількість створюваних шейдерів на цей момент + + + + The current selected resolution scaling multiplier. + Поточний обраний множник масштабування роздільної здатності. + + + + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. + Поточна швидкість емуляції. Значення вище або нижче 100% вказують на те, що емуляція йде швидше або повільніше, ніж на Switch. + + + + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. + Кількість кадрів на секунду в цей момент. Значення буде змінюватися між іграми та сценами. + + + + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. + Час, який потрібен для емуляції 1 кадру Switch, не беручи до уваги обмеження FPS або вертикальну синхронізацію. Для емуляції в повній швидкості значення має бути не більше 16,67 мс. + + + + VULKAN + VULKAN + + + + OPENGL + OPENGL + + + + &Clear Recent Files + [&C] Очистити нещодавні файли + + + + &Continue + [&C] Продовжити + + + + &Pause + [&P] Пауза + + + + yuzu is running a game + TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping + В yuzu запущено гру + + + + Warning Outdated Game Format + Попередження застарілий формат гри + + + + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. + Для цієї гри ви використовуєте розархівований формат ROM'а, який є застарілим і був замінений іншими, такими як NCA, NAX, XCI або NSP. У розархівованих каталогах ROM'а відсутні іконки, метадані та підтримка оновлень. <br><br>Для отримання інформації про різні формати Switch, підтримувані yuzu, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>перегляньте нашу вікі</a>. Це повідомлення більше не буде відображатися. + + + + + Error while loading ROM! + Помилка під час завантаження ROM! + + + + The ROM format is not supported. + Формат ROM'а не підтримується. + + + + An error occurred initializing the video core. + Сталася помилка під час ініціалізації відеоядра. + + + + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. + yuzu зіткнувся з помилкою під час запуску відеоядра. Зазвичай це спричинено застарілими драйверами ГП, включно з інтегрованими. Перевірте журнал для отримання більш детальної інформації. Додаткову інформацію про доступ до журналу дивіться на наступній сторінці: <a href='https://yuzu-emu.org/help/reference/log-files/'>Як завантажити файл журналу</a>. + + + + Error while loading ROM! %1 + %1 signifies a numeric error code. + Помилка під час завантаження ROM'а! %1 + + + + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. + %1 signifies an error string. + %1<br>Будь ласка, дотримуйтесь <a href='https://yuzu-emu.org/help/quickstart/'>короткого керівництва користувача yuzu</a> щоб пере-дампити ваші файли<br>Ви можете звернутися до вікі yuzu</a> або Discord yuzu</a> для допомоги + + + + An unknown error occurred. Please see the log for more details. + Сталася невідома помилка. Будь ласка, перевірте журнал для подробиць. + + + + (64-bit) + (64-бітний) + + + + (32-bit) + (32-бітний) + + + + %1 %2 + %1 is the title name. %2 indicates if the title is 64-bit or 32-bit + %1 %2 + + + + Save Data + Збереження + + + + Mod Data + Дані модів + + + + Error Opening %1 Folder + Помилка під час відкриття папки %1 + + + + + Folder does not exist! + Папка не існує! + + + + Error Opening Transferable Shader Cache + Помилка під час відкриття переносного кешу шейдерів + + + + Failed to create the shader cache directory for this title. + Не вдалося створити папку кешу шейдерів для цієї гри. + + + + Error Removing Contents + + + + + Error Removing Update + + + + + Error Removing DLC + + + + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + + Remove Entry + Видалити запис + + + + + + + + + Successfully Removed + Успішно видалено + + + + Successfully removed the installed base game. + Встановлену гру успішно видалено. + + + + The base game is not installed in the NAND and cannot be removed. + Гру не встановлено в NAND і не може буде видалено. + + + + Successfully removed the installed update. + Встановлене оновлення успішно видалено. + + + + There is no update installed for this title. + Для цієї гри не було встановлено оновлення. + + + + There are no DLC installed for this title. + Для цієї гри не було встановлено DLC. + + + + Successfully removed %1 installed DLC. + Встановлений DLC %1 було успішно видалено + + + + Delete OpenGL Transferable Shader Cache? + Видалити переносний кеш шейдерів OpenGL? + + + + Delete Vulkan Transferable Shader Cache? + Видалити переносний кеш шейдерів Vulakn? + + + + Delete All Transferable Shader Caches? + Видалити весь переносний кеш шейдерів? + + + + Remove Custom Game Configuration? + Видалити користувацьке налаштування гри? + + + + Remove File + Видалити файл + + + + + Error Removing Transferable Shader Cache + Помилка під час видалення переносного кешу шейдерів + + + + + A shader cache for this title does not exist. + Кеш шейдерів для цієї гри не існує. + + + + Successfully removed the transferable shader cache. + Переносний кеш шейдерів успішно видалено. + + + + Failed to remove the transferable shader cache. + Не вдалося видалити переносний кеш шейдерів. + + + + + Error Removing Transferable Shader Caches + Помилка під час видалення переносного кешу шейдерів + + + + Successfully removed the transferable shader caches. + Переносний кеш шейдерів успішно видалено. + + + + Failed to remove the transferable shader cache directory. + Помилка під час видалення папки переносного кешу шейдерів. + + + + + Error Removing Custom Configuration + Помилка під час видалення користувацького налаштування + + + + A custom configuration for this title does not exist. + Користувацьких налаштувань для цієї гри не існує. + + + + Successfully removed the custom game configuration. + Користувацьке налаштування гри успішно видалено. + + + + Failed to remove the custom game configuration. + Не вдалося видалити користувацьке налаштування гри. + + + + + RomFS Extraction Failed! + Не вдалося вилучити RomFS! + + + + There was an error copying the RomFS files or the user cancelled the operation. + Сталася помилка під час копіювання файлів RomFS або користувач скасував операцію. + + + + Full + Повний + + + + Skeleton + Скелет + + + + Select RomFS Dump Mode + Виберіть режим дампа RomFS + + + + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. + Будь ласка, виберіть, як ви хочете виконати дамп RomFS <br>Повний скопіює всі файли в нову папку, тоді як <br>скелет створить лише структуру папок. + + + + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root + В %1 недостатньо вільного місця для вилучення RomFS. Будь ласка, звільніть місце або виберіть іншу папку для дампа в Емуляція > Налаштування > Система > Файлова система > Корінь дампа + + + + Extracting RomFS... + Вилучення RomFS... + + + + + Cancel + Скасувати + + + + RomFS Extraction Succeeded! + Вилучення RomFS пройшло успішно! + + + + The operation completed successfully. + Операція завершилася успішно. + + + + Error Opening %1 + Помилка відкриття %1 + + + + Select Directory + Обрати папку + + + + Properties + Властивості + + + + The game properties could not be loaded. + Не вдалося завантажити властивості гри. + + + + Switch Executable (%1);;All Files (*.*) + %1 is an identifier for the Switch executable file extensions. + Виконуваний файл Switch (%1);;Усі файли (*.*) + + + + Load File + Завантажити файл + + + + Open Extracted ROM Directory + Відкрити папку вилученого ROM'а + + + + Invalid Directory Selected + Вибрано неприпустиму папку + + + + The directory you have selected does not contain a 'main' file. + Папка, яку ви вибрали, не містить файлу 'main'. + + + + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) + Встановлюваний файл Switch (*.nca, *.nsp, *.xci);;Архів контенту Nintendo (*.nca);;Пакет подачі Nintendo (*.nsp);;Образ картриджа NX (*.xci) + + + + Install Files + Встановити файли + + + + %n file(s) remaining + Залишився %n файлЗалишилося %n файл(ів)Залишилося %n файл(ів)Залишилося %n файл(ів) + + + + Installing file "%1"... + Встановлення файлу "%1"... + + + + + Install Results + Результати встановлення + + + + To avoid possible conflicts, we discourage users from installing base games to the NAND. +Please, only use this feature to install updates and DLC. + Щоб уникнути можливих конфліктів, ми не рекомендуємо користувачам встановлювати ігри в NAND. +Будь ласка, використовуйте цю функцію тільки для встановлення оновлень і завантажуваного контенту. + + + + %n file(s) were newly installed + + %n файл було нещодавно встановлено +%n файл(ів) було нещодавно встановлено +%n файл(ів) було нещодавно встановлено +%n файл(ів) було нещодавно встановлено + + + + + %n file(s) were overwritten + + %n файл було перезаписано +%n файл(ів) було перезаписано +%n файл(ів) було перезаписано +%n файл(ів) було перезаписано + + + + + %n file(s) failed to install + + %n файл не вдалося встановити +%n файл(ів) не вдалося встановити +%n файл(ів) не вдалося встановити +%n файл(ів) не вдалося встановити + + + + + System Application + Системний додаток + + + + System Archive + Системний архів + + + + System Application Update + Оновлення системного додатку + + + + Firmware Package (Type A) + Пакет прошивки (Тип А) + + + + Firmware Package (Type B) + Пакет прошивки (Тип Б) + + + + Game + Гра + + + + Game Update + Оновлення гри + + + + Game DLC + DLC до гри + + + + Delta Title + Дельта-титул + + + + Select NCA Install Type... + Виберіть тип установки NCA... + + + + Please select the type of title you would like to install this NCA as: +(In most instances, the default 'Game' is fine.) + Будь ласка, виберіть тип додатку, який ви хочете встановити для цього NCA: +(У більшості випадків, підходить стандартний вибір "Гра".) + + + + Failed to Install + Помилка встановлення + + + + The title type you selected for the NCA is invalid. + Тип додатку, який ви вибрали для NCA, недійсний. + + + + File not found + Файл не знайдено + + + + File "%1" not found + Файл "%1" не знайдено + + + + OK + ОК + + + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + + Missing yuzu Account + Відсутній обліковий запис yuzu + + + + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. + Щоб надіслати звіт про сумісність гри, необхідно прив'язати свій обліковий запис yuzu. <br><br/>Щоб прив'язати свій обліковий запис yuzu, перейдіть у розділ Емуляція &gt; Параметри &gt; Мережа. + + + + Error opening URL + Помилка під час відкриття URL + + + + Unable to open the URL "%1". + Не вдалося відкрити URL: "%1". + + + + TAS Recording + Запис TAS + + + + Overwrite file of player 1? + Перезаписати файл гравця 1? + + + + Invalid config detected + Виявлено неприпустиму конфігурацію + + + + Handheld controller can't be used on docked mode. Pro controller will be selected. + Портативний контролер не може бути використаний у режимі док-станції. Буде обрано контролер Pro. + + + + + Amiibo + Amiibo + + + + + The current amiibo has been removed + Поточний amiibo було прибрано + + + + Error + Помилка + + + + + The current game is not looking for amiibos + Поточна гра не шукає amiibo + + + + Amiibo File (%1);; All Files (*.*) + Файл Amiibo (%1);; Всі Файли (*.*) + + + + Load Amiibo + Завантажити Amiibo + + + + Error loading Amiibo data + Помилка під час завантаження даних Amiibo + + + + The selected file is not a valid amiibo + Обраний файл не є допустимим amiibo + + + + The selected file is already on use + Обраний файл уже використовується + + + + An unknown error occurred + Виникла невідома помилка + + + + Capture Screenshot + Зробити знімок екрану + + + + PNG Image (*.png) + Зображення PNG (*.png) + + + + TAS state: Running %1/%2 + Стан TAS: Виконується %1/%2 + + + + TAS state: Recording %1 + Стан TAS: Записується %1 + + + + TAS state: Idle %1/%2 + Стан TAS: Простий %1/%2 + + + + TAS State: Invalid + Стан TAS: Неприпустимий + + + + &Stop Running + [&S] Зупинка + + + + &Start + [&S] Почати + + + + Stop R&ecording + [&E] Закінчити запис + + + + R&ecord + [&E] Запис + + + + Building: %n shader(s) + Побудова: %n шейдерПобудова: %n шейдер(ів)Побудова: %n шейдер(ів)Побудова: %n шейдер(ів) + + + + Scale: %1x + %1 is the resolution scaling factor + Масштаб: %1x + + + + Speed: %1% / %2% + Швидкість: %1% / %2% + + + + Speed: %1% + Швидкість: %1% + + + + Game: %1 FPS (Unlocked) + Гра: %1 FPS (Необмежено) + + + + Game: %1 FPS + Гра: %1 FPS + + + + Frame: %1 ms + Кадр: %1 мс + + + + GPU NORMAL + ГП НОРМАЛЬНО + + + + GPU HIGH + ГП ВИСОКО + + + + GPU EXTREME + ГП ЕКСТРИМ + + + + GPU ERROR + ГП ПОМИЛКА + + + + DOCKED + В ДОК-СТАНЦІЇ + + + + HANDHELD + ПОРТАТИВНИЙ + + + + NEAREST + НАЙБЛИЖЧІЙ + + + + + BILINEAR + БІЛІНІЙНИЙ + + + + BICUBIC + БІКУБІЧНИЙ + + + + GAUSSIAN + ГАУС + + + + SCALEFORCE + SCALEFORCE + + + + FSR + FSR + + + + + NO AA + БЕЗ ЗГЛАДЖУВАННЯ + + + + FXAA + FXAA + + + + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. + Гра, яку ви намагаєтеся завантажити, вимагає, щоб додаткові файли були здамплені з вашого Switch перед початком гри. <br/><br/>Для отримання додаткової інформації про дамп цих файлів див. наступну вікі: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Дамп системних архівів і загальних шрифтів з консолі</a>. <br/><br/>Хочете повернутися до списку ігор? Продовження емуляції може призвести до збоїв, пошкодження збережених даних або інших помилок. + + + + yuzu was unable to locate a Switch system archive. %1 + yuzu не вдалося знайти системний архів Switch. %1 + + + + yuzu was unable to locate a Switch system archive: %1. %2 + yuzu не вдалося знайти системний архів Switch: %1. %2 + + + + System Archive Not Found + Системний архів не знайдено + + + + System Archive Missing + Відсутній системний архів + + + + yuzu was unable to locate the Switch shared fonts. %1 + yuzu не вдалося знайти загальні шрифти Switch. %1 + + + + Shared Fonts Not Found + Загальні шрифти не знайдено + + + + Shared Font Missing + Загальні шрифти відсутні + + + + Fatal Error + Фатальна помилка + + + + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. + yuzu зіткнувся з фатальною помилкою, перевірте журнал для отримання більш детальної інформації. Для отримання додаткової інформації про доступ до журналу відкрийте наступну сторінку: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Як завантажити файл журналу</a>.<br/><br/>Ви хочете повернутися до списку ігор? Продовження емуляції може призвести до збоїв, пошкодження збережень або інших помилок. + + + + Fatal Error encountered + Сталася фатальна помилка + + + + Confirm Key Rederivation + Підтвердіть перерахунок ключа + + + + You are about to force rederive all of your keys. +If you do not know what this means or what you are doing, +this is a potentially destructive action. +Please make sure this is what you want +and optionally make backups. + +This will delete your autogenerated key files and re-run the key derivation module. + Ви збираєтеся примусово перерахувати всі ваші ключі. +Якщо ви не знаєте, що це означає або що ви робите, +це потенційно руйнівна дія. +Будь ласка, переконайтеся, що це те, що ви хочете +і, по бажанню, зробіть резервні копії. + +Це видалить ваші автоматично згенеровані файли ключів і повторно запустить модуль розрахунку ключів. + + + + Missing fuses + Відсутні запобіжники + + + + - Missing BOOT0 + - Відсутній BOOT0 + + + + - Missing BCPKG2-1-Normal-Main + - Відсутній BCPKG2-1-Normal-Main + + + + - Missing PRODINFO + - Відсутній PRODINFO + + + + Derivation Components Missing + Компоненти розрахунку відсутні + + + + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> + Ключі шифрування відсутні.<br>Будь ласка, дотримуйтесь <a href='https://yuzu-emu.org/help/quickstart/'>короткого керівництва користувача yuzu</a>, щоб отримати всі ваші ключі, прошивку та ігри<br><br><small>(%1)</small> + + + + Deriving keys... +This may take up to a minute depending +on your system's performance. + Отримання ключів... +Це може зайняти до хвилини залежно від +від продуктивності вашої системи. + + + + Deriving Keys + Отримання ключів + + + + Select RomFS Dump Target + Оберіть ціль для дампа RomFS + + + + Please select which RomFS you would like to dump. + Будь ласка, виберіть, який RomFS ви хочете здампити. + + + + Are you sure you want to close yuzu? + Ви впевнені, що хочете закрити yuzu? + + + + + + yuzu + yuzu + + + + Are you sure you want to stop the emulation? Any unsaved progress will be lost. + Ви впевнені, що хочете зупинити емуляцію? Будь-який незбережений прогрес буде втрачено. + + + + The currently running application has requested yuzu to not exit. + +Would you like to bypass this and exit anyway? + Запущений на даний момент додаток просить yuzu не завершувати роботу. + +Чи хочете ви обійти це і вийти в будь-якому випадку? + + + + GRenderWindow + + + OpenGL not available! + OpenGL недоступний! + + + + yuzu has not been compiled with OpenGL support. + yuzu не було зібрано з підтримкою OpenGL. + + + + + Error while initializing OpenGL! + Помилка під час ініціалізації OpenGL! + + + + Your GPU may not support OpenGL, or you do not have the latest graphics driver. + Ваш ГП може не підтримувати OpenGL, або у вас встановлено застарілий графічний драйвер. + + + + Error while initializing OpenGL 4.6! + Помилка під час ініціалізації OpenGL 4.6! + + + + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 + Ваш ГП може не підтримувати OpenGL 4.6, або у вас встановлено застарілий графічний драйвер.<br><br>Рендерер GL:<br>%1 + + + + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 + Ваш ГП може не підтримувати одне або кілька необхідних розширень OpenGL. Будь ласка, переконайтеся в тому, що у вас встановлено останній графічний драйвер.<br><br>Рендерер GL:<br>%1<br><br>Розширення, що не підтримуються:<br>%2 + + + + GameList + + + Favorite + Улюблені + + + + Start Game + Запустити гру + + + + Start Game without Custom Configuration + Запустити гру без користувацького налаштування + + + + Open Save Data Location + Відкрити папку для збережень + + + + Open Mod Data Location + Відкрити папку для модів + + + + Open Transferable Pipeline Cache + Відкрити переносний кеш конвеєра + + + + Remove + Видалити + + + + Remove Installed Update + Видалити встановлене оновлення + + + + Remove All Installed DLC + Видалити усі DLC + + + + Remove Custom Configuration + Видалити користувацьке налаштування + + + + Remove OpenGL Pipeline Cache + Видалити кеш конвеєра OpenGL + + + + Remove Vulkan Pipeline Cache + Видалити кеш конвеєра Vulkan + + + + Remove All Pipeline Caches + Видалити весь кеш конвеєра + + + + Remove All Installed Contents + Видалити весь встановлений вміст + + + + + Dump RomFS + Дамп RomFS + + + + Dump RomFS to SDMC + Здампити RomFS у SDMC + + + + Copy Title ID to Clipboard + Скопіювати ідентифікатор додатку в буфер обміну + + + + Navigate to GameDB entry + Перейти до сторінки GameDB + + + + Properties + Властивості + + + + Scan Subfolders + Сканувати підпапки + + + + Remove Game Directory + Видалити директорію гри + + + + ▲ Move Up + ▲ Перемістити вверх + + + + ▼ Move Down + ▼ Перемістити вниз + + + + Open Directory Location + Відкрити розташування папки + + + + Clear + Очистити + + + + Name + Назва + + + + Compatibility + Сумісність + + + + Add-ons + Доповнення + + + + File type + Тип файлу + + + + Size + Розмір + + + + GameListItemCompat + + + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + + Perfect + Ідеально + + + + Game can be played without issues. + + + + + Playable + + + + + Game functions with minor graphical or audio glitches and is playable from start to finish. + + + + + Intro/Menu + Вступ/Меню + + + + Game loads, but is unable to progress past the Start Screen. + + + + + Won't Boot + Не запускається + + + + The game crashes when attempting to startup. + Гра вилітає під час запуску. + + + + Not Tested + Не перевірено + + + + The game has not yet been tested. + Гру ще не перевіряли на сумісність. + + + + GameListPlaceholder + + + Double-click to add a new folder to the game list + Натисніть двічі, щоб додати нову папку до списку ігор + + + + GameListSearchField + + + %1 of %n result(s) + %1 із %n результат(ів)%1 із %n результат(ів)%1 із %n результат(ів)%1 із %n результат(ів) + + + + Filter: + Пошук: + + + + Enter pattern to filter + Введіть текст для пошуку + + + + HostRoom + + + Create Room + Створити кімнату + + + + Room Name + Назва кімнати + + + + Preferred Game + Переважна гра + + + + Max Players + Максимальна кількість гравців + + + + Username + Ім'я користувача + + + + (Leave blank for open game) + (Залиште порожнім для відкритої гри) + + + + Password + Пароль + + + + Port + Порт + + + + Room Description + Опис кімнати + + + + Load Previous Ban List + Завантажити попередній список заблокованих + + + + Public + Публічна + + + + Unlisted + Прихована + + + + Host Room + Створити кімнату + + + + HostRoomWindow + + + Error + Помилка + + + + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. +Debug Message: + Не вдалося оголосити кімнату в публічному фойє. Щоб хостити публічну кімнату, у вас має бути діючий обліковий запис yuzu, налаштований в Емуляція -> Налаштування -> Мережа. Якщо ви не хочете оголошувати кімнату в публічному лобі, виберіть замість цього прихований тип. +Повідомлення налагодження: + + + + Hotkeys + + + Audio Mute/Unmute + Увімкнення/вимкнення звуку + + + + + + + + + + + + + + + + + + + + + + + + + Main Window + Основне вікно + + + + Audio Volume Down + Зменшити гучність звуку + + + + Audio Volume Up + Підвищити гучність звуку + + + + Capture Screenshot + Зробити знімок екрану + + + + Change Adapting Filter + Змінити адаптуючий фільтр + + + + Change Docked Mode + Змінити режим консолі + + + + Change GPU Accuracy + Змінити точність ГП + + + + Continue/Pause Emulation + Продовження/Пауза емуляції + + + + Exit Fullscreen + Вийти з повноекранного режиму + + + + Exit yuzu + Вийти з yuzu + + + + Fullscreen + Повний екран + + + + Load File + Завантажити файл + + + + Load/Remove Amiibo + Завантажити/видалити Amiibo + + + + Restart Emulation + Перезапустити емуляцію + + + + Stop Emulation + Зупинити емуляцію + + + + TAS Record + Запис TAS + + + + TAS Reset + Скидання TAS + + + + TAS Start/Stop + Старт/Стоп TAS + + + + Toggle Filter Bar + Переключити панель пошуку + + + + Toggle Framerate Limit + Переключити обмеження частоти кадрів + + + + Toggle Mouse Panning + Переключити панорамування миші + + + + Toggle Status Bar + Переключити панель стану + + + + InstallDialog + + + Please confirm these are the files you wish to install. + Будь ласка, переконайтеся, що це ті файли, які ви хочете встановити. + + + + Installing an Update or DLC will overwrite the previously installed one. + Встановлення оновлення або завантажуваного контенту перезапише раніше встановлене. + + + + Install + Встановити + + + + Install Files to NAND + Встановити файли в NAND + + + + LimitableInputDialog + + + The text can't contain any of the following characters: +%1 + У тексті неприпустимі такі символи: +%1 + + + + LoadingScreen + + + Loading Shaders 387 / 1628 + Завантаження шейдерів 387 / 1628 + + + + Loading Shaders %v out of %m + Завантаження шейдерів %v із %m + + + + Estimated Time 5m 4s + Залишилося приблизно 5м 4с + + + + Loading... + Завантаження... + + + + Loading Shaders %1 / %2 + Завантаження шейдерів %1 / %2 + + + + Launching... + Запуск... + + + + Estimated Time %1 + Залишилося приблизно %1 + + + + Lobby + + + Public Room Browser + Браузер публічних кімнат + + + + + Nickname + Псевдонім + + + + Filters + Фільтри + + + + Search + Пошук + + + + Games I Own + Ігри, якими я володію + + + + Hide Full Rooms + Приховати повні кімнати + + + + Refresh Lobby + Оновити фойє + + + + Password Required to Join + Для входу необхідний пароль + + + + Password: + Пароль: + + + + Players + Гравці + + + + Room Name + Назва кімнати + + + + Preferred Game + Переважна гра + + + + Host + Хост + + + + Refreshing + Оновлення + + + + Refresh List + Оновити список + + + + MainWindow + + + yuzu + yuzu + + + + &File + [&F] Файл + + + + &Recent Files + [&R] Нещодавні файли + + + + &Emulation + [&E] Емуляція + + + + &View + [&V] Вигляд + + + + &Reset Window Size + [&R] Скинути розмір вікна + + + + &Debugging + [&D] Налагодження + + + + Reset Window Size to &720p + Скинути розмір вікна до &720p + + + + Reset Window Size to 720p + Скинути розмір вікна до 720p + + + + Reset Window Size to &900p + Скинути розмір вікна до &900p + + + + Reset Window Size to 900p + Скинути розмір вікна до 900p + + + + Reset Window Size to &1080p + Скинути розмір вікна до &1080p + + + + Reset Window Size to 1080p + Скинути розмір вікна до 1080p + + + + &Multiplayer + [&M] Мультиплеєр + + + + &Tools + [&T] Інструменти + + + + &TAS + [&T] TAS + + + + &Help + [&H] Допомога + + + + &Install Files to NAND... + [&I] Встановити файли в NAND... + + + + L&oad File... + [&O] Завантажити файл... + + + + Load &Folder... + [&F] Завантажити папку... + + + + E&xit + [&X] Вихід + + + + &Pause + [&P] Пауза + + + + &Stop + [&S] Стоп + + + + &Reinitialize keys... + [&R] Переініціалізувати ключі... + + + + &About yuzu + [&A] Про yuzu + + + + Single &Window Mode + [&W] Режим одного вікна + + + + Con&figure... + [&F] Налаштування... + + + + Display D&ock Widget Headers + [&O] Відображати заголовки віджетів дока + + + + Show &Filter Bar + [&F] Показати панель пошуку + + + + Show &Status Bar + [&S] Показати панель статусу + + + + Show Status Bar + Показати панель статусу + + + + &Browse Public Game Lobby + [&B] Переглянути публічні ігрові фойє + + + + &Create Room + [&C] Створити кімнату + + + + &Leave Room + [&L] Залишити кімнату + + + + &Direct Connect to Room + [&D] Пряме під'єднання до кімнати + + + + &Show Current Room + [&S] Показати поточну кімнату + + + + F&ullscreen + [&U] Повноекранний + + + + &Restart + [&R] Перезапустити + + + + Load/Remove &Amiibo... + [&A] Завантажити/Видалити Amiibo... + + + + &Report Compatibility + [&R] Повідомити про сумісність + + + + Open &Mods Page + [&M] Відкрити сторінку модів + + + + Open &Quickstart Guide + [&Q] Відкрити посібник користувача + + + + &FAQ + [&F] ЧАП + + + + Open &yuzu Folder + [&Y] Відкрити папку yuzu + + + + &Capture Screenshot + [&C] Зробити знімок екрану + + + + &Configure TAS... + [&C] Налаштування TAS... + + + + Configure C&urrent Game... + [&U] Налаштувати поточну гру... + + + + &Start + [&S] Почати + + + + &Reset + [&S] Скинути + + + + R&ecord + [&E] Запис + + + + MicroProfileDialog + + + &MicroProfile + [&M] MicroProfile + + + + ModerationDialog + + + Moderation + Модерація + + + + Ban List + Список заблокованих + + + + + Refreshing + Оновлення + + + + Unban + Розблокувати + + + + Subject + Суб'єкт + + + + Type + Тип + + + + Forum Username + Ім'я користувача на форумі + + + + IP Address + IP-адреса + + + + Refresh + Оновити + + + + MultiplayerState + + + Current connection status + Поточний стан з'єднання + + + + Not Connected. Click here to find a room! + Не з'єднано. Натисніть тут, щоб знайти кімнату! + + + + Not Connected + Не з'єднано + + + + Connected + З'єднано + + + + New Messages Received + Отримано нові повідомлення + + + + Error + Помилка + + + + Failed to update the room information. Please check your Internet connection and try hosting the room again. +Debug Message: + Не вдалося оновити інформацію про кімнату. Будь ласка, перевірте підключення до Інтернету та спробуйте знову зайти в кімнату. +Повідомлення налагодження: + + + + NetworkMessage + + + Username is not valid. Must be 4 to 20 alphanumeric characters. + Ім'я користувача неприпустиме. Має бути від 4 до 20 буквено-цифрових символів. + + + + Room name is not valid. Must be 4 to 20 alphanumeric characters. + Назва кімнати неприпустима. Має бути від 4 до 20 буквено-цифрових символів. + + + + Username is already in use or not valid. Please choose another. + Ім'я користувача вже використовується або недійсне. Будь ласка, виберіть інше. + + + + IP is not a valid IPv4 address. + IP-адреса не є дійсною адресою IPv4. + + + + Port must be a number between 0 to 65535. + Порт повинен бути числом від 0 до 65535. + + + + You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list. + Ви повинні вибрати бажану гру, щоб хостити кімнату. Якщо у вашому списку ігор ще немає жодної гри, додайте папку з грою, натиснувши на значок плюса у списку ігор. + + + + Unable to find an internet connection. Check your internet settings. + Неможливо знайти підключення до Інтернету. Перевірте налаштування інтернету. + + + + Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded. + Неможливо підключитися до хоста. Перевірте правильність налаштувань підключення. Якщо під'єднання, як і раніше, неможливе, зв'яжіться з хостом кімнати та переконайтеся, що хост правильно налаштований із прокинутим зовнішнім портом. + + + + Unable to connect to the room because it is already full. + Неможливо підключитися до кімнати, оскільки вона вже заповнена. + + + + Creating a room failed. Please retry. Restarting yuzu might be necessary. + Створення кімнати не вдалося. Будь ласка, повторіть спробу. Можливо, потрібно перезапустити yuzu. + + + + The host of the room has banned you. Speak with the host to unban you or try a different room. + Хост кімнати заблокував вас. Поговоріть із хостом, щоб він розблокував вас, або спробуйте іншу кімнату. + + + + Version mismatch! Please update to the latest version of yuzu. If the problem persists, contact the room host and ask them to update the server. + Невідповідність версій! Будь ласка, оновіть yuzu до останньої версії. Якщо проблема не зникне, зверніться до хосту кімнати і попросіть його оновити сервер. + + + + Incorrect password. + Невірний пароль. + + + + An unknown error occurred. If this error continues to occur, please open an issue + Сталася невідома помилка. Якщо ця помилка продовжує виникати, будь ласка, відкрийте проблему + + + + Connection to room lost. Try to reconnect. + З'єднання з кімнатою втрачено. Спробуйте підключитися знову. + + + + You have been kicked by the room host. + Вас вигнав хост кімнати. + + + + IP address is already in use. Please choose another. + IP-адреса вже використовується. Будь ласка, виберіть іншу. + + + + You do not have enough permission to perform this action. + У вас немає достатніх дозволів для виконання цієї дії. + + + + The user you are trying to kick/ban could not be found. +They may have left the room. + Користувача, якого ви намагаєтеся вигнати/заблокувати, не знайдено. +Можливо, вони покинули кімнату. + + + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + Не вибрано припустимий інтерфейс мережі. +Будь ласка, перейдіть у Налаштування -> Система -> Мережа та зробіть вибір. + + + + Game already running + Гру вже запущено + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + Приєднуватися до кімнати, коли гру вже запущено, не рекомендується, це може призвести до неправильної роботи функції кімнати. +Все одно продовжити? + + + + Leave Room + Залишити кімнату + + + + You are about to close the room. Any network connections will be closed. + Ви збираєтеся закрити кімнату. Усі мережеві підключення буде закрито. + + + + Disconnect + Від'єднатися + + + + You are about to leave the room. Any network connections will be closed. + Ви збираєтеся покинути кімнату. Усі мережеві підключення буде закрито. + + + + NetworkMessage::ErrorManager + + + Error + Помилка + + + + OverlayDialog + + + Dialog + Діалог + + + + + Cancel + Скасувати + + + + + OK + ОК + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:18pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:18pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + PlayerControlPreview + + + START/PAUSE + СТАРТ/ПАУЗА + + + + QObject + + + %1 is not playing a game + %1 не грає у гру + + + + %1 is playing %2 + %1 грає в %2 + + + + Not playing a game + Не грає в гру + + + + Installed SD Titles + Встановлені SD ігри + + + + Installed NAND Titles + Встановлені NAND ігри + + + + System Titles + Системні ігри + + + + Add New Game Directory + Додати нову папку з іграми + + + + Favorites + Улюблені + + + + + + Shift + Shift + + + + + + Ctrl + Ctrl + + + + + + Alt + Alt + + + + + + + + [not set] + [не задано] + + + + Hat %1 %2 + Напр. %1 %2 + + + + + + + + + + + + Axis %1%2 + Ось %1%2 + + + + Button %1 + Кнопка %1 + + + + + + + + + + [unknown] + [невідомо] + + + + + + Left + Вліво + + + + + + Right + Вправо + + + + + + Down + Вниз + + + + + + Up + Вгору + + + + + Z + Z + + + + + R + R + + + + + L + L + + + + + A + A + + + + + B + B + + + + + X + X + + + + + Y + Y + + + + + Start + Start + + + + + L1 + L1 + + + + + L2 + L2 + + + + + L3 + L3 + + + + + R1 + R1 + + + + + R2 + R2 + + + + + R3 + R3 + + + + + Circle + Кружечок + + + + + Cross + Хрестик + + + + + Square + Квадратик + + + + + Triangle + Трикутничок + + + + + Share + Share + + + + + Options + Options + + + + + [undefined] + [невизначено] + + + + %1%2 + %1%2 + + + + + [invalid] + [неприпустимо] + + + + + + + %1%2Hat %3 + %1%2Напр. %3 + + + + + + + + + %1%2Axis %3 + %1%2Ось %3 + + + + + %1%2Axis %3,%4,%5 + %1%2Ось %3,%4,%5 + + + + + %1%2Motion %3 + %1%2Рух %3 + + + + + + + %1%2Button %3 + %1%2Кнопка %3 + + + + + [unused] + [не використаний] + + + + Home + Home + + + + Touch + Сенсор + + + + Wheel + Indicates the mouse wheel + Коліщатко + + + + Backward + Назад + + + + Forward + Вперед + + + + Task + Задача + + + + Extra + Додаткова + + + + %1%2%3 + %1%2%3 + + + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + Тип + + + + Name + Назва + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + + + QtControllerSelectorDialog + + + Controller Applet + Аплет контролера + + + + Supported Controller Types: + Підтримувані типи контролерів: + + + + Players: + Гравці: + + + + 1 - 8 + 1 - 8 + + + + P4 + P4 + + + + + + + + + + + + Pro Controller + Контролер Pro + + + + + + + + + + + + Dual Joycons + Подвійні Joy-Con'и + + + + + + + + + + + + Left Joycon + Лівий Joy-Con + + + + + + + + + + + + Right Joycon + Правий Joy-Con + + + + + + + + + + + Use Current Config + Використовувати поточну конфігурацію + + + + P2 + P2 + + + + P1 + P1 + + + + + + Handheld + Портативний + + + + P3 + P3 + + + + P7 + P7 + + + + P8 + P8 + + + + P5 + P5 + + + + P6 + P6 + + + + Console Mode + Режим консолі + + + + Docked + У док-станції + + + + Vibration + Вібрація + + + + + Configure + Налаштувати + + + + Motion + Рух + + + + Profiles + Профілі + + + + Create + Створити + + + + Controllers + Контролери + + + + 1 + 1 + + + + 2 + 2 + + + + 4 + 4 + + + + 3 + 3 + + + + Connected + З'єднано + + + + 5 + 5 + + + + 7 + 7 + + + + 6 + 6 + + + + 8 + 8 + + + + GameCube Controller + Контролер GameCube + + + + Poke Ball Plus + Poke Ball Plus + + + + NES Controller + Контролер NES + + + + SNES Controller + Контролер SNES + + + + N64 Controller + Контролер N64 + + + + Sega Genesis + Sega Genesis + + + + QtErrorDisplay + + + + + Error Code: %1-%2 (0x%3) + Код помилки: %1-%2 (0x%3) + + + + An error has occurred. +Please try again or contact the developer of the software. + Сталася помилка. +Будь ласка, спробуйте ще раз або зв'яжіться з розробником ПЗ. + + + + An error occurred on %1 at %2. +Please try again or contact the developer of the software. + Сталася помилка на %1 у %2. +Будь ласка, спробуйте ще раз або зв'яжіться з розробником ПЗ. + + + + An error has occurred. + +%1 + +%2 + Сталася помилка. + +%1 + +%2 + + + + QtProfileSelectionDialog + + + %1 +%2 + %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) + %1 +%2 + + + + Select a user: + Оберить користувача + + + + Users + Користувачі + + + + Profile Selector + Вибір профілю + + + + QtSoftwareKeyboardDialog + + + Software Keyboard + Віртуальна клавіатура + + + + Enter Text + Введіть текст + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:26pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:26pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + + OK + ОК + + + + Cancel + Скасувати + + + + SequenceDialog + + + Enter a hotkey + Введіть комбінацію + + + + WaitTreeCallstack + + + Call stack + Стек викликів + + + + WaitTreeMutexInfo + + + waiting for mutex 0x%1 + + + + + has waiters: %1 + + + + + owner handle: 0x%1 + + + + + WaitTreeObjectList + + + waiting for all objects + + + + + waiting for one of the following objects + + + + + WaitTreeSynchronizationObject + + + [%1] %2 %3 + [%1] %2 %3 + + + + waited by no thread + + + + + WaitTreeThread + + + runnable + + + + + paused + + + + + sleeping + + + + + waiting for IPC reply + + + + + waiting for objects + + + + + waiting for condition variable + + + + + waiting for address arbiter + + + + + waiting for suspend resume + + + + + waiting + + + + + initialized + + + + + terminated + + + + + unknown + + + + + PC = 0x%1 LR = 0x%2 + PC = 0x%1 LR = 0x%2 + + + + ideal + + + + + core %1 + + + + + processor = %1 + + + + + ideal core = %1 + + + + + affinity mask = %1 + + + + + thread id = %1 + + + + + priority = %1(current) / %2(normal) + + + + + last running ticks = %1 + + + + + not waiting for mutex + + + + + WaitTreeThreadList + + + waited by thread + + + + + WaitTreeWidget + + + &Wait Tree + [&W] Дерево очікування + + + \ No newline at end of file diff --git a/dist/languages/vi.ts b/dist/languages/vi.ts index 6570ab6..532f4f0 100644 --- a/dist/languages/vi.ts +++ b/dist/languages/vi.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -204,7 +204,7 @@ This would ban both their forum username and their IP address. - %1 (%2/%3 members) - connected + %1 - %2 (%3/%4 members) - connected @@ -218,6 +218,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Báo cáo độ tương thích trò chơi @@ -227,92 +232,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Nếu bạn chọn gửi bản kiểm tra vào </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">danh sách tương thích yuzu</span></a><span style=" font-size:10pt;">, Thông tin sau sẽ được thu thập và hiển thị lên trang web:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Thông tin phần cứng (CPU / GPU / Hệ điều hành)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Phiên bản yuzu bạn đang chạy</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Tài khoản yuzu đang kết nối</li></ul></body></html> - - Perfect - Tốt nhất + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Có thể chơi một cách mượt mà mà không có lỗi âm thanh hoặc đồ họa nào.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Tốt + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Có thể chơi từ đầu đến cuối nhưng vẫn có một số lỗi nhỏ về đồ họa hoặc âm thanh. Có thể sẽ cần tới một vài tinh chỉnh nào đó.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Tạm ổn + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Tạm chơi được nhưng có lỗi đồ họa hoặc âm thanh một cách đáng kể, tuy vậy vẫn có thể chơi hết từ đầu đến cuối với một số tinh chỉnh.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Không tốt + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Game chạy được, tuy nhiên với nhiều lỗi về đồ hoạ và âm thanh. Không thể tiếp tục ở một số khu vực trong game kể cả với tinh chỉnh.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Phần mở đầu/Menu + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Hoàn toàn không thể chơi được do có nhiều lỗi đồ họa hoặc âm thanh. Không thể chơi tiếp sau khi bấm nút bắt đầu trò chơi.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Không khởi động + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Trò chơi sẽ thoát đột ngột khi khởi động.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Không phụ thuộc vào tốc độ hay hiệu suất, trò chơi này chơi như thế nào từ đầu đến cuối trên phiên bản này của yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Cảm ơn bạn đã gửi đến cho chúng tôi! - + Submitting Đang gửi - + Communication error Đã xảy ra lỗi giao tiếp với máy chủ - + An error occurred while sending the Testcase Có lỗi xảy ra khi gửi Testcase - + Next Tiếp theo @@ -410,7 +450,7 @@ This would ban both their forum username and their IP address. Khôi phục về mặc định - + Auto Tự động @@ -733,200 +773,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub Cho phép bật GDB sơ khai - + Port: Cổng: - + Logging Báo cáo - + Global Log Filter Bộ lọc báo cáo chung - + Show Log in Console - + Open Log Location Mở vị trí sổ ghi chép - + When checked, the max size of the log increases from 100 MB to 1 GB Khi tích vào, dung lượng tối đa cho file log chuyển từ 100 MB lên 1 GB - + Enable Extended Logging** - + Homebrew Homebrew - + Arguments String Xâu lệnh - + Graphics Đồ hoạ - + When checked, the graphics API enters a slower debugging mode - + Enable Graphics Debugging Kích hoạt chế độ gỡ lỗi đồ hoạ - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - + Disable Macro JIT Không dùng Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Vá lỗi - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Nâng Cao - + Kiosk (Quest) Mode - + Enable CPU Debugging Bật Vá Lỗi CPU - + Enable Debug Asserts - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Sẽ tự động thiết lập lại khi tắt yuzu. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1296,193 +1371,218 @@ This would ban both their forum username and their IP address. API đồ hoạ: - + Graphics Settings Cài đặt đồ hoạ - + Use disk pipeline cache - + Use asynchronous GPU emulation Dùng giả lập GPU không đồng bộ - + Accelerate ASTC texture decoding - + NVDEC emulation: Giả lập NVDEC - + No Video Output Không Video Đầu Ra - + CPU Video Decoding - + GPU Video Decoding (Default) - + Fullscreen Mode: Chế độ Toàn màn hình: - + Borderless Windowed Cửa sổ không viền - + Exclusive Fullscreen Toàn màn hình - + Aspect Ratio: Tỉ lệ khung hình: - + Default (16:9) Mặc định (16:9) - + Force 4:3 Dùng 4:3 - + Force 21:9 Dùng 21:9 - + + Force 16:10 + + + + Stretch to Window Kéo dãn đến cửa sổ phần mềm - + Resolution: - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) - + 2X (1440p/2160p) - + 3X (2160p/3240p) - + 4X (2880p/4320p) - + 5X (3600p/5400p) - + 6X (4320p/6480p) - + Window Adapting Filter: - + Nearest Neighbor - + Bilinear - + Bicubic - + Gaussian - + ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) - + Anti-Aliasing Method: - + None Trống - + FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Dùng màu nền theo cài đặt - + Set background color: Chọn màu nền: - + Background Color: Màu nền: @@ -1491,6 +1591,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM (Assembly Shaders, Chỉ Cho NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1545,37 +1651,47 @@ This would ban both their forum username and their IP address. Tăng Tốc Thời Gian GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Bộ lọc góc nghiêng: - + Automatic - + Default Mặc định - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2067,7 +2183,7 @@ This would ban both their forum username and their IP address. - + Left Stick Cần trái @@ -2161,14 +2277,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2187,7 +2303,7 @@ This would ban both their forum username and their IP address. - + Plus Cộng @@ -2200,15 +2316,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2265,231 +2381,236 @@ This would ban both their forum username and their IP address. - + Right Stick Cần phải - - - - + + + + Clear Bỏ trống - - - - - + + + + + [not set] [không đặt] - - - Toggle button - - - - - + + Invert button - - + + + Toggle button + + + + + Invert axis - - - + + + Set threshold - - + + Choose a value between 0% and 100% Chọn một giá trị giữa 0% và 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Thiết lập Cần Điều Khiển - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Sau khi bấm OK, di chuyển cần sang ngang, rồi sau đó sang dọc. Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần sang dọc trước, rồi sang ngang. - + Center axis - - + + Deadzone: %1% - - + + Modifier Range: %1% - - + + Pro Controller Tay cầm Pro Controller - + Dual Joycons Joycon đôi - + Left Joycon Joycon Trái - + Right Joycon Joycon Phải - + Handheld Cầm tay - + GameCube Controller Tay cầm GameCube - + Poke Ball Plus - + NES Controller - + SNES Controller - + N64 Controller - + Sega Genesis - + Start / Pause Bắt đầu / Tạm ngưng - + Z Z - + Control Stick - + C-Stick C-Stick - + Shake! Lắc! - + [waiting] [Chờ] - + New Profile Hồ sơ mới - + Enter a profile name: Nhập tên hồ sơ: - - + + Create Input Profile Tạo Hồ Sơ Phím - + The given profile name is not valid! Tên hồ sơ không hợp lệ! - + Failed to create the input profile "%1" Quá trình tạo hồ sơ phím "%1" thất bại - + Delete Input Profile Xóa Hồ Sơ Phím - + Failed to delete the input profile "%1" Quá trình xóa hồ sơ phím "%1" thất bại - + Load Input Profile Nạp Hồ Sơ Phím - + Failed to load the input profile "%1" Quá trình nạp hồ sơ phím "%1" thất bại - + Save Input Profile Lưu Hồ Sơ Phím - + Failed to save the input profile "%1" Quá trình lưu hồ sơ phím "%1" thất bại @@ -2744,42 +2865,42 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s Nhà Phát Hành - + Add-Ons Bổ Sung - + General Chung - + System Hệ thống - + CPU CPU - + Graphics Đồ hoạ - + Adv. Graphics Đồ Họa Nâng Cao - + Audio Âm thanh - + Properties Thuộc tính @@ -2835,37 +2956,37 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s Người Dùng Hiện Tại - + Username Tên - + Set Image Đặt Hình Ảnh - + Add Thêm - + Rename Đổi Tên - + Remove Gỡ Bỏ - + Profile management is available only when game is not running. Chỉ có thể quản lí hồ sơ khi game không chạy. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2873,96 +2994,105 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s %2 - + Enter Username Điền Tên - + Users Người Dùng - + Enter a username for the new user: Chọn tên cho người dùng mới - + Enter a new username: Chọn một tên mới: - - Confirm Delete - Xác nhận xóa - - - - You are about to delete user with name "%1". Are you sure? - Bạn đang xóa người dùng "%1". Bạn chắc chứ? - - - + Select User Image Chọn Ảnh cho Người Dùng - + JPEG Images (*.jpg *.jpeg) Ảnh JPEG (*.jpg *.jpeg) - + Error deleting image Lỗi khi xóa ảnh - + Error occurred attempting to overwrite previous image at: %1. Có lỗi khi ghi đè ảnh trước tại: %1. - + Error deleting file Lỗi xóa ảnh - + Unable to delete existing file: %1. Không thể xóa ảnh hiện tại: %1. - + Error creating user image directory Lỗi khi tạo thư mục chứa ảnh người dùng - + Unable to create directory %1 for storing user images. Không thể tạo thư mục %1 để chứa ảnh người dùng - + Error copying user image Lỗi chép ảnh người dùng - + Unable to copy image from %1 to %2 Không thể chép ảnh từ %1 sang %2 - + Error resizing user image Lỗi thu phóng ảnh - + Unable to resize image Không thể thu phóng ảnh + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Xác nhận xóa + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3491,47 +3621,47 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Đường dẫn - + ... ... @@ -3782,56 +3912,71 @@ Drag points to change position, or double-click table cells to edit values. + Show Compatibility List + + + + Show Add-Ons Column Hiện thị cột Tiện ích ngoài - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: - + Folder Icon Size: - + Row 1 Text: Dòng chữ hàng 1: - + Row 2 Text: Dòng chữ hàng 2: - + Screenshots - + Ask Where To Save Screenshots (Windows Only) - + Screenshots Path: - + ... ... - + Select Screenshots Path... - + <System> <System> @@ -3940,7 +4085,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify Xác nhận @@ -4027,7 +4172,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified @@ -4042,17 +4187,36 @@ Drag points to change position, or double-click table cells to edit values. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Xác nhận không thành công + + + Verification failed Xác nhận không thành công - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. @@ -4121,12 +4285,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting - + Connect @@ -4134,908 +4298,910 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Dữ liệu ẩn danh được thu thập</a>để hỗ trợ cải thiện yuzu. <br/><br/>Bạn có muốn chia sẽ dữ liệu sử dụng cho chúng tôi? - + Telemetry Viễn trắc - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Tốc độ giả lập hiện tại. Giá trị cao hơn hoặc thấp hơn 100% chỉ ra giả lập sẽ chạy nhanh hơn hoặc chậm hơn trên máy Switch - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Có bao nhiêu khung hình trên mỗi giây mà trò chơi đang hiển thị. Điều này sẽ thay đổi từ giữa các trò chơi và các khung cảnh khác nhau. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Thời gian mà giả lập lấy từ khung hình Switch, sẽ không kể đến giới hạn khung hình hoặc v-sync. Đối với tốc độ tối đa mà giả lập nhận được nhiều nhất là ở độ khoảng 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files - + &Continue - + &Pause &Tạm dừng - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Chú ý định dạng trò chơi đã lỗi thời - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Bạn đang sử dụng định dạng danh mục ROM giải mã cho trò chơi này, và đó là một định dạng lỗi thời đã được thay thế bởi những thứ khác như NCA, NAX, XCI, hoặc NSP. Danh mục ROM giải mã có thể thiếu các icon, metadata, và hỗ trợ cập nhật.<br><br>Để hiểu thêm về các định dạng khác nhau của Switch mà yuzu hỗ trợ, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>vui lòng kiểm tra trên wiki của chúng tôi</a>. Thông báo này sẽ không hiển thị lại lần sau. - - + + Error while loading ROM! Lỗi xảy ra khi nạp ROM! - + The ROM format is not supported. Định dạng ROM này không hỗ trợ. - + An error occurred initializing the video core. Đã xảy ra lỗi khi khởi tạo lõi đồ hoạ. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. Đã xảy ra lỗi không xác định. Hãy kiểm tra phần báo cáo để biết thêm chi tiết. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Save Data - + Mod Data - + Error Opening %1 Folder Xảy ra lỗi khi mở %1 thư mục - - + + Folder does not exist! Thư mục này không tồn tại! - + Error Opening Transferable Shader Cache - + Failed to create the shader cache directory for this title. - - Contents - - - - - Update - Cập nhật - - - - DLC - - - - - Remove Entry - - - - - Remove Installed Game %1? - - - - - - - - - Successfully Removed + Error Removing Contents - - Successfully removed the installed base game. - - - - - - - Error Removing %1 - - - - - The base game is not installed in the NAND and cannot be removed. - - - - - Successfully removed the installed update. + + Error Removing Update + Error Removing DLC + + + + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + + Remove Entry + + + + + + + + + + Successfully Removed + + + + + Successfully removed the installed base game. + + + + + The base game is not installed in the NAND and cannot be removed. + + + + + Successfully removed the installed update. + + + + There is no update installed for this title. - + There are no DLC installed for this title. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File - - + + Error Removing Transferable Shader Cache - - + + A shader cache for this title does not exist. - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! Khai thác RomFS không thành công! - + There was an error copying the RomFS files or the user cancelled the operation. Đã xảy ra lỗi khi sao chép tệp tin RomFS hoặc người dùng đã hủy bỏ hoạt động này. - + Full - + Skeleton Sườn - + Select RomFS Dump Mode Chọn chế độ kết xuất RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Vui lòng chọn cách mà bạn muốn RomFS kết xuất.<br>Chế độ Đầy Đủ sẽ sao chép toàn bộ tệp tin vào một danh mục mới trong khi <br>chế độ Sườn chỉ tạo kết cấu danh mục. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Khai thác RomFS... - - + + Cancel Hủy bỏ - + RomFS Extraction Succeeded! Khai thác RomFS thành công! - + The operation completed successfully. Các hoạt động đã hoàn tất thành công. - + Error Opening %1 - + Select Directory Chọn danh mục - + Properties Thuộc tính - + The game properties could not be loaded. Không thể tải thuộc tính của trò chơi. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Thực thi Switch (%1);;Tất cả tệp tin (*.*) - + Load File Nạp tệp tin - + Open Extracted ROM Directory Mở danh mục ROM đã trích xuất - + Invalid Directory Selected Danh mục đã chọn không hợp lệ - + The directory you have selected does not contain a 'main' file. Danh mục mà bạn đã chọn không có chứa tệp tin 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Những tệp tin Switch cài được (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... Đang cài đặt tệp tin "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Ứng dụng hệ thống - + System Archive Hệ thống lưu trữ - + System Application Update Cập nhật hệ thống ứng dụng - + Firmware Package (Type A) Gói phần mềm hệ thống (Loại A) - + Firmware Package (Type B) Gói phần mềm (Loại B) - + Game Trò chơi - + Game Update Cập nhật trò chơi - + Game DLC Nội dung trò chơi có thể tải xuống - + Delta Title Tiêu đề Delta - + Select NCA Install Type... Chọn cách cài đặt NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Vui lòng chọn loại tiêu đề mà bạn muốn cài đặt NCA này: (Trong hầu hết trường hợp, chọn mặc định 'Game' là tốt nhất.) - + Failed to Install Cài đặt đã không thành công - + The title type you selected for the NCA is invalid. Loại tiêu đề NCA mà bạn chọn nó không hợp lệ. - + File not found Không tìm thấy tệp tin - + File "%1" not found Không tìm thấy tệp tin "%1" - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Thiếu tài khoản yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Để gửi trường hợp thử nghiệm trò chơi tương thích, bạn phải liên kết tài khoản yuzu.<br><br/>Để liên kết tải khoản yuzu của bạn, hãy đến Giả lập &gt; Thiết lập &gt; Web. - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - - Error - - - - - - The current game is not looking for amiibos - - - - - + + Amiibo - - + + The current amiibo has been removed - + + Error + + + + + + The current game is not looking for amiibos + + + + Amiibo File (%1);; All Files (*.*) Tệp tin Amiibo (%1);; Tất cả tệp tin (*.*) - + Load Amiibo Nạp dữ liệu Amiibo - - Error opening Amiibo data file - Xảy ra lỗi khi mở dữ liệu tệp tin Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Không thể mở tệp tin Amiibo "%1" để đọc. - - - - Error reading Amiibo data file - Xảy ra lỗi khi đọc dữ liệu tệp tin Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Hoàn toàn không thể đọc được dữ liệu Amiibo. Dự kiến byte sẽ đọc là %1, nhưng byte chỉ có thể đọc là %2. - - - + Error loading Amiibo data Xảy ra lỗi khi nạp dữ liệu Amiibo - - Unable to load Amiibo data. - Không thể nạp dữ liệu Amiibo. - - - - Capture Screenshot - Chụp ảnh màn hình - - - - PNG Image (*.png) - Hình ảnh PNG (*.png) - - - - TAS state: Running %1/%2 + + The selected file is not a valid amiibo - - TAS state: Recording %1 + + The selected file is already on use - - TAS state: Idle %1/%2 + + An unknown error occurred + Capture Screenshot + Chụp ảnh màn hình + + + + PNG Image (*.png) + Hình ảnh PNG (*.png) + + + + TAS state: Running %1/%2 + + + + + TAS state: Recording %1 + + + + + TAS state: Idle %1/%2 + + + + TAS State: Invalid - + &Stop Running - + &Start &Bắt đầu - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Tốc độ: %1% / %2% - + Speed: %1% Tốc độ: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Trò chơi: %1 FPS - + Frame: %1 ms Khung hình: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Trò chơi bạn muốn chạy yêu cầu một số tệp tin được sao chép từ thiết từ máy Switch của bạn trước khi bắt đầu chơi.<br/><br/>Để biết thêm thông tin về cách sao chép những tệp tin đó, vui lòng tham khảo những wiki sau: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Sao chép dữ liệu hệ thống và font dùng chung từ máy Switch</a>.<br/><br/>Bạn có muốn trở về danh sách trò chơi? Nếu bạn vẫn tiếp tục thì trò chơi có thể gặp sự cố, dữ liệu lưu tiến trình có thể bị lỗi, hoặc bạn sẽ gặp những lỗi khác. - + yuzu was unable to locate a Switch system archive. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 - + System Archive Not Found Không tìm thấy tệp tin hệ thống - + System Archive Missing Bị thiếu tệp tin hệ thống - + yuzu was unable to locate the Switch shared fonts. %1 yuzu không thể tìm thấy vị trí font dùng chung của Switch. %1 - + Shared Fonts Not Found Không tìm thấy font dùng chung - + Shared Font Missing Bị thiếu font dùng chung - + Fatal Error Lỗi nghiêm trọng - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu đã xảy ra lỗi nghiêm trọng, vui lòng kiểm tra sổ ghi chép để biết thêm chi tiết. Để biết thêm thông tin về cách truy cập sổ ghi chép, vui lòng xem trang sau: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Làm sao để tải tệp tin sổ ghi chép lên</a>.<br/><br/>Bạn có muốn trở về danh sách game? Tiếp tục có thể khiến giả lập gặp sự cố, gây hỏng dữ liệu đã lưu, hoặc gây các lỗi khác. - + Fatal Error encountered - + Confirm Key Rederivation Xác nhận mã khóa Rederivation - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5052,37 +5218,37 @@ và phải tạo ra một bản sao lưu lại. Điều này sẽ xóa mã khóa tự động tạo trên tệp tin của bạn và chạy lại mô-đun chiết xuất mã khoá. - + Missing fuses - + - Missing BOOT0 - + - Missing BCPKG2-1-Normal-Main - + - Missing PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5091,39 +5257,39 @@ on your system's performance. hệ thống của bạn. - + Deriving Keys Mã khóa xuất phát - + Select RomFS Dump Target Chọn thư mục để sao chép RomFS - + Please select which RomFS you would like to dump. Vui lòng chọn RomFS mà bạn muốn chiết xuất. - + Are you sure you want to close yuzu? Bạn có chắc chắn muốn đóng yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Bạn có chắc rằng muốn dừng giả lập? Bất kì tiến trình nào chưa được lưu sẽ bị mất. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5135,38 +5301,38 @@ Bạn có muốn bỏ qua yêu cầu đó và thoát luôn không? GRenderWindow - + OpenGL not available! Không có sẵn OpenGL! - + yuzu has not been compiled with OpenGL support. - - + + Error while initializing OpenGL! Đã xảy ra lỗi khi khởi tạo OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. - + Error while initializing OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 @@ -5174,153 +5340,153 @@ Bạn có muốn bỏ qua yêu cầu đó và thoát luôn không? GameList - + Favorite - + Start Game - + Start Game without Custom Configuration - + Open Save Data Location Mở vị trí dữ liệu lưu - + Open Mod Data Location Mở vị trí chỉnh sửa dữ liệu - + Open Transferable Pipeline Cache - + Remove Gỡ Bỏ - + Remove Installed Update - + Remove All Installed DLC - + Remove Custom Configuration - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents - + Dump RomFS Kết xuất RomFS - + Dump RomFS to SDMC - + Copy Title ID to Clipboard Sao chép ID tiêu đề vào bộ nhớ tạm - + Navigate to GameDB entry Điều hướng đến mục cơ sở dữ liệu trò chơi - + Properties Thuộc tính - + Scan Subfolders - + Remove Game Directory - + ▲ Move Up - + ▼ Move Down - + Open Directory Location - + Clear Bỏ trống - + Name Tên - + Compatibility Độ tương thích - + Add-ons Tiện ích ngoài - + File type Loại tệp tin - + Size Kích cỡ @@ -5329,78 +5495,61 @@ Bạn có muốn bỏ qua yêu cầu đó và thoát luôn không? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Tốt nhất - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - Game chạy rất tốt, không lỗi về âm thanh và đồ hoạ, mọi chức năng của game được thử thì hoạt động như dự kiến mà không cần -bất kì tinh chỉnh nào. - - - - Great - Tốt - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - Có thể chơi từ đầu đến cuối nhưng vẫn có một số lỗi nhỏ về đồ họa hoặc âm thanh. Có thể sẽ cần tới một vài tinh chỉnh nào đó. - - Okay - Tạm ổn - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - Game chạy nhưng với rất nhiều lỗi đồ hoạ và âm thanh, tuy nhiên có thể chơi từ đầu đến cuối với -một số tinh chỉnh. + Game can be played without issues. + - Bad - Không tốt + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Game chạy được, tuy nhiên với nhiều lỗi về đồ hoạ và âm thanh. Không thể tiếp tục ở một số khu vực trong game kể cả với tinh chỉnh. + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu Phần mở đầu/Menu - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Hoàn toàn không thể chơi được do có nhiều lỗi đồ họa hoặc âm thanh. Không thể chơi tiếp sau khi bấm nút bắt đầu trò chơi. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Không khởi động - + The game crashes when attempting to startup. Trò chơi sẽ thoát đột ngột khi khởi động. - + Not Tested Chưa ai thử - + The game has not yet been tested. Trò chơi này chưa có ai thử cả. @@ -5408,7 +5557,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list @@ -5421,12 +5570,12 @@ Screen. - + Filter: Bộ lọc: - + Enter pattern to filter Nhập khuôn để lọc @@ -5502,12 +5651,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5516,11 +5665,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5542,112 +5692,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Chụp ảnh màn hình - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Toàn màn hình - + Load File Nạp tệp tin - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5761,42 +5910,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Người chơi - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5819,232 +5968,237 @@ Debug Message: - + &Emulation &Giả lập - + &View &Xem - + &Reset Window Size - + &Debugging - + Reset Window Size to &720p - + Reset Window Size to 720p - + Reset Window Size to &900p - + Reset Window Size to 900p - + Reset Window Size to &1080p - + Reset Window Size to 1080p - + + &Multiplayer + + + + &Tools - + &TAS - + &Help &Trợ giúp - + &Install Files to NAND... - + L&oad File... - + Load &Folder... - + E&xit Th&oát - + &Pause &Tạm dừng - + &Stop &Dừng - + &Reinitialize keys... - + &About yuzu - + Single &Window Mode - + Con&figure... - + Display D&ock Widget Headers - + Show &Filter Bar - + Show &Status Bar - + Show Status Bar Hiển thị thanh trạng thái - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room + &Browse Public Game Lobby - - Direct Connect to Room + + &Create Room - - Show Current Room + + &Leave Room - F&ullscreen + &Direct Connect to Room - &Restart + &Show Current Room - Load/Remove &Amiibo... + F&ullscreen - &Report Compatibility + &Restart + Load/Remove &Amiibo... + + + + + &Report Compatibility + + + + Open &Mods Page - + Open &Quickstart Guide - + &FAQ - + Open &yuzu Folder - + &Capture Screenshot - + &Configure TAS... - + Configure C&urrent Game... - + &Start &Bắt đầu - + &Reset - + R&ecord @@ -6109,46 +6263,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Đã kết nối - - - - Not Connected - + + Connected + Đã kết nối + + + + New Messages Received + + + + Error - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6249,22 +6398,39 @@ They may have left the room. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6272,7 +6438,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6317,42 +6483,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 is playing %2 - + Not playing a game - + Installed SD Titles - + Installed NAND Titles - + System Titles - + Add New Game Directory - + Favorites @@ -6382,7 +6548,7 @@ p, li { white-space: pre-wrap; } - + [not set] [chưa đặt nút] @@ -6397,10 +6563,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Trục %1%2 @@ -6414,9 +6580,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [không xác định] @@ -6581,15 +6747,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6597,35 +6763,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [không sử dụng] @@ -6666,11 +6832,124 @@ p, li { white-space: pre-wrap; } - + %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Tên + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6774,6 +7053,7 @@ p, li { white-space: pre-wrap; } + Handheld Cầm tay @@ -6813,11 +7093,6 @@ p, li { white-space: pre-wrap; } Docked Chế độ cắm TV - - - Undocked - Chế độ cầm tay - Vibration diff --git a/dist/languages/vi_VN.ts b/dist/languages/vi_VN.ts index 88c9720..f258769 100644 --- a/dist/languages/vi_VN.ts +++ b/dist/languages/vi_VN.ts @@ -89,78 +89,78 @@ p, li { white-space: pre-wrap; } - + Members - + %1 has joined - + %1 has left - + %1 has been kicked - + %1 has been banned - + %1 has been unbanned - + View Profile - - + + Block Player - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - + Kick - + Ban - + Kick Player - + Are you sure you would like to <b>kick</b> %1? - + Ban Player - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -204,7 +204,7 @@ This would ban both their forum username and their IP address. - %1 (%2/%3 members) - connected + %1 - %2 (%3/%4 members) - connected @@ -218,6 +218,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility Báo cáo trò chơi tương thích @@ -227,92 +232,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">Nếu bạn chọn gửi bản kiểm tra vào </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">danh sách tương thích yuzu</span></a><span style=" font-size:10pt;">, Thông tin sau sẽ được thu thập và hiển thị lên trang web:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Thông tin phần cứng (CPU / GPU / Hệ điều hành)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Phiên bản yuzu bạn đang chạy</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Tài khoản yuzu đang kết nối</li></ul></body></html> - - Perfect - Tốt nhất + + <html><head/><body><p>Does the game boot?</p></body></html> + - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>Có thể chơi một cách mượt mà mà không có lỗi âm thanh hoặc đồ họa nào.</p></body></html> + + Yes The game starts to output video or audio + - - Great - Tốt + + No The game doesn't get past the "Launching..." screen + - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>Có thể chơi từ đầu đến cuối nhưng vẫn có một số lỗi nhỏ về đồ họa hoặc âm thanh. Có thể sẽ cần tới một vài tinh chỉnh nào đó.</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + - - Okay - Tạm ổn + + No The game crashes or freezes while loading or using the menu + - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>Tạm chơi được nhưng có lỗi đồ họa hoặc âm thanh một cách đáng kể, tuy vậy vẫn có thể chơi hết từ đầu đến cuối với một số tinh chỉnh.</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + - - Bad - Không tốt + + Yes The game works without crashes + - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>Chơi được nhưng có nhiều lỗi đồ họa hoặc âm thanh. Không thể chơi tiếp ở một số nơi nào đó do lỗi dù có tinh chỉnh thế nào đi nữa.</p></body></html> + + No The game crashes or freezes during gameplay + - - Intro/Menu - Phần mở đầu/Menu + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>Hoàn toàn không thể chơi được do có nhiều lỗi đồ họa hoặc âm thanh. Không thể chơi tiếp sau khi bấm nút bắt đầu trò chơi.</p></body></html> + + Yes The game can be finished without any workarounds + - - Won't Boot - Không hoạt động + + No The game can't progress past a certain area + - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>Trò chơi sẽ thoát đột ngột khi khởi động.</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>Không phụ thuộc vào tốc độ hay hiệu suất, trò chơi này chơi như thế nào từ đầu đến cuối trên phiên bản này của yuzu?</p></body></html> + + Major The game has major graphical errors + - + + Minor The game has minor graphical errors + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + + + Major The game has major audio errors + + + + + Minor The game has minor audio errors + + + + + None Audio is played perfectly + + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + + Thank you for your submission! Cảm ơn bạn đã gửi đến cho chúng tôi! - + Submitting Đang gửi - + Communication error Đã xảy ra lỗi giao tiếp với máy chủ - + An error occurred while sending the Testcase Có lỗi xảy ra khi gửi Testcase - + Next Tiếp theo @@ -410,7 +450,7 @@ This would ban both their forum username and their IP address. Khôi phục về mặc định - + Auto Tự động @@ -733,200 +773,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger - + Enable GDB Stub Cho phép bật GDB sơ khai - + Port: Cổng: - + Logging Sổ ghi chép - + Global Log Filter Bộ lọc sổ ghi chép - + Show Log in Console - + Open Log Location Mở vị trí sổ ghi chép - + When checked, the max size of the log increases from 100 MB to 1 GB Khi tích vào, dung lượng tối đa cho file log chuyển từ 100 MB lên 1 GB - + Enable Extended Logging** - + Homebrew Homebrew - + Arguments String Chuỗi đối số - + Graphics Đồ hoạ - + When checked, the graphics API enters a slower debugging mode - + Enable Graphics Debugging Kích hoạt chế độ gỡ lỗi đồ hoạ - + When checked, it enables Nsight Aftermath crash dumps - + Enable Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - + Dump Game Shaders - + When checked, it will dump all the macro programs of the GPU - + Dump Maxwell Macros - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower - + Disable Macro JIT Không dùng Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache - + Enable Shader Feedback - + When checked, it executes shaders without loop logic changes - + Disable Loop safety checks - + Debugging Vá lỗi - - Enable FS Access Log - - - - - Dump Audio Commands To Console** - - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - - - - + Enable Verbose Reporting Services** - + + Enable FS Access Log + + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + + + + + Dump Audio Commands To Console** + + + + + Create Minidump After Crash + + + + Advanced Nâng Cao - + Kiosk (Quest) Mode - + Enable CPU Debugging Bật Vá Lỗi CPU - + Enable Debug Asserts - + Enable Auto-Stub** - + Enable All Controller Types - + Disable Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + + + Perform Startup Vulkan Check + + + + **This will be reset automatically when yuzu closes. **Sẽ tự động thiết lập lại khi tắt yuzu. + + + Restart Required + + + + + yuzu is required to restart in order to apply this setting. + + + + + Web applet not compiled + + + + + MiniDump creation not compiled + + ConfigureDebugController @@ -1296,193 +1371,218 @@ This would ban both their forum username and their IP address. API đồ hoạ: - + Graphics Settings Cài đặt đồ hoạ - + Use disk pipeline cache - + Use asynchronous GPU emulation Dùng giả lập GPU không đồng bộ - + Accelerate ASTC texture decoding - + NVDEC emulation: Giả lập NVDEC - + No Video Output Không Video Đầu Ra - + CPU Video Decoding - + GPU Video Decoding (Default) - + Fullscreen Mode: Chế độ Toàn màn hình: - + Borderless Windowed Cửa sổ không viền - + Exclusive Fullscreen Toàn màn hình - + Aspect Ratio: Tỉ lệ khung hình: - + Default (16:9) Mặc định (16:9) - + Force 4:3 Dùng 4:3 - + Force 21:9 Dùng 21:9 - + + Force 16:10 + + + + Stretch to Window Kéo dãn đến cửa sổ phần mềm - + Resolution: - + 0.5X (360p/540p) [EXPERIMENTAL] - + 0.75X (540p/810p) [EXPERIMENTAL] - + 1X (720p/1080p) - + 2X (1440p/2160p) - + 3X (2160p/3240p) - + 4X (2880p/4320p) - + 5X (3600p/5400p) - + 6X (4320p/6480p) - + Window Adapting Filter: - + Nearest Neighbor - + Bilinear - + Bicubic - + Gaussian - + ScaleForce - + AMD FidelityFX™️ Super Resolution (Vulkan Only) - + Anti-Aliasing Method: - + None Trống - + FXAA - - + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + FSR Sharpness: + + + + + 100% + + + + + Use global background color Dùng màu nền theo cài đặt - + Set background color: Chọn màu nền: - + Background Color: Màu nền: @@ -1491,6 +1591,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM (Assembly Shaders, Chỉ Cho NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1545,37 +1651,47 @@ This would ban both their forum username and their IP address. Tăng Tốc Thời Gian GPU (Hack) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + + + + + Use pessimistic buffer flushes (Hack) + + + + Anisotropic Filtering: Bộ lọc góc nghiêng: - + Automatic - + Default Mặc định - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2067,7 +2183,7 @@ This would ban both their forum username and their IP address. - + Left Stick Cần trái @@ -2161,14 +2277,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2187,7 +2303,7 @@ This would ban both their forum username and their IP address. - + Plus Cộng @@ -2200,15 +2316,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2265,231 +2381,236 @@ This would ban both their forum username and their IP address. - + Right Stick Cần phải - - - - + + + + Clear Bỏ trống - - - - - + + + + + [not set] [không đặt] - - - Toggle button - - - - - + + Invert button - - + + + Toggle button + + + + + Invert axis - - - + + + Set threshold - - + + Choose a value between 0% and 100% Chọn một giá trị giữa 0% và 100% - + + Toggle axis + + + + Set gyro threshold - + Map Analog Stick Thiết lập Cần Điều Khiển - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. Sau khi bấm OK, di chuyển cần sang ngang, rồi sau đó sang dọc. Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần sang dọc trước, rồi sang ngang. - + Center axis - - + + Deadzone: %1% - - + + Modifier Range: %1% - - + + Pro Controller Tay cầm Pro Controller - + Dual Joycons Joycon đôi - + Left Joycon Joycon Trái - + Right Joycon Joycon Phải - + Handheld Cầm tay - + GameCube Controller Tay cầm GameCube - + Poke Ball Plus - + NES Controller - + SNES Controller - + N64 Controller - + Sega Genesis - + Start / Pause Bắt đầu / Tạm ngưng - + Z Z - + Control Stick - + C-Stick C-Stick - + Shake! Lắc! - + [waiting] [Chờ] - + New Profile Hồ sơ mới - + Enter a profile name: Nhập tên hồ sơ: - - + + Create Input Profile Tạo Hồ Sơ Phím - + The given profile name is not valid! Tên hồ sơ không hợp lệ! - + Failed to create the input profile "%1" Quá trình tạo hồ sơ phím "%1" thất bại - + Delete Input Profile Xóa Hồ Sơ Phím - + Failed to delete the input profile "%1" Quá trình xóa hồ sơ phím "%1" thất bại - + Load Input Profile Nạp Hồ Sơ Phím - + Failed to load the input profile "%1" Quá trình nạp hồ sơ phím "%1" thất bại - + Save Input Profile Lưu Hồ Sơ Phím - + Failed to save the input profile "%1" Quá trình lưu hồ sơ phím "%1" thất bại @@ -2744,42 +2865,42 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s Nhà Phát Hành - + Add-Ons Bổ Sung - + General Tổng Quan - + System Hệ Thống - + CPU CPU - + Graphics Đồ Họa - + Adv. Graphics Đồ Họa Nâng Cao - + Audio Âm Thanh - + Properties Thuộc tính @@ -2835,37 +2956,37 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s Người Dùng Hiện Tại - + Username Tên - + Set Image Đặt Hình Ảnh - + Add Thêm - + Rename Đổi Tên - + Remove Gỡ Bỏ - + Profile management is available only when game is not running. Chỉ có thể quản lí hồ sơ khi game không chạy. - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2873,96 +2994,105 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s %2 - + Enter Username Điền Tên - + Users Người Dùng - + Enter a username for the new user: Chọn tên cho người dùng mới - + Enter a new username: Chọn một tên mới: - - Confirm Delete - Xác nhận xóa - - - - You are about to delete user with name "%1". Are you sure? - Bạn đang xóa người dùng "%1". Bạn chắc chứ? - - - + Select User Image Chọn Ảnh cho Người Dùng - + JPEG Images (*.jpg *.jpeg) Ảnh JPEG (*.jpg *.jpeg) - + Error deleting image Lỗi khi xóa ảnh - + Error occurred attempting to overwrite previous image at: %1. Có lỗi khi ghi đè ảnh trước tại: %1. - + Error deleting file Lỗi xóa ảnh - + Unable to delete existing file: %1. Không thể xóa ảnh hiện tại: %1. - + Error creating user image directory Lỗi khi tạo thư mục chứa ảnh người dùng - + Unable to create directory %1 for storing user images. Không thể tạo thư mục %1 để chứa ảnh người dùng - + Error copying user image Lỗi chép ảnh người dùng - + Unable to copy image from %1 to %2 Không thể chép ảnh từ %1 sang %2 - + Error resizing user image Lỗi thu phóng ảnh - + Unable to resize image Không thể thu phóng ảnh + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + + + + + Confirm Delete + Xác nhận xóa + + + + Name: %1 +UUID: %2 + + + ConfigureRingController @@ -3491,47 +3621,47 @@ Nếu muốn đảo ngược hướng cần điều khiển, di chuyển cần s - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. - + Settings - + Enable TAS features - + Loop script - + Pause execution during loads - + Script Directory - + Path Đường dẫn - + ... ... @@ -3782,56 +3912,71 @@ Drag points to change position, or double-click table cells to edit values. + Show Compatibility List + + + + Show Add-Ons Column Hiện thị cột Tiện ích ngoài - + + Show Size Column + + + + + Show File Types Column + + + + Game Icon Size: - + Folder Icon Size: - + Row 1 Text: Dòng chữ hàng 1: - + Row 2 Text: Dòng chữ hàng 2: - + Screenshots - + Ask Where To Save Screenshots (Windows Only) - + Screenshots Path: - + ... ... - + Select Screenshots Path... - + <System> <System> @@ -3940,7 +4085,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify Xác nhận @@ -4027,7 +4172,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified @@ -4042,17 +4187,36 @@ Drag points to change position, or double-click table cells to edit values. - + + Unverified, please click Verify before saving configuration + Tooltip + + + + + Verifying... - + + Verified + Tooltip + + + + + Verification failed + Tooltip + Xác nhận không thành công + + + Verification failed Xác nhận không thành công - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. @@ -4121,12 +4285,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting - + Connect @@ -4134,908 +4298,910 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>Dữ liệu ẩn danh được thu thập</a>để hỗ trợ cải thiện yuzu. <br/><br/>Bạn có muốn chia sẽ dữ liệu sử dụng cho chúng tôi? - + Telemetry Viễn trắc - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Tốc độ giả lập hiện tại. Giá trị cao hơn hoặc thấp hơn 100% chỉ ra giả lập sẽ chạy nhanh hơn hoặc chậm hơn trên máy Switch - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Có bao nhiêu khung hình trên mỗi giây mà trò chơi đang hiển thị. Điều này sẽ thay đổi từ trò chơi này đến trò chơi kia và khung cảnh này đến khung cảnh kia. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Thời gian mà giả lập lấy từ khung hình Switch, sẽ không kể đến giới hạn khung hình hoặc v-sync. Đối với tốc độ tối đa mà giả lập nhận được nhiều nhất là ở độ khoảng 16.67 ms. - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files - + &Continue - + &Pause &Tạm dừng - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Warning Outdated Game Format Chú ý định dạng trò chơi đã lỗi thời - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. Bạn đang sử dụng định dạng danh mục ROM giải mã cho trò chơi này, và đó là một định dạng lỗi thời đã được thay thế bởi những thứ khác như NCA, NAX, XCI, hoặc NSP. Danh mục ROM giải mã có thể thiếu biểu tượng, metadata, và hỗ trợ cập nhật.<br><br>Để giải thích về các định dạng khác nhau của Switch mà yuzu hỗ trợ, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>vui lòng kiểm tra trên wiki của chúng tôi</a>. Thông báo này sẽ không hiển thị lại lần sau. - - + + Error while loading ROM! Xảy ra lỗi khi đang nạp ROM! - + The ROM format is not supported. Định dạng ROM này không hỗ trợ. - + An error occurred initializing the video core. Đã xảy ra lỗi khi khởi tạo lõi video. - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. Đã xảy ra lỗi không xác định. Vui lòng kiểm tra sổ ghi chép để biết thêm chi tiết. - + (64-bit) - + (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Save Data - + Mod Data - + Error Opening %1 Folder Xảy ra lỗi khi mở %1 thư mục - - + + Folder does not exist! Thư mục này không tồn tại! - + Error Opening Transferable Shader Cache - + Failed to create the shader cache directory for this title. - - Contents - - - - - Update - Cập nhật - - - - DLC - - - - - Remove Entry - - - - - Remove Installed Game %1? - - - - - - - - - Successfully Removed + Error Removing Contents - - Successfully removed the installed base game. - - - - - - - Error Removing %1 - - - - - The base game is not installed in the NAND and cannot be removed. - - - - - Successfully removed the installed update. + + Error Removing Update + Error Removing DLC + + + + + Remove Installed Game Contents? + + + + + Remove Installed Game Update? + + + + + Remove Installed Game DLC? + + + + + Remove Entry + + + + + + + + + + Successfully Removed + + + + + Successfully removed the installed base game. + + + + + The base game is not installed in the NAND and cannot be removed. + + + + + Successfully removed the installed update. + + + + There is no update installed for this title. - + There are no DLC installed for this title. - + Successfully removed %1 installed DLC. - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove File - - + + Error Removing Transferable Shader Cache - - + + A shader cache for this title does not exist. - + Successfully removed the transferable shader cache. - + Failed to remove the transferable shader cache. - - + + Error Removing Transferable Shader Caches - + Successfully removed the transferable shader caches. - + Failed to remove the transferable shader cache directory. - - + + Error Removing Custom Configuration - + A custom configuration for this title does not exist. - + Successfully removed the custom game configuration. - + Failed to remove the custom game configuration. - - + + RomFS Extraction Failed! Khai thác RomFS không thành công! - + There was an error copying the RomFS files or the user cancelled the operation. Đã xảy ra lỗi khi sao chép tệp tin RomFS hoặc người dùng đã hủy bỏ hoạt động này. - + Full - + Skeleton Sườn - + Select RomFS Dump Mode Chọn chế độ kết xuất RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Vui lòng chọn RomFS mà bạn muốn kết xuất như thế nào.<br>Đầy đủ sẽ sao chép toàn bộ tệp tin vào một danh mục mới trong khi <br>bộ xương chỉ tạo kết cấu danh mục. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Khai thác RomFS... - - + + Cancel Hủy bỏ - + RomFS Extraction Succeeded! Khai thác RomFS thành công! - + The operation completed successfully. Các hoạt động đã hoàn tất thành công. - + Error Opening %1 - + Select Directory Chọn danh mục - + Properties Thuộc tính - + The game properties could not be loaded. Thuộc tính của trò chơi không thể nạp được. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Thực thi Switch (%1);;Tất cả tệp tin (*.*) - + Load File Nạp tệp tin - + Open Extracted ROM Directory Mở danh mục ROM đã trích xuất - + Invalid Directory Selected Danh mục đã chọn không hợp lệ - + The directory you have selected does not contain a 'main' file. Danh mục mà bạn đã chọn không có chứa tệp tin 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Những tệp tin Switch cài được (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... Đang cài đặt tệp tin "%1"... - - + + Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Hệ thống ứng dụng - + System Archive Hệ thống lưu trữ - + System Application Update Cập nhật hệ thống ứng dụng - + Firmware Package (Type A) Gói phần mềm (Loại A) - + Firmware Package (Type B) Gói phần mềm (Loại B) - + Game Trò chơi - + Game Update Cập nhật trò chơi - + Game DLC Nội dung trò chơi có thể tải xuống - + Delta Title Tiêu đề Delta - + Select NCA Install Type... Chọn loại NCA để cài đặt... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Vui lòng chọn loại tiêu đề mà bạn muốn cài đặt NCA này: (Trong hầu hết trường hợp, chọn mặc định 'Game' là tốt nhất.) - + Failed to Install Cài đặt đã không thành công - + The title type you selected for the NCA is invalid. Loại tiêu đề NCA mà bạn chọn nó không hợp lệ. - + File not found Không tìm thấy tệp tin - + File "%1" not found Không tìm thấy "%1" tệp tin - + OK OK - + + Hardware requirements not met + + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + + + + Missing yuzu Account Thiếu tài khoản yuzu - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. Để gửi trường hợp thử nghiệm trò chơi tương thích, bạn phải liên kết tài khoản yuzu.<br><br/>Để liên kết tải khoản yuzu của bạn, hãy đến Giả lập &gt; Thiết lập &gt; Web. - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - - Error - - - - - - The current game is not looking for amiibos - - - - - + + Amiibo - - + + The current amiibo has been removed - + + Error + + + + + + The current game is not looking for amiibos + + + + Amiibo File (%1);; All Files (*.*) Tệp tin Amiibo (%1);; Tất cả tệp tin (*.*) - + Load Amiibo Nạp dữ liệu Amiibo - - Error opening Amiibo data file - Xảy ra lỗi khi mở dữ liệu tệp tin Amiibo - - - - Unable to open Amiibo file "%1" for reading. - Không thể mở tệp tin Amiibo "%1" để đọc. - - - - Error reading Amiibo data file - Xảy ra lỗi khi đọc dữ liệu tệp tin Amiibo - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - Hoàn toàn không thể đọc được dữ liệu Amiibo. Dự kiến byte sẽ đọc là %1, nhưng byte chỉ có thể đọc là %2. - - - + Error loading Amiibo data Xảy ra lỗi khi nạp dữ liệu Amiibo - - Unable to load Amiibo data. - Không thể nạp dữ liệu Amiibo. - - - - Capture Screenshot - Chụp ảnh màn hình - - - - PNG Image (*.png) - Hình ảnh PNG (*.png) - - - - TAS state: Running %1/%2 + + The selected file is not a valid amiibo - - TAS state: Recording %1 + + The selected file is already on use - - TAS state: Idle %1/%2 + + An unknown error occurred + Capture Screenshot + Chụp ảnh màn hình + + + + PNG Image (*.png) + Hình ảnh PNG (*.png) + + + + TAS state: Running %1/%2 + + + + + TAS state: Recording %1 + + + + + TAS state: Idle %1/%2 + + + + TAS State: Invalid - + &Stop Running - + &Start &Bắt đầu - + Stop R&ecording - + R&ecord - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% Tốc độ: %1% / %2% - + Speed: %1% Tốc độ: %1% - + Game: %1 FPS (Unlocked) - + Game: %1 FPS Trò chơi: %1 FPS - + Frame: %1 ms Khung hình: %1 ms - + GPU NORMAL - + GPU HIGH - + GPU EXTREME - + GPU ERROR - + DOCKED - + HANDHELD - + NEAREST - - + + BILINEAR - + BICUBIC - + GAUSSIAN - + SCALEFORCE - + FSR - - + + NO AA - + FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. Trò chơi bạn muốn chạy yêu cầu một số tệp tin được sao chép từ thiết từ máy Switch của bạn trước khi bắt đầu chơi.<br/><br/>Để biết thêm thông tin về cách sao chép những tệp tin đó, vui lòng tham khảo những wiki sau: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Sao chép dữ liệu hệ thống và font dùng chung từ máy Switch</a>.<br/><br/>Bạn có muốn trở về danh sách trò chơi? Nếu bạn vẫn tiếp tục thì trò chơi có thể gặp sự cố, dữ liệu lưu tiến trình có thể bị lỗi, hoặc bạn sẽ gặp những lỗi khác. - + yuzu was unable to locate a Switch system archive. %1 - + yuzu was unable to locate a Switch system archive: %1. %2 - + System Archive Not Found Không tìm thấy tệp tin hệ thống - + System Archive Missing Bị thiếu tệp tin hệ thống - + yuzu was unable to locate the Switch shared fonts. %1 yuzu không thể tìm thấy vị trí font dùng chung của Switch. %1 - + Shared Fonts Not Found Không tìm thấy font dùng chung - + Shared Font Missing Bị thiếu font dùng chung - + Fatal Error Lỗi nghiêm trọng - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu đã xảy ra lỗi nghiêm trọng, vui lòng kiểm tra sổ ghi chép để biết thêm chi tiết. Để biết thêm thông tin về cách truy cập sổ ghi chép, vui lòng xem trang sau: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Làm sao để tải tệp tin sổ ghi chép lên</a>.<br/><br/>Bạn có muốn trở về danh sách game? Tiếp tục có thể khiến giả lập gặp sự cố, gây hỏng dữ liệu đã lưu, hoặc gây các lỗi khác. - + Fatal Error encountered - + Confirm Key Rederivation Xác nhận mã khóa Rederivation - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5052,37 +5218,37 @@ và phải tạo ra một bản sao lưu lại. Điều này sẽ xóa mã khóa tự động tạo trên tệp tin của bạn và chạy lại mô-đun mã khóa derivation. - + Missing fuses - + - Missing BOOT0 - + - Missing BCPKG2-1-Normal-Main - + - Missing PRODINFO - + Derivation Components Missing - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5091,39 +5257,39 @@ on your system's performance. vào hiệu suất hệ thống của bạn. - + Deriving Keys Mã khóa xuất phát - + Select RomFS Dump Target Chọn thư mục để sao chép RomFS - + Please select which RomFS you would like to dump. Vui lòng chọn RomFS mà bạn muốn sao chép. - + Are you sure you want to close yuzu? Bạn có chắc chắn muốn đóng yuzu? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Bạn có chắc rằng muốn dừng giả lập? Bất kì tiến trình nào chưa được lưu sẽ bị mất. - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5135,38 +5301,38 @@ Bạn có muốn bỏ qua yêu cầu đó và thoát luôn không? GRenderWindow - + OpenGL not available! Không có sẵn OpenGL! - + yuzu has not been compiled with OpenGL support. - - + + Error while initializing OpenGL! Đã xảy ra lỗi khi khởi tạo OpenGL! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. - + Error while initializing OpenGL 4.6! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 @@ -5174,153 +5340,153 @@ Bạn có muốn bỏ qua yêu cầu đó và thoát luôn không? GameList - + Favorite - + Start Game - + Start Game without Custom Configuration - + Open Save Data Location Mở vị trí lưu dữ liệu - + Open Mod Data Location Mở vị trí chỉnh sửa dữ liệu - + Open Transferable Pipeline Cache - + Remove Gỡ Bỏ - + Remove Installed Update - + Remove All Installed DLC - + Remove Custom Configuration - + Remove OpenGL Pipeline Cache - + Remove Vulkan Pipeline Cache - + Remove All Pipeline Caches - + Remove All Installed Contents - + Dump RomFS Kết xuất RomFS - + Dump RomFS to SDMC - + Copy Title ID to Clipboard Sao chép ID tiêu đề vào bộ nhớ tạm - + Navigate to GameDB entry Điều hướng đến mục cơ sở dữ liệu trò chơi - + Properties Thuộc tính - + Scan Subfolders - + Remove Game Directory - + ▲ Move Up - + ▼ Move Down - + Open Directory Location - + Clear Bỏ trống - + Name Tên - + Compatibility Tương thích - + Add-ons Tiện ích ngoài - + File type Loại tệp tin - + Size Kích cỡ @@ -5329,76 +5495,61 @@ Bạn có muốn bỏ qua yêu cầu đó và thoát luôn không? GameListItemCompat + Ingame + + + + + Game starts, but crashes or major glitches prevent it from being completed. + + + + Perfect Tốt nhất - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - Có thể chơi một cách mượt mà mà không có lỗi âm thanh hoặc đồ họa nào, tất cả các chức năng đều hoạt động như ý mà không cần bất kì tinh chỉnh nào. - - - - Great - Tốt - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - Có thể chơi từ đầu đến cuối nhưng vẫn có một số lỗi nhỏ về đồ họa hoặc âm thanh. Có thể sẽ cần tới một vài tinh chỉnh nào đó. - - Okay - Tạm ổn - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - Tạm chơi được nhưng có lỗi đồ họa hoặc âm thanh một cách đáng kể, tuy vậy vẫn có thể chơi hết từ đầu đến cuối với một số tinh chỉnh. + Game can be played without issues. + - Bad - Không tốt + Playable + - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - Chơi được nhưng có nhiều lỗi đồ họa hoặc âm thanh. Không thể chơi tiếp ở một số nơi nào đó do lỗi dù có tinh chỉnh thế nào đi nữa. + Game functions with minor graphical or audio glitches and is playable from start to finish. + - + Intro/Menu Phần mở đầu/Menu - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - Hoàn toàn không thể chơi được do có nhiều lỗi đồ họa hoặc âm thanh. Không thể chơi tiếp sau khi bấm nút bắt đầu trò chơi. + + Game loads, but is unable to progress past the Start Screen. + - + Won't Boot Không hoạt động - + The game crashes when attempting to startup. Trò chơi sẽ thoát đột ngột khi khởi động. - + Not Tested Chưa ai thử - + The game has not yet been tested. Trò chơi này chưa có ai thử cả. @@ -5406,7 +5557,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list @@ -5419,12 +5570,12 @@ Screen. - + Filter: Bộ lọc: - + Enter pattern to filter Nhập khuôn để lọc @@ -5500,12 +5651,12 @@ Screen. HostRoomWindow - + Error - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: @@ -5514,11 +5665,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute + @@ -5540,112 +5692,111 @@ Debug Message: - Main Window - + Audio Volume Down - + Audio Volume Up - + Capture Screenshot Chụp ảnh màn hình - + Change Adapting Filter - + Change Docked Mode - + Change GPU Accuracy - + Continue/Pause Emulation - + Exit Fullscreen - + Exit yuzu - + Fullscreen Toàn màn hình - + Load File Nạp tệp tin - + Load/Remove Amiibo - + Restart Emulation - + Stop Emulation - + TAS Record - + TAS Reset - + TAS Start/Stop - + Toggle Filter Bar - + Toggle Framerate Limit - + Toggle Mouse Panning - + Toggle Status Bar @@ -5759,42 +5910,42 @@ Debug Message: - + Password Required to Join - + Password: - - Room Name - - - - - Preferred Game - - - - - Host - - - - + Players Người chơi - + + Room Name + + + + + Preferred Game + + + + + Host + + + + Refreshing - + Refresh List @@ -5817,232 +5968,237 @@ Debug Message: - + &Emulation &Giả lập - + &View &Xem - + &Reset Window Size - + &Debugging - + Reset Window Size to &720p - + Reset Window Size to 720p - + Reset Window Size to &900p - + Reset Window Size to 900p - + Reset Window Size to &1080p - + Reset Window Size to 1080p - + + &Multiplayer + + + + &Tools - + &TAS - + &Help &Trợ giúp - + &Install Files to NAND... - + L&oad File... - + Load &Folder... - + E&xit Th&oát - + &Pause &Tạm dừng - + &Stop &Dừng - + &Reinitialize keys... - + &About yuzu - + Single &Window Mode - + Con&figure... - + Display D&ock Widget Headers - + Show &Filter Bar - + Show &Status Bar - + Show Status Bar Hiển thị thanh trạng thái - - - Browse Public Game Lobby - - - - - Create Room - - - Leave Room + &Browse Public Game Lobby - - Direct Connect to Room + + &Create Room - - Show Current Room + + &Leave Room - F&ullscreen + &Direct Connect to Room - &Restart + &Show Current Room - Load/Remove &Amiibo... + F&ullscreen - &Report Compatibility + &Restart + Load/Remove &Amiibo... + + + + + &Report Compatibility + + + + Open &Mods Page - + Open &Quickstart Guide - + &FAQ - + Open &yuzu Folder - + &Capture Screenshot - + &Configure TAS... - + Configure C&urrent Game... - + &Start &Bắt đầu - + &Reset - + R&ecord @@ -6107,46 +6263,41 @@ Debug Message: MultiplayerState - - + Current connection status - - + Not Connected. Click here to find a room! - - - Connected - Đã kết nối - - - - Not Connected - + + Connected + Đã kết nối + + + + New Messages Received + + + + Error - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: - - - New Messages Received - - NetworkMessage @@ -6247,22 +6398,39 @@ They may have left the room. - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + + + + + Game already running + + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + + + + Leave Room - + You are about to close the room. Any network connections will be closed. - + Disconnect - + You are about to leave the room. Any network connections will be closed. @@ -6270,7 +6438,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error @@ -6315,42 +6483,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game - + %1 is playing %2 - + Not playing a game - + Installed SD Titles - + Installed NAND Titles - + System Titles - + Add New Game Directory - + Favorites @@ -6380,7 +6548,7 @@ p, li { white-space: pre-wrap; } - + [not set] [chưa đặt nút] @@ -6395,10 +6563,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Trục %1%2 @@ -6412,9 +6580,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [không xác định] @@ -6579,15 +6747,15 @@ p, li { white-space: pre-wrap; } - + [invalid] - - + + %1%2Hat %3 @@ -6595,35 +6763,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 - + %1%2Axis %3,%4,%5 - + %1%2Motion %3 - - + + %1%2Button %3 - + [unused] [không sử dụng] @@ -6664,11 +6832,124 @@ p, li { white-space: pre-wrap; } - + %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + + + + + Amiibo Info + + + + + Series + + + + + Type + + + + + Name + Tên + + + + Amiibo Data + + + + + Custom Name + + + + + Owner + + + + + Creation Date + + + + + dd/MM/yyyy + + + + + Modification Date + + + + + dd/MM/yyyy + + + + + Game Data + + + + + Game Id + + + + + Mount Amiibo + + + + + ... + ... + + + + File Path + + + + + No game data present + + + + + The following amiibo data will be formatted: + + + + + The following game data will removed: + + + + + Set nickname and owner: + + + + + Do you wish to restore this amiibo? + + + QtControllerSelectorDialog @@ -6772,6 +7053,7 @@ p, li { white-space: pre-wrap; } + Handheld Cầm tay @@ -6811,11 +7093,6 @@ p, li { white-space: pre-wrap; } Docked Chế độ cắm TV - - - Undocked - Chế độ cầm tay - Vibration diff --git a/dist/languages/zh_CN.ts b/dist/languages/zh_CN.ts index a70052b..3e191a1 100644 --- a/dist/languages/zh_CN.ts +++ b/dist/languages/zh_CN.ts @@ -95,78 +95,78 @@ p, li { white-space: pre-wrap; } 发送消息 - + Members 成员 - + %1 has joined %1 已加入 - + %1 has left %1 已离开 - + %1 has been kicked %1 已被踢出房间 - + %1 has been banned %1 已被封禁 - + %1 has been unbanned %1 已被解封 - + View Profile 查看个人资料 - - + + Block Player 屏蔽玩家 - + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - 当你屏蔽玩家后,你将无法收到他们的聊天消息。<br><br>您确定要屏蔽 %1 吗? + 屏蔽玩家后,你将无法收到他们的聊天消息。<br><br>您确定要屏蔽 %1 吗? - + Kick 踢出房间 - + Ban 封禁 - + Kick Player 踢出玩家 - + Are you sure you would like to <b>kick</b> %1? 您确定要将 %1 <b>踢出房间</b>吗? - + Ban Player 封禁玩家 - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. @@ -212,8 +212,8 @@ This would ban both their forum username and their IP address. - %1 (%2/%3 members) - connected - %1 (%2/%3 玩家) - 已连接 + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 个成员) - 已连接 @@ -226,6 +226,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility 报告游戏兼容性 @@ -235,92 +240,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">如果您选择向 </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">yuzu 兼容性列表</span></a><span style=" font-size:10pt;">提交测试用例的话,以下信息将会被收集并显示在网站上:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">设备硬件信息 (CPU / GPU / 操作系统)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">您正在使用的 yuzu 版本</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">已关联的 yuzu 账户信息</li></ul></body></html> - - Perfect - 完美 + + <html><head/><body><p>Does the game boot?</p></body></html> + <html><head/><body><p>游戏启动了吗?</p></body></html> - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>游戏运行完美,没有音频或图形问题。</p></body></html> + + Yes The game starts to output video or audio + 是的,游戏开始输出视频或音频 - - Great - 良好 + + No The game doesn't get past the "Launching..." screen + 不,游戏无法通过“启动中…”页面 - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>游戏运行时会有非常轻微的图像或音频问题,但是能从头玩到尾。可能需要一些技巧才能完成游戏。</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + 是的,游戏出现开场/菜单页面并进入游戏 - - Okay - 一般 + + No The game crashes or freezes while loading or using the menu + 不,加载或使用菜单时游戏崩溃或卡死 - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>游戏运行时会有很多图像或音频错误,但是在使用一些特殊技巧之后能完整地完成游戏。</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + <html><head/><body><p>游戏是否具有游戏性?</p></body></html> - - Bad - 较差 + + Yes The game works without crashes + 是的,游戏运行时没有崩溃 - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>游戏能运行,但是会有大量图像或音频错误。即使使用一些技巧也无法通过游戏的某些区域。</p></body></html> + + No The game crashes or freezes during gameplay + 不,游戏运行时出现卡死或崩溃 - - Intro/Menu - 开场 / 菜单 + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + <html><head/><body><p>游戏在运行时有没有崩溃、卡死或出现软锁?</p></body></html> - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>游戏完全没法玩,图像或音频有重大错误。通过开场菜单后无法继续。</p></body></html> + + Yes The game can be finished without any workarounds + 没有,可以顺利地完成游戏过程 - - Won't Boot - 无法打开 + + No The game can't progress past a certain area + 有,游戏在特定区段无法继续 - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>在启动游戏时直接崩溃了。</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + <html><head/><body><p>游戏从头到尾都可以顺利运行吗?</p></body></html> - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <body><html><head/><p>在不考虑速度或帧率的情况下,使用此版本 yuzu 玩这款游戏的情况如何?</p></body></html> + + Major The game has major graphical errors + 严重 游戏在运行时有严重的图形错误 - + + Minor The game has minor graphical errors + 轻微 游戏在运行时有轻微的图形错误 + + + + None Everything is rendered as it looks on the Nintendo Switch + 完美 媲美实机的游戏体验 + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + <html><head/><body><p>游戏运行时出现了图形问题吗?</p></body></html> + + + + Major The game has major audio errors + 严重 游戏运行时出现严重的音频错误 + + + + Minor The game has minor audio errors + 轻微 游戏运行时出现轻微的音频错误 + + + + None Audio is played perfectly + 完美 游戏运行时音频未出现任何问题 + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + <html><head/><body><p>游戏是否出现音频故障/效果缺失?</p></body></html> + + + Thank you for your submission! 感谢您向我们提交信息! - + Submitting 提交中 - + Communication error 网络错误 - + An error occurred while sending the Testcase 在提交测试用例时发生错误。 - + Next 下一步 @@ -418,7 +458,7 @@ This would ban both their forum username and their IP address. 恢复默认 - + Auto 自动 @@ -768,200 +808,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger 调试器 - + Enable GDB Stub 开启 GDB 调试 - + Port: 端口: - + Logging 日志 - + Global Log Filter 全局日志过滤器 - + Show Log in Console 显示日志窗口 - + Open Log Location 打开日志位置 - + When checked, the max size of the log increases from 100 MB to 1 GB 选中此项后,日志文件的最大大小从 100MB 增加到 1GB - + Enable Extended Logging** 启用扩展的日志记录** - + Homebrew 自制游戏 - + Arguments String 参数字符串 - + Graphics 图形 - + When checked, the graphics API enters a slower debugging mode 启用时,图形 API 将进入较慢的调试模式。 - + Enable Graphics Debugging 启用图形调试 - + When checked, it enables Nsight Aftermath crash dumps 启用后,yuzu 将会保存 Nsight Aftermath 格式的崩溃转储文件 - + Enable Nsight Aftermath 启用 Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found 启用时,将从磁盘着色器缓存或游戏中转储所有的着色器文件。 - + Dump Game Shaders 转储着色器文件 - + When checked, it will dump all the macro programs of the GPU 选中后,将转储 GPU 的所有宏程序 - + Dump Maxwell Macros 转储 Maxwell 宏 - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower 启用时,将禁用宏即时编译器。这会降低游戏运行速度。 - + Disable Macro JIT 禁用宏 JIT - + When checked, yuzu will log statistics about the compiled pipeline cache 选中时,yuzu 将记录有关已编译着色器缓存的统计信息。 - + Enable Shader Feedback 启用着色器反馈 - + When checked, it executes shaders without loop logic changes 启用后,yuzu 在执行着色器时,不会修改循环结构的条件判断 - + Disable Loop safety checks 禁用循环体安全检查 - + Debugging 调试选项 - - Enable FS Access Log - 启用文件系统访问记录 - - - - Dump Audio Commands To Console** - 将音频命令转储至控制台** - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - 启用此选项会将最新的音频命令列表输出到控制台。只影响使用音频渲染器的游戏。 - - - + Enable Verbose Reporting Services** 启用详细报告服务** - + + Enable FS Access Log + 启用文件系统访问记录 + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + 启用此选项会将最新的音频命令列表输出到控制台。只影响使用音频渲染器的游戏。 + + + + Dump Audio Commands To Console** + 将音频命令转储至控制台** + + + + Create Minidump After Crash + 微型故障转储 + + + Advanced 高级选项 - + Kiosk (Quest) Mode Kiosk (Quest) 模式 - + Enable CPU Debugging 启用 CPU 模拟调试 - + Enable Debug Asserts 启用调试 - + Enable Auto-Stub** 启用自动函数打桩(Auto-Stub)** - + Enable All Controller Types 启用其他类型的控制器 - + Disable Web Applet 禁用 Web 应用程序 - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + 允许 yuzu 在启动时检查 Vulkan 环境是否正常工作。如果是其他程序导致 yuzu 出现相关问题,请禁用此选项。 + + + + Perform Startup Vulkan Check + 启动时进行 Vulkan 检测 + + + **This will be reset automatically when yuzu closes. **该选项将在 yuzu 关闭时自动重置。 + + + Restart Required + 需要重启 + + + + yuzu is required to restart in order to apply this setting. + 重启 yuzu 后才能应用此设置。 + + + + Web applet not compiled + Web 应用程序未编译 + + + + MiniDump creation not compiled + 微型转储未编译 + ConfigureDebugController @@ -1168,7 +1243,7 @@ This would ban both their forum username and their IP address. Caching - 缓存中 + 缓存 @@ -1331,193 +1406,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings 图形设置 - + Use disk pipeline cache 启用磁盘着色器缓存 - + Use asynchronous GPU emulation 使用 GPU 异步模拟 - + Accelerate ASTC texture decoding 加速 ASTC 格式材质解码 - + NVDEC emulation: NVDEC 模拟方式: - + No Video Output 无视频输出 - + CPU Video Decoding CPU 视频解码 - + GPU Video Decoding (Default) GPU 视频解码 (默认) - + Fullscreen Mode: 全屏模式: - + Borderless Windowed 无边框窗口 - + Exclusive Fullscreen 独占全屏 - + Aspect Ratio: 屏幕纵横比: - + Default (16:9) 默认 (16:9) - + Force 4:3 强制 4:3 - + Force 21:9 强制 21:9 - + + Force 16:10 + 强制 16:10 + + + Stretch to Window 拉伸窗口 - + Resolution: 画面分辨率: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [实验性] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [实验性] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: 窗口滤镜: - + Nearest Neighbor 近邻取样 - + Bilinear 双线性过滤 - + Bicubic 双三线过滤 - + Gaussian 高斯模糊 - + ScaleForce 强制缩放 - + AMD FidelityFX™️ Super Resolution (Vulkan Only) - AMD FidelityFX™️ 超高清技术 (仅限 Vulkan 模式) + AMD FidelityFX™️ 超级分辨率锐画技术 (仅限 Vulkan 模式) - + Anti-Aliasing Method: 抗锯齿方式: - + None - + FXAA 快速近似抗锯齿 - - + + Use global FSR Sharpness + 启用全局 FSR 锐化 + + + + Set FSR Sharpness + 设置 FSR 锐化 + + + + FSR Sharpness: + FSR 锐化度: + + + + 100% + 100% + + + + Use global background color 使用全局背景颜色 - + Set background color: 设置背景颜色: - + Background Color: 背景颜色: @@ -1526,6 +1626,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM(汇编着色器,仅限 NVIDIA 显卡) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1580,37 +1686,47 @@ This would ban both their forum username and their IP address. 启用快速 GPU 时钟 (不稳定) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + 启用悲观缓冲区刷新。此选项将强制刷新未修改的缓冲区,可能会降低性能。 + + + + Use pessimistic buffer flushes (Hack) + 启用悲观缓冲区刷新 (不稳定) + + + Anisotropic Filtering: 各向异性过滤: - + Automatic 自动 - + Default 系统默认 - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2102,7 +2218,7 @@ This would ban both their forum username and their IP address. - + Left Stick 左摇杆 @@ -2196,14 +2312,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2222,7 +2338,7 @@ This would ban both their forum username and their IP address. - + Plus @@ -2235,15 +2351,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2300,231 +2416,236 @@ This would ban both their forum username and their IP address. - + Right Stick 右摇杆 - - - - + + + + Clear 清除 - - - - - + + + + + [not set] [未设置] - - - Toggle button - 切换按键 - - - - + + Invert button 反转按钮 - - + + + Toggle button + 切换按键 + + + + Invert axis 体感方向倒置 - - - + + + Set threshold 阈值设定 - - + + Choose a value between 0% and 100% 选择一个介于 0% 和 100% 之间的值 - + + Toggle axis + 切换轴 + + + Set gyro threshold 陀螺仪阈值设定 - + Map Analog Stick 映射摇杆 - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. 在按下确定后,首先水平移动你的手柄,然后垂直移动它。 如果要使体感方向倒置,首先垂直移动你的手柄,然后水平移动它。 - + Center axis 中心轴 - - + + Deadzone: %1% 摇杆死区:%1% - - + + Modifier Range: %1% 摇杆灵敏度:%1% - - + + Pro Controller Pro Controller - + Dual Joycons 双 Joycons 手柄 - + Left Joycon 左 Joycon 手柄 - + Right Joycon 右 Joycon 手柄 - + Handheld 掌机模式 - + GameCube Controller GameCube 控制器 - + Poke Ball Plus 精灵球 PLUS - + NES Controller NES 控制器 - + SNES Controller SNES 控制器 - + N64 Controller N64 控制器 - + Sega Genesis 世嘉创世纪 - + Start / Pause 开始 / 暂停 - + Z Z - + Control Stick 控制摇杆 - + C-Stick C 摇杆 - + Shake! 摇动! - + [waiting] [等待中] - + New Profile 保存自定义设置 - + Enter a profile name: 输入配置文件名称: - - + + Create Input Profile 新建输入配置文件 - + The given profile name is not valid! 输入的配置文件名称无效! - + Failed to create the input profile "%1" 新建输入配置文件 "%1" 失败 - + Delete Input Profile 删除输入配置文件 - + Failed to delete the input profile "%1" 删除输入配置文件 "%1" 失败 - + Load Input Profile 加载输入配置文件 - + Failed to load the input profile "%1" 加载输入配置文件 "%1" 失败 - + Save Input Profile 保存输入配置文件 - + Failed to save the input profile "%1" 保存输入配置文件 "%1" 失败 @@ -2779,42 +2900,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 开发商 - + Add-Ons 附加项 - + General 通用 - + System 系统 - + CPU CPU - + Graphics 图形 - + Adv. Graphics 高级图形 - + Audio 声音 - + Properties 属性 @@ -2870,37 +2991,37 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 当前用户 - + Username 用户名 - + Set Image 设置图像 - + Add 添加 - + Rename 重命名 - + Remove 移除 - + Profile management is available only when game is not running. 只有当游戏不在运行时,才能进行用户配置的管理。 - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2908,96 +3029,106 @@ To invert the axes, first move your joystick vertically, and then horizontally.< %2 - + Enter Username 输入用户名 - + Users 用户 - + Enter a username for the new user: 输入新用户的用户名: - + Enter a new username: 输入新的用户名: - - Confirm Delete - 确认删除 - - - - You are about to delete user with name "%1". Are you sure? - 您确定要删除用户名为 “%1” 的用户吗? - - - + Select User Image 选择用户图像 - + JPEG Images (*.jpg *.jpeg) JPEG 图像 (*.jpg *.jpeg) - + Error deleting image 删除图像时出错 - + Error occurred attempting to overwrite previous image at: %1. 尝试覆盖该用户的现有图像时出错: %1 - + Error deleting file 删除文件时出错 - + Unable to delete existing file: %1. 无法删除文件: %1 - + Error creating user image directory 创建用户图像目录时出错 - + Unable to create directory %1 for storing user images. 无法创建存储用户图像的目录 %1 。 - + Error copying user image 复制用户图像时出错 - + Unable to copy image from %1 to %2 无法将图像从 %1 复制到 %2 - + Error resizing user image 调整用户图像大小时出错 - + Unable to resize image 无法调整图像的大小 + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + 删除此用户?此用户保存的所有数据都将被删除。 + + + + Confirm Delete + 确认删除 + + + + Name: %1 +UUID: %2 + 名称: %1 +UUID: %2 + + ConfigureRingController @@ -3526,47 +3657,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< <html><head/><body><p>通过读取与 TAS-nx 脚本具有相同格式的脚本来读取控制器的输入。<br/>有关详细信息,请参阅 yuzu 官方网站的<a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">帮助页面</span></a>。</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). 要确认是哪些热键控制播放/录制,请参阅热键设置。(设置—>通用—>热键) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. 警告:这是一个实验性功能。<br/>由于暂不完善的同步方式,可能无法完整地进行回放。 - + Settings 设置 - + Enable TAS features 启用 TAS 功能 - + Loop script 循环脚本 - + Pause execution during loads 遇到载入画面时暂停执行 - + Script Directory 脚本目录 - + Path 路径 - + ... ... @@ -3818,56 +3949,71 @@ Drag points to change position, or double-click table cells to edit values. + Show Compatibility List + 显示兼容性列表 + + + Show Add-Ons Column 显示“附加项”列 - + + Show Size Column + 显示“文件大小”列 + + + + Show File Types Column + 显示“文件类型”列 + + + Game Icon Size: 游戏图标大小: - + Folder Icon Size: 文件夹图标大小: - + Row 1 Text: 第一行: - + Row 2 Text: 第二行: - + Screenshots 捕获截图 - + Ask Where To Save Screenshots (Windows Only) 询问保存截图的位置 (仅限 Windows ) - + Screenshots Path: 截图保存位置: - + ... ... - + Select Screenshots Path... 选择截图保存位置... - + <System> <System> @@ -3976,7 +4122,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify 验证 @@ -4003,7 +4149,7 @@ Drag points to change position, or double-click table cells to edit values. Web Service configuration can only be changed when a public room isn't being hosted. - 公共房间未被开放时,才能更改网络服务设置。 + 公共房间未被创建时,才能更改网络服务设置。 @@ -4063,7 +4209,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified 未指定 @@ -4078,17 +4224,36 @@ Drag points to change position, or double-click table cells to edit values.令牌未被验证。您对用户名和令牌的更改尚未保存。 - + + Unverified, please click Verify before saving configuration + Tooltip + 令牌未验证,请在保存配置前先进行验证。 + + + + Verifying... 验证中... - + + Verified + Tooltip + 已验证 + + + + Verification failed + Tooltip + 验证失败 + + + Verification failed 验证失败 - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. 验证失败。请检查您输入的令牌是否正确,并且确保您的互联网连接正常。 @@ -4157,12 +4322,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting 连接中 - + Connect 连接 @@ -4170,913 +4335,915 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? <a href='https://yuzu-emu.org/help/feature/telemetry/'>我们收集匿名数据</a>来帮助改进 yuzu 。<br/><br/>您愿意和我们分享您的使用数据吗? - + Telemetry 使用数据共享 - + Broken Vulkan Installation Detected 检测到 Vulkan 的安装已损坏 - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. Vulkan 初始化失败。<br><br>点击<a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>这里</a>获取此问题的相关信息。 - + Loading Web Applet... 正在加载 Web 应用程序... - - + + Disable Web Applet 禁用 Web 应用程序 - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) 禁用 Web 应用程序可能会发生未知的行为,且只能在《超级马里奥 3D 全明星》中使用。您确定要禁用 Web 应用程序吗? (您可以在调试选项中重新启用它。) - + The amount of shaders currently being built 当前正在构建的着色器数量 - + The current selected resolution scaling multiplier. 当前选定的分辨率缩放比例。 - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. 当前的模拟速度。高于或低于 100% 的值表示模拟正在运行得比实际 Switch 更快或更慢。 - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 游戏当前运行的帧率。这将因游戏和场景的不同而有所变化。 - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 在不计算速度限制和垂直同步的情况下,模拟一个 Switch 帧的实际时间。若要进行全速模拟,这个数值不应超过 16.67 毫秒。 - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files 清除最近文件 (&C) - + &Continue 继续 (&C) - + &Pause 暂停 (&P) - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu 正在运行中 - + Warning Outdated Game Format 过时游戏格式警告 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. 目前使用的游戏为解体的 ROM 目录格式,这是一种过时的格式,已被其他格式替代,如 NCA,NAX,XCI 或 NSP。解体的 ROM 目录缺少图标、元数据和更新支持。<br><br>有关 yuzu 支持的各种 Switch 格式的说明,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>请查看我们的 wiki</a>。此消息将不会再次出现。 - - + + Error while loading ROM! 加载 ROM 时出错! - + The ROM format is not supported. 该 ROM 格式不受支持。 - + An error occurred initializing the video core. 在初始化视频核心时发生错误。 - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu 在运行视频核心时发生错误。这可能是由 GPU 驱动程序过旧造成的。有关详细信息,请参阅日志文件。关于日志文件的更多信息,请参考以下页面:<a href='https://yuzu-emu.org/help/reference/log-files/'>如何上传日志文件</a>。 - + Error while loading ROM! %1 %1 signifies a numeric error code. 加载 ROM 时出错! %1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>请参考<a href='https://yuzu-emu.org/help/quickstart/'>yuzu 快速导航</a>以获取相关文件。<br>您可以参考 yuzu 的 wiki 页面</a>或 Discord 社区</a>以获得帮助。 - + An unknown error occurred. Please see the log for more details. 发生了未知错误。请查看日志了解详情。 - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data 保存数据 - + Mod Data Mod 数据 - + Error Opening %1 Folder 打开 %1 文件夹时出错 - - + + Folder does not exist! 文件夹不存在! - + Error Opening Transferable Shader Cache 打开可转移着色器缓存时出错 - + Failed to create the shader cache directory for this title. 为该游戏创建着色器缓存目录时失败。 - - Contents - 目录 + + Error Removing Contents + 删除内容时出错 - - Update - 游戏更新 + + Error Removing Update + 删除更新时出错 - - DLC - DLC + + Error Removing DLC + 删除 DLC 时出错 - + + Remove Installed Game Contents? + 删除已安装的游戏内容? + + + + Remove Installed Game Update? + 删除已安装的游戏更新? + + + + Remove Installed Game DLC? + 删除已安装的游戏 DLC 内容? + + + Remove Entry 删除项目 - - Remove Installed Game %1? - 删除已安装的游戏 %1 ? - - - - - - - - + + + + + + Successfully Removed 删除成功 - + Successfully removed the installed base game. 成功删除已安装的游戏。 - - - - Error Removing %1 - 删除 %1 时出错 - - - + The base game is not installed in the NAND and cannot be removed. 该游戏未安装于 NAND 中,无法删除。 - + Successfully removed the installed update. 成功删除已安装的游戏更新。 - + There is no update installed for this title. 这个游戏没有任何已安装的更新。 - + There are no DLC installed for this title. 这个游戏没有任何已安装的 DLC 。 - + Successfully removed %1 installed DLC. 成功删除游戏 %1 安装的 DLC 。 - + Delete OpenGL Transferable Shader Cache? 删除 OpenGL 模式的着色器缓存? - + Delete Vulkan Transferable Shader Cache? 删除 Vulkan 模式的着色器缓存? - + Delete All Transferable Shader Caches? 删除所有的着色器缓存? - + Remove Custom Game Configuration? 移除自定义游戏设置? - + Remove File 删除文件 - - + + Error Removing Transferable Shader Cache 删除着色器缓存时出错 - - + + A shader cache for this title does not exist. 这个游戏的着色器缓存不存在。 - + Successfully removed the transferable shader cache. 成功删除着色器缓存。 - + Failed to remove the transferable shader cache. 删除着色器缓存失败。 - - + + Error Removing Transferable Shader Caches 删除着色器缓存时出错 - + Successfully removed the transferable shader caches. 着色器缓存删除成功。 - + Failed to remove the transferable shader cache directory. 删除着色器缓存目录失败。 - - + + Error Removing Custom Configuration 移除自定义游戏设置时出错 - + A custom configuration for this title does not exist. 这个游戏的自定义设置不存在。 - + Successfully removed the custom game configuration. 成功移除自定义游戏设置。 - + Failed to remove the custom game configuration. 移除自定义游戏设置失败。 - - + + RomFS Extraction Failed! RomFS 提取失败! - + There was an error copying the RomFS files or the user cancelled the operation. 复制 RomFS 文件时出错,或用户取消了操作。 - + Full 完整 - + Skeleton 框架 - + Select RomFS Dump Mode 选择 RomFS 转储模式 - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. 请选择希望 RomFS 转储的方式。<br>“Full” 会将所有文件复制到新目录中,而<br>“Skeleton” 只会创建目录结构。 - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1 没有足够的空间用于提取 RomFS。请保持足够的空间或于模拟—>设置—>系统—>文件系统—>转储根目录中选择一个其他目录。 - + Extracting RomFS... 正在提取 RomFS... - - + + Cancel 取消 - + RomFS Extraction Succeeded! RomFS 提取成功! - + The operation completed successfully. 操作成功完成。 - + Error Opening %1 打开 %1 时出错 - + Select Directory 选择目录 - + Properties 属性 - + The game properties could not be loaded. 无法加载该游戏的属性信息。 - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch 可执行文件 (%1);;所有文件 (*.*) - + Load File 加载文件 - + Open Extracted ROM Directory 打开提取的 ROM 目录 - + Invalid Directory Selected 选择的目录无效 - + The directory you have selected does not contain a 'main' file. 选择的目录不包含 “main” 文件。 - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) 可安装的 Switch 文件 (*.nca *.nsp *.xci);;任天堂内容档案 (*.nca);;任天堂应用包 (*.nsp);;NX 卡带镜像 (*.xci) - + Install Files 安装文件 - + %n file(s) remaining 剩余 %n 个文件 - + Installing file "%1"... 正在安装文件 "%1"... - - + + Install Results 安装结果 - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. 为了避免可能存在的冲突,我们不建议将游戏本体安装到 NAND 中。 此功能仅用于安装游戏更新和 DLC 。 - + %n file(s) were newly installed 最近安装了 %n 个文件 - + %n file(s) were overwritten %n 个文件被覆盖 - + %n file(s) failed to install %n 个文件安装失败 - + System Application 系统应用 - + System Archive 系统档案 - + System Application Update 系统应用更新 - + Firmware Package (Type A) 固件包 (A型) - + Firmware Package (Type B) 固件包 (B型) - + Game 游戏 - + Game Update 游戏更新 - + Game DLC 游戏 DLC - + Delta Title 差量程序 - + Select NCA Install Type... 选择 NCA 安装类型... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) 请选择此 NCA 的程序类型: (在大多数情况下,选择默认的“游戏”即可。) - + Failed to Install 安装失败 - + The title type you selected for the NCA is invalid. 选择的 NCA 程序类型无效。 - + File not found 找不到文件 - + File "%1" not found 文件 "%1" 未找到 - + OK 确定 - + + Hardware requirements not met + 硬件不满足要求 + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + 您的系统不满足运行 yuzu 推荐的推荐配置。兼容性报告已被禁用。 + + + Missing yuzu Account 未设置 yuzu 账户 - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. 要提交游戏兼容性测试用例,您必须设置您的 yuzu 帐户。<br><br/>要设置您的 yuzu 帐户,请转到模拟 &gt; 设置 &gt; 网络。 - + Error opening URL 打开 URL 时出错 - + Unable to open the URL "%1". 无法打开 URL : "%1" 。 - + TAS Recording TAS 录制中 - + Overwrite file of player 1? 覆盖玩家 1 的文件? - + Invalid config detected 检测到无效配置 - + Handheld controller can't be used on docked mode. Pro controller will be selected. 掌机手柄无法在主机模式中使用。将会选择 Pro controller。 - - - Error - 错误 - - - - - The current game is not looking for amiibos - 当前游戏并没有在寻找 Amiibos - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed 当前的 Amiibo 已被移除。 - + + Error + 错误 + + + + + The current game is not looking for amiibos + 当前游戏并没有在寻找 Amiibos + + + Amiibo File (%1);; All Files (*.*) Amiibo 文件 (%1);; 全部文件 (*.*) - + Load Amiibo 加载 Amiibo - - Error opening Amiibo data file - 打开 Amiibo 数据文件时出错 - - - - Unable to open Amiibo file "%1" for reading. - 无法打开 Amiibo 文件 %1。 - - - - Error reading Amiibo data file - 读取 Amiibo 数据文件时出错 - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - 无法完全读取 Amiibo 数据。应读取 %1 个字节,但实际仅能读取 %2 个字节。 - - - + Error loading Amiibo data 加载 Amiibo 数据时出错 - - Unable to load Amiibo data. - 无法加载 Amiibo 数据。 + + The selected file is not a valid amiibo + 选择的文件并不是有效的 amiibo - + + The selected file is already on use + 选择的文件已在使用中 + + + + An unknown error occurred + 发生了未知错误 + + + Capture Screenshot 捕获截图 - + PNG Image (*.png) PNG 图像 (*.png) - + TAS state: Running %1/%2 TAS 状态:正在运行 %1/%2 - + TAS state: Recording %1 TAS 状态:正在录制 %1 - + TAS state: Idle %1/%2 TAS 状态:空闲 %1/%2 - + TAS State: Invalid TAS 状态:无效 - + &Stop Running 停止运行 (&S) - + &Start 开始 (&S) - + Stop R&ecording 停止录制 (&E) - + R&ecord 录制 (&E) - + Building: %n shader(s) 正在编译 %n 个着色器文件 - + Scale: %1x %1 is the resolution scaling factor 缩放比例: %1x - + Speed: %1% / %2% 速度: %1% / %2% - + Speed: %1% 速度: %1% - + Game: %1 FPS (Unlocked) 游戏: %1 FPS (未锁定) - + Game: %1 FPS FPS: %1 - + Frame: %1 ms 帧延迟:%1 毫秒 - + GPU NORMAL GPU NORMAL - + GPU HIGH GPU HIGH - + GPU EXTREME GPU EXTREME - + GPU ERROR GPU ERROR - + DOCKED 主机模式 - + HANDHELD 掌机模式 - + NEAREST 邻近取样 - - + + BILINEAR 双线性过滤 - + BICUBIC 双三线过滤 - + GAUSSIAN 高斯模糊 - + SCALEFORCE 强制缩放 - + FSR FSR - - + + NO AA 抗锯齿关 - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. 您正尝试启动的游戏需要从 Switch 转储的其他文件。<br/><br/>有关转储这些文件的更多信息,请参阅以下 wiki 页面:<a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>。<br/><br/>您要退出并返回至游戏列表吗?继续模拟可能会导致崩溃,存档损坏或其他错误。 - + yuzu was unable to locate a Switch system archive. %1 Yuzu 找不到 Switch 系统档案 %1 - + yuzu was unable to locate a Switch system archive: %1. %2 Yuzu 找不到 Switch 系统档案: %1, %2 - + System Archive Not Found 未找到系统档案 - + System Archive Missing 系统档案缺失 - + yuzu was unable to locate the Switch shared fonts. %1 Yuzu 找不到 Swtich 共享字体 %1 - + Shared Fonts Not Found 未找到共享字体 - + Shared Font Missing 共享字体文件缺失 - + Fatal Error 致命错误 - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu 遇到了致命错误,请查看日志了解详情。有关查找日志的更多信息,请参阅以下页面:<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>。<br/><br/>您要退出并返回至游戏列表吗?继续模拟可能会导致崩溃,存档损坏或其他错误。 - + Fatal Error encountered 发生致命错误 - + Confirm Key Rederivation 确认重新生成密钥 - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5092,37 +5259,37 @@ This will delete your autogenerated key files and re-run the key derivation modu 这将删除您自动生成的密钥文件并重新运行密钥生成模块。 - + Missing fuses 项目丢失 - + - Missing BOOT0 - 丢失 BOOT0 - + - Missing BCPKG2-1-Normal-Main - 丢失 BCPKG2-1-Normal-Main - + - Missing PRODINFO - 丢失 PRODINFO - + Derivation Components Missing 组件丢失 - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> 密钥缺失。<br>请查看<a href='https://yuzu-emu.org/help/quickstart/'>yuzu 快速导航</a>以获得你的密钥、固件和游戏。<br><br><small>(%1)</small> - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5131,39 +5298,39 @@ on your system's performance. 您的系统性能。 - + Deriving Keys 生成密钥 - + Select RomFS Dump Target 选择 RomFS 转储目标 - + Please select which RomFS you would like to dump. 请选择希望转储的 RomFS。 - + Are you sure you want to close yuzu? 您确定要关闭 yuzu 吗? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. 您确定要停止模拟吗?未保存的进度将会丢失。 - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5175,38 +5342,38 @@ Would you like to bypass this and exit anyway? GRenderWindow - + OpenGL not available! OpenGL 模式不可用! - + yuzu has not been compiled with OpenGL support. yuzu 没有使用 OpenGL 进行编译。 - - + + Error while initializing OpenGL! 初始化 OpenGL 时出错! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. 您的 GPU 可能不支持 OpenGL ,或者您没有安装最新的显卡驱动。 - + Error while initializing OpenGL 4.6! 初始化 OpenGL 4.6 时出错! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 您的 GPU 可能不支持 OpenGL 4.6 ,或者您没有安装最新的显卡驱动。<br><br>GL 渲染器:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 您的 GPU 可能不支持某些必需的 OpenGL 扩展。请确保您已经安装最新的显卡驱动。<br><br>GL 渲染器:<br>%1<br><br>不支持的扩展:<br>%2 @@ -5214,153 +5381,153 @@ Would you like to bypass this and exit anyway? GameList - + Favorite 收藏 - + Start Game 开始游戏 - + Start Game without Custom Configuration 使用公共设置项进行游戏 - + Open Save Data Location 打开存档位置 - + Open Mod Data Location 打开 MOD 数据位置 - + Open Transferable Pipeline Cache 打开可转移着色器缓存 - + Remove 删除 - + Remove Installed Update 删除已安装的游戏更新 - + Remove All Installed DLC 删除所有已安装 DLC - + Remove Custom Configuration 删除自定义设置 - + Remove OpenGL Pipeline Cache 删除 OpenGL 着色器缓存 - + Remove Vulkan Pipeline Cache 删除 Vulkan 着色器缓存 - + Remove All Pipeline Caches 删除所有着色器缓存 - + Remove All Installed Contents 删除所有安装的项目 - + Dump RomFS 转储 RomFS - + Dump RomFS to SDMC 转储 RomFS 到 SDMC - + Copy Title ID to Clipboard 复制游戏 ID 到剪贴板 - + Navigate to GameDB entry 查看兼容性报告 - + Properties 属性 - + Scan Subfolders 扫描子文件夹 - + Remove Game Directory 移除游戏目录 - + ▲ Move Up ▲ 向上移动 - + ▼ Move Down ▼ 向下移动 - + Open Directory Location 打开目录位置 - + Clear 清除 - + Name 名称 - + Compatibility 兼容性 - + Add-ons 附加项 - + File type 文件类型 - + Size 大小 @@ -5369,76 +5536,61 @@ Would you like to bypass this and exit anyway? GameListItemCompat + Ingame + 可进游戏 + + + + Game starts, but crashes or major glitches prevent it from being completed. + 游戏可以开始,但会出现崩溃或严重故障导致游戏无法继续。 + + + Perfect 完美 - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - 游戏功能完美,没有音频或图形问题,所有测试功能按预期工作,无需需要任何解决办法。 - - - - Great - 良好 - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - 游戏运行时会有非常轻微的图像或音频问题,但是能从头玩到尾。可能需要一些技巧才能完成游戏。 - - Okay - 一般 - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - 游戏运行时会有很多图像或音频错误,但是在使用一些特殊技巧之后能完整地完成游戏。 + Game can be played without issues. + 游戏可以毫无问题地运行。 - Bad - 较差 + Playable + 可运行 - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - 游戏能运行,但是会有大量图像或音频错误。即使使用一些技巧也无法通过游戏的某些区域。 + Game functions with minor graphical or audio glitches and is playable from start to finish. + 游戏可以从头到尾完整地运行,但可能出现轻微的图形或音频故障。 - + Intro/Menu 开场 / 菜单 - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - 游戏完全没法玩,图像或音频有重大错误。通过开场菜单后无法继续。 + + Game loads, but is unable to progress past the Start Screen. + 游戏可以加载,但无法通过标题页面。 - + Won't Boot 无法打开 - + The game crashes when attempting to startup. 在启动游戏时直接崩溃了。 - + Not Tested 未测试 - + The game has not yet been tested. 游戏尚未经过测试。 @@ -5446,7 +5598,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list 双击以添加新的游戏文件夹 @@ -5459,12 +5611,12 @@ Screen. %1 / %n 个结果 - + Filter: 搜索: - + Enter pattern to filter 搜索游戏 @@ -5534,32 +5686,33 @@ Screen. Host Room - 管理房间 + 创建房间 HostRoomWindow - + Error 错误 - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: - 向公共大厅公开房间时失败。为了管理公开房间,您必须在模拟 -> 设置 -> 网络中配置有效的 yuzu 帐户。如果不想在公共大厅中公开房间,请选择“未列出”。 + 向公共大厅公开房间时失败。为了创建公开房间,您必须在模拟 -> 设置 -> 网络中配置有效的 yuzu 帐户。如果不想在公共大厅中公开房间,请选择“未列出”。 调试消息: Hotkeys - + Audio Mute/Unmute 开启/关闭静音 + @@ -5581,112 +5734,111 @@ Debug Message: - Main Window 主窗口 - + Audio Volume Down 调低音量 - + Audio Volume Up 调高音量 - + Capture Screenshot 捕获截图 - + Change Adapting Filter 更改窗口滤镜 - + Change Docked Mode 更改主机运行模式 - + Change GPU Accuracy 更改 GPU 精度 - + Continue/Pause Emulation 继续/暂停模拟 - + Exit Fullscreen 退出全屏 - + Exit yuzu 退出 yuzu - + Fullscreen 全屏 - + Load File 加载文件 - + Load/Remove Amiibo 加载/移除 Amiibo - + Restart Emulation 重新启动模拟 - + Stop Emulation 停止模拟 - + TAS Record TAS 录制 - + TAS Reset 重置 TAS - + TAS Start/Stop TAS 开始/停止 - + Toggle Filter Bar 切换搜索栏 - + Toggle Framerate Limit 切换帧率限制 - + Toggle Mouse Panning 切换鼠标平移 - + Toggle Status Bar 切换状态栏 @@ -5788,7 +5940,7 @@ Debug Message: Games I Own - 游戏 I 我的 + 我拥有的游戏 @@ -5801,42 +5953,42 @@ Debug Message: 刷新游戏大厅 - + Password Required to Join 加入此房间需要密码 - + Password: 密码: - + + Players + 玩家数 + + + Room Name 房间名称 - + Preferred Game 首选游戏 - + Host - 管理 + 房主 - - Players - 玩家 - - - + Refreshing 刷新中 - + Refresh List 刷新列表 @@ -5859,232 +6011,237 @@ Debug Message: 最近文件 (&R) - + &Emulation 模拟 (&E) - + &View 视图 (&V) - + &Reset Window Size 重置窗口大小 (&R) - + &Debugging 调试 (&D) - + Reset Window Size to &720p 重置窗口大小为720p (&7) - + Reset Window Size to 720p 重置窗口大小为720p - + Reset Window Size to &900p 重置窗口大小为900p (&9) - + Reset Window Size to 900p 重置窗口大小为900p - + Reset Window Size to &1080p 重置窗口大小为1080p (&1) - + Reset Window Size to 1080p 重置窗口大小为1080p - + + &Multiplayer + 多人游戏 (&M) + + + &Tools 工具 (&T) - + &TAS TAS (&T) - + &Help 帮助 (&H) - + &Install Files to NAND... 安装文件到 NAND... (&I) - + L&oad File... 加载文件... (&O) - + Load &Folder... 加载文件夹... (&F) - + E&xit 退出 (&X) - + &Pause 暂停 (&P) - + &Stop 停止 (&S) - + &Reinitialize keys... 重新生成密钥... (&R) - + &About yuzu 关于 yuzu (&A) - + Single &Window Mode 单窗口模式 (&W) - + Con&figure... 设置... (&F) - + Display D&ock Widget Headers 显示停靠小部件的标题 (&O) - + Show &Filter Bar 显示搜索栏 (&F) - + Show &Status Bar 显示状态栏 (&S) - + Show Status Bar 显示状态栏 - - - Browse Public Game Lobby - 浏览公共游戏大厅 - - - - Create Room - 创建房间 - - Leave Room - 离开房间 + &Browse Public Game Lobby + 浏览公共游戏大厅 (&B) - - Direct Connect to Room - 直接连接到房间 + + &Create Room + 创建房间 (&C) - - Show Current Room - 显示当前的房间 + + &Leave Room + 离开房间 (&L) + &Direct Connect to Room + 直接连接到房间 (&D) + + + + &Show Current Room + 显示当前房间 (&S) + + + F&ullscreen 全屏 (&U) - + &Restart 重新启动 (&R) - + Load/Remove &Amiibo... 加载/移除 Amiibo... (&A) - + &Report Compatibility 报告兼容性 (&R) - + Open &Mods Page 打开 Mod 页面 (&M) - + Open &Quickstart Guide 查看快速导航 (&Q) - + &FAQ FAQ (&F) - + Open &yuzu Folder 打开 yuzu 文件夹 (&Y) - + &Capture Screenshot 捕获截图 (&C) - + &Configure TAS... 配置 TAS... (&C) - + Configure C&urrent Game... 配置当前游戏... (&U) - + &Start 开始 (&S) - + &Reset 重置 (&R) - + R&ecord 录制 (&E) @@ -6149,47 +6306,42 @@ Debug Message: MultiplayerState - - + Current connection status 当前的连接状态 - - + Not Connected. Click here to find a room! 未连接。点击此处查找一个房间! - - - Connected - 已连接 - - - - Not Connected 未连接 - + + Connected + 已连接 + + + + New Messages Received + 收到了新消息 + + + Error 错误 - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: 更新房间信息时失败。请检查网络连接并尝试重开房间。 调试信息: - - - New Messages Received - 收到了新消息 - NetworkMessage @@ -6291,22 +6443,41 @@ They may have left the room. 他们可能已经离开了房间。 - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + 未选择有效的网络接口。 +请于设置 -> 系统 -> 网络中进行相关设置。 + + + + Game already running + 游戏正在运行中 + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + 不推荐游戏正在运行时加入房间,这可能会导致房间的某些功能出现异常。 +是否继续? + + + Leave Room 离开房间 - + You are about to close the room. Any network connections will be closed. 您正要关闭房间。所有的网络连接都将关闭。 - + Disconnect 断开连接 - + You are about to leave the room. Any network connections will be closed. 您正要离开房间。所有的网络连接都将关闭。 @@ -6314,7 +6485,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error 错误 @@ -6363,42 +6534,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game %1 不在玩游戏 - + %1 is playing %2 %1 正在玩 %2 - + Not playing a game 不在玩游戏 - + Installed SD Titles SD 卡中安装的项目 - + Installed NAND Titles NAND 中安装的项目 - + System Titles 系统项目 - + Add New Game Directory 添加游戏目录 - + Favorites 收藏 @@ -6428,7 +6599,7 @@ p, li { white-space: pre-wrap; } - + [not set] [未设置] @@ -6443,10 +6614,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 轴 %1%2 @@ -6460,9 +6631,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [未知] @@ -6627,15 +6798,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [无效] - - + + %1%2Hat %3 %1%2Hat 控制器 %3 @@ -6643,35 +6814,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2轴 %3 - + %1%2Axis %3,%4,%5 %1%2轴 %3,%4,%5 - + %1%2Motion %3 %1%2体感 %3 - - + + %1%2Button %3 %1%2按键 %3 - + [unused] [未使用] @@ -6712,11 +6883,124 @@ p, li { white-space: pre-wrap; } 额外按键 - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + Amiibo 设置 + + + + Amiibo Info + Amiibo 信息 + + + + Series + 系列 + + + + Type + 类型 + + + + Name + 名称 + + + + Amiibo Data + Amiibo 数据 + + + + Custom Name + 自定义名称 + + + + Owner + 所有者 + + + + Creation Date + 创建日期 + + + + dd/MM/yyyy + dd/MM/yyyy + + + + Modification Date + 修改日期 + + + + dd/MM/yyyy + dd/MM/yyyy + + + + Game Data + 游戏数据 + + + + Game Id + 游戏 ID + + + + Mount Amiibo + 挂载 Amiibo + + + + ... + ... + + + + File Path + 文件路径 + + + + No game data present + 没有游戏数据 + + + + The following amiibo data will be formatted: + 将格式化以下 amiibo 数据: + + + + The following game data will removed: + 将删除以下游戏数据: + + + + Set nickname and owner: + 设置昵称及所有者: + + + + Do you wish to restore this amiibo? + 您想要恢复这个 amiibo 吗? + + QtControllerSelectorDialog @@ -6820,6 +7104,7 @@ p, li { white-space: pre-wrap; } + Handheld 掌机模式 @@ -6859,11 +7144,6 @@ p, li { white-space: pre-wrap; } Docked 主机模式 - - - Undocked - 掌机模式 - Vibration diff --git a/dist/languages/zh_TW.ts b/dist/languages/zh_TW.ts index ed797e8..70004ce 100644 --- a/dist/languages/zh_TW.ts +++ b/dist/languages/zh_TW.ts @@ -36,7 +36,7 @@ p, li { white-space: pre-wrap; } <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html> - <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">官方网站</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">源代码</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">贡献者</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">许可证</span></a></p></body></html> + <html><head/><body><p><a href="https://yuzu-emu.org/"><span style=" text-decoration: underline; color:#039be5;">官網</span></a> | <a href="https://github.com/yuzu-emu"><span style=" text-decoration: underline; color:#039be5;">原始碼</span></a> | <a href="https://github.com/yuzu-emu/yuzu/graphs/contributors"><span style=" text-decoration: underline; color:#039be5;">貢獻者</span></a> | <a href="https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">許可證</span></a></p></body></html> @@ -82,97 +82,97 @@ p, li { white-space: pre-wrap; } Room Window - 房间窗口 + 房間視窗 Send Chat Message - 发送聊天信息 + 傳送聊天訊息 Send Message - 发送消息 + 傳送訊息 - + Members - 成员 + 成員 - + %1 has joined %1 已加入 - + %1 has left - %1 已离开 + %1 已離開 - + %1 has been kicked - %1 已被踢出房间 + %1 已被踢出房間 - + %1 has been banned - %1 已被封禁 + %1 已被封鎖 - + %1 has been unbanned %1 已被解封 - + View Profile - 查看个人资料 - - - - - Block Player - 屏蔽玩家 - - - - When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? - 当你屏蔽玩家后,你将无法收到他们的聊天消息。<br><br>您确定要屏蔽 %1 吗? - - - - Kick - 踢出房间 - - - - Ban - 封禁 + 查看個人資料 + + Block Player + 隱藏玩家 + + + + When you block a player, you will no longer receive chat messages from them.<br><br>Are you sure you would like to block %1? + 當你隱藏玩家後,你將無法收到他們的聊天訊息。<br><br>您确定要隱藏 %1 嗎? + + + + Kick + 踢出 + + + + Ban + 封鎖 + + + Kick Player 踢出玩家 - + Are you sure you would like to <b>kick</b> %1? - 您确定要将 %1 <b>踢出房间</b>吗? + 您確定要將 %1 <b>踢出</b>嗎? - + Ban Player - 封禁玩家 + 封鎖玩家 - + Are you sure you would like to <b>kick and ban</b> %1? This would ban both their forum username and their IP address. - 您确定要<b>踢出并封禁</b> %1 吗? + 您確定要<b>踢出並封鎖</b> %1 嗎? -这将封禁他们的用户名和 IP 地址。 +這將封鎖他們的用戶名稱和 IP 位址。 @@ -180,22 +180,22 @@ This would ban both their forum username and their IP address. Room Window - 房间窗口 + 房間視窗 Room Description - 房间描述 + 房間敘述 Moderation... - 内容审核... + 内容審核... Leave Room - 离开房间 + 離開房間 @@ -208,12 +208,12 @@ This would ban both their forum username and their IP address. Disconnected - 已断开连接 + 已斷開連線 - %1 (%2/%3 members) - connected - %1 (%1/%2 玩家) - 已连接 + %1 - %2 (%3/%4 members) - connected + %1 - %2 (%3/%4 個成員) - 已連線 @@ -226,6 +226,11 @@ This would ban both their forum username and their IP address. + + + + + Report Game Compatibility 回報遊戲相容性 @@ -235,92 +240,127 @@ This would ban both their forum username and their IP address. <html><head/><body><p><span style=" font-size:10pt;">如果您選擇向 </span><a href="https://yuzu-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">yuzu 相容性清單</span></a><span style=" font-size:10pt;">上傳測試結果,以下資訊將會被收集並顯示在網站上:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">裝置硬體資訊 (CPU / GPU / 作業系統)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">您正在使用的 yuzu 版本</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">已登入的 yuzu 帳號資訊</li></ul></body></html> - - Perfect - 完美 + + <html><head/><body><p>Does the game boot?</p></body></html> + <html><head/><body><p>遊戲啟動了嗎?</p></body></html> - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - <html><head/><body><p>遊戲能完整進行。不會出現任何圖形或音訊錯誤。</p></body></html> + + Yes The game starts to output video or audio + 是,遊戲開始輸出影像或音訊 - - Great - 極佳 + + No The game doesn't get past the "Launching..." screen + 否,遊戲無法通過“啟動中…”畫面 - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - <html><head/><body><p>遊戲能進行。會出現少量圖形或音訊錯誤,可能需要使用一些替代方案以破完遊戲。</p></body></html> + + Yes The game gets past the intro/menu and into gameplay + 是的,游戏出现前奏/菜单页面并进入游戏 - - Okay - 尚可 + + No The game crashes or freezes while loading or using the menu + 不,加载或使用菜单时游戏崩溃或卡死 - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - <html><head/><body><p>遊戲能進行。會出現大量圖形或音訊錯誤,但是使用一些替代方案能順利破完遊戲。</p></body></html> + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + <html><head/><body><p>游戏是否具有游戏性?</p></body></html> - - Bad - 不佳 + + Yes The game works without crashes + 是的,游戏运行时没有崩溃 - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - <html><head/><body><p>遊戲能進行。但是會出現大量圖形或音訊錯誤,即使使用一些替代方案也無法通過遊戲的某些區域。</p></body></html> + + No The game crashes or freezes during gameplay + 不,游戏运行时出现卡死或崩溃 - - Intro/Menu - 開始畫面/選單 + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + <html><head/><body><p>游戏在运行时有没有崩溃、卡死或出现软锁?</p></body></html> - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - <html><head/><body><p>遊戲完全無法進行。因為圖形或音訊的大量錯誤,在通過開始畫面後無法繼續遊戲。</p></body></html> + + Yes The game can be finished without any workarounds + 没有,可以顺利地完成整个游戏过程 - - Won't Boot - 無法啟動 + + No The game can't progress past a certain area + 有,游戏在特定区段无法继续 - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - <html><head/><body><p>啟動遊戲時異常關閉</p></body></html> + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + <html><head/><body><p>游戏从头到尾都可以顺利运行吗?</p></body></html> - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> - <html><head/><body><p>在不考慮速度或效能的情況下,使用此版本的 yuzu 玩這遊戲時的體驗如何?</p></body></html> + + Major The game has major graphical errors + 严重 游戏在运行时有严重的图像错误 - + + Minor The game has minor graphical errors + 轻微 游戏在运行时有轻微的图形错误 + + + + None Everything is rendered as it looks on the Nintendo Switch + 完美 媲美实机的游戏体验 + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + <html><head/><body><p>游戏运行时出现了图形问题吗?</p></body></html> + + + + Major The game has major audio errors + 严重 游戏运行时出现严重的音频错误 + + + + Minor The game has minor audio errors + 轻微 游戏运行时出现轻微的音频错误 + + + + None Audio is played perfectly + 完美 游戏运行时音频未出现任何问题 + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + <html><head/><body><p>游戏是否出现音频故障/效果缺失?</p></body></html> + + + Thank you for your submission! 感謝您的回報! - + Submitting 上傳中 - + Communication error 連線錯誤 - + An error occurred while sending the Testcase 在上傳測試結果時發生錯誤 - + Next 下一步 @@ -418,7 +458,7 @@ This would ban both their forum username and their IP address. 還原預設值 - + Auto 自動 @@ -770,200 +810,235 @@ This would ban both their forum username and their IP address. ConfigureDebug - + Debugger 调试器 - + Enable GDB Stub 开启 GDB 调试 - + Port: 通訊埠: - + Logging 紀錄 - + Global Log Filter 全域紀錄篩選器 - + Show Log in Console 在終端機中顯示紀錄 - + Open Log Location 開啟紀錄位置 - + When checked, the max size of the log increases from 100 MB to 1 GB 啟用後紀錄檔案大小上限從 100MB 增加到 1GB - + Enable Extended Logging** 啟用延伸紀錄** - + Homebrew Homebrew - + Arguments String 參數字串 - + Graphics 圖形 - + When checked, the graphics API enters a slower debugging mode 啟用時圖形 API 會進入較慢的偵錯模式。 - + Enable Graphics Debugging 啟用圖形偵錯 - + When checked, it enables Nsight Aftermath crash dumps 啟用時 yuzu 將會儲存 Nsight Aftermath 格式的錯誤傾印檔案。 - + Enable Nsight Aftermath 啟用 Nsight Aftermath - + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found 啟用時,將從磁碟著色器快娶或遊戲中轉儲所有的著色器檔案。 - + Dump Game Shaders 傾印遊戲著色器 - + When checked, it will dump all the macro programs of the GPU 选中后,将转储 GPU 的所有宏程序 - + Dump Maxwell Macros 转储 Maxwell 宏 - + When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower 啟用時將停用 Macro Just In Time 編譯器,會使得效能降低。 - + Disable Macro JIT 停用 Macro JIT - + When checked, yuzu will log statistics about the compiled pipeline cache 啟用時 yuzu 將記錄有關編譯著色器快取的統計資訊。 - + Enable Shader Feedback 啟用著色器回饋 - + When checked, it executes shaders without loop logic changes 啟用時 yuzu 在執行著色器時,不會修改循環結構的條件判斷。 - + Disable Loop safety checks 停用循環安全檢查 - + Debugging 偵錯 - - Enable FS Access Log - 啟用檔案系統存取記錄 - - - - Dump Audio Commands To Console** - 将音频命令转储至控制台** - - - - Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - 启用此选项会将最新的音频命令列表输出到控制台。只影响使用音频渲染器的游戏。 - - - + Enable Verbose Reporting Services** 啟用詳細報告服務 - + + Enable FS Access Log + 啟用檔案系統存取記錄 + + + + Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. + 启用此选项会将最新的音频命令列表输出到控制台。只影响使用音频渲染器的游戏。 + + + + Dump Audio Commands To Console** + 将音频命令转储至控制台** + + + + Create Minidump After Crash + 微型故障转储 + + + Advanced 進階 - + Kiosk (Quest) Mode Kiosk (Quest) 模式 - + Enable CPU Debugging 啟用 CPU 模擬偵錯 - + Enable Debug Asserts 啟用偵錯 - + Enable Auto-Stub** 啟用自動偵錯** - + Enable All Controller Types 启用其他控制器 - + Disable Web Applet 停用 Web Applet - + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + 允许 yuzu 在启动时检查 Vulkan 环境是否正常工作。如果是其他程序导致 yuzu 出现此问题,请禁用此选项。 + + + + Perform Startup Vulkan Check + 启动时进行 Vulkan 检测 + + + **This will be reset automatically when yuzu closes. **當 yuzu 關閉時會自動重設。 + + + Restart Required + 需要重启 + + + + yuzu is required to restart in order to apply this setting. + 重启 yuzu 后才能应用此设置。 + + + + Web applet not compiled + Web 应用程序未编译 + + + + MiniDump creation not compiled + 小型转储创建未编译 + ConfigureDebugController @@ -1333,193 +1408,218 @@ This would ban both their forum username and their IP address. API: - + Graphics Settings 圖形設定 - + Use disk pipeline cache 使用硬碟管線快取 - + Use asynchronous GPU emulation 使用非同步 CPU 模擬 - + Accelerate ASTC texture decoding 加速 ASTC 材質解碼 - + NVDEC emulation: NVDEC 模擬方式: - + No Video Output 無視訊輸出 - + CPU Video Decoding CPU 視訊解碼 - + GPU Video Decoding (Default) GPU 視訊解碼(預設) - + Fullscreen Mode: 全螢幕模式: - + Borderless Windowed 無邊框視窗 - + Exclusive Fullscreen 全螢幕獨占 - + Aspect Ratio: 長寬比: - + Default (16:9) 預設 (16:9) - + Force 4:3 強制 4:3 - + Force 21:9 強制 21:9 - + + Force 16:10 + 强制 16:10 + + + Stretch to Window 延伸視窗 - + Resolution: 解析度: - + 0.5X (360p/540p) [EXPERIMENTAL] 0.5X (360p/540p) [實驗性] - + 0.75X (540p/810p) [EXPERIMENTAL] 0.75X (540p/810p) [實驗性] - + 1X (720p/1080p) 1X (720p/1080p) - + 2X (1440p/2160p) 2X (1440p/2160p) - + 3X (2160p/3240p) 3X (2160p/3240p) - + 4X (2880p/4320p) 4X (2880p/4320p) - + 5X (3600p/5400p) 5X (3600p/5400p) - + 6X (4320p/6480p) 6X (4320p/6480p) - + Window Adapting Filter: 視窗濾鏡: - + Nearest Neighbor 最近鄰域 - + Bilinear 雙線性 - + Bicubic 雙三次 - + Gaussian 高斯 - + ScaleForce 強制縮放 - + AMD FidelityFX™️ Super Resolution (Vulkan Only) AMD FidelityFX™️ 超高畫質技術 (僅限 Vulkan 模式) - + Anti-Aliasing Method: 抗鋸齒方式: - + None - + FXAA FXAA - - + + Use global FSR Sharpness + 启用全局 FSR 锐化 + + + + Set FSR Sharpness + 设置 FSR 锐化 + + + + FSR Sharpness: + FSR 锐化度: + + + + 100% + 100% + + + + Use global background color 使用全域背景顏色 - + Set background color: 設定背景顏色: - + Background Color: 背景顏色: @@ -1528,6 +1628,12 @@ This would ban both their forum username and their IP address. GLASM (Assembly Shaders, NVIDIA Only) GLASM(組合語言著色器,僅限 NVIDIA) + + + %1% + FSR sharpening percentage (e.g. 50%) + %1% + ConfigureGraphicsAdvanced @@ -1582,37 +1688,47 @@ This would ban both their forum username and their IP address. 使用快速 GPU 時間(不穩定) - + + Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance. + 启用悲观缓冲区刷新。此选项将强制刷新未修改的缓冲区,可能会降低性能。 + + + + Use pessimistic buffer flushes (Hack) + 启用悲观缓冲区刷新 (不稳定) + + + Anisotropic Filtering: 各向異性過濾: - + Automatic 自動 - + Default 預設 - + 2x 2x - + 4x 4x - + 8x 8x - + 16x 16x @@ -2104,7 +2220,7 @@ This would ban both their forum username and their IP address. - + Left Stick 左搖桿 @@ -2198,14 +2314,14 @@ This would ban both their forum username and their IP address. - + L L - + ZL ZL @@ -2224,7 +2340,7 @@ This would ban both their forum username and their IP address. - + Plus @@ -2237,15 +2353,15 @@ This would ban both their forum username and their IP address. - + R R - + ZR ZR @@ -2302,231 +2418,236 @@ This would ban both their forum username and their IP address. - + Right Stick 右搖桿 - - - - + + + + Clear 清除 - - - - - + + + + + [not set] [未設定] - - - Toggle button - 切換按鍵 - - - - + + Invert button 無效按鈕 - - + + + Toggle button + 切換按鍵 + + + + Invert axis 方向反轉 - - - + + + Set threshold 設定閾值 - - + + Choose a value between 0% and 100% 選擇介於 0% 和 100% 之間的值 - + + Toggle axis + 切换轴 + + + Set gyro threshold 陀螺仪阈值设定 - + Map Analog Stick 搖桿映射 - + After pressing OK, first move your joystick horizontally, and then vertically. To invert the axes, first move your joystick vertically, and then horizontally. 按下確定後,先水平再上下移動您的搖桿。 要反轉方向,則先上下再水平移動您的搖桿。 - + Center axis 中心轴 - - + + Deadzone: %1% 無感帶:%1% - - + + Modifier Range: %1% 輕推靈敏度:%1% - - + + Pro Controller Pro 手把 - + Dual Joycons 雙 Joycon 手把 - + Left Joycon 左 Joycon 手把 - + Right Joycon 右 Joycon 手把 - + Handheld 掌機模式 - + GameCube Controller GameCube 手把 - + Poke Ball Plus 精靈球 PLUS - + NES Controller NES 控制器 - + SNES Controller SNES 控制器 - + N64 Controller N64 控制器 - + Sega Genesis Mega Drive - + Start / Pause 開始 / 暫停 - + Z Z - + Control Stick 控制搖桿 - + C-Stick C 搖桿 - + Shake! 搖動! - + [waiting] [等待中] - + New Profile 新增設定檔 - + Enter a profile name: 輸入設定檔名稱: - - + + Create Input Profile 建立輸入設定檔 - + The given profile name is not valid! 輸入的設定檔名稱無效! - + Failed to create the input profile "%1" 建立輸入設定檔「%1」失敗 - + Delete Input Profile 刪除輸入設定檔 - + Failed to delete the input profile "%1" 刪除輸入設定檔「%1」失敗 - + Load Input Profile 載入輸入設定檔 - + Failed to load the input profile "%1" 載入輸入設定檔「%1」失敗 - + Save Input Profile 儲存輸入設定檔 - + Failed to save the input profile "%1" 儲存輸入設定檔「%1」失敗 @@ -2781,42 +2902,42 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 出版商 - + Add-Ons 延伸模組 - + General 一般 - + System 系統 - + CPU CPU - + Graphics 圖形 - + Adv. Graphics 進階圖形 - + Audio 音訊 - + Properties 屬性 @@ -2872,37 +2993,37 @@ To invert the axes, first move your joystick vertically, and then horizontally.< 目前使用者 - + Username 使用者名稱 - + Set Image 選擇圖片 - + Add 新增 - + Rename 重新命名 - + Remove 移除 - + Profile management is available only when game is not running. 僅在遊戲未執行時才能修改使用者設定檔 - + %1 %2 %1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF)) @@ -2910,96 +3031,106 @@ To invert the axes, first move your joystick vertically, and then horizontally.< %2 - + Enter Username 輸入使用者名稱 - + Users 使用者 - + Enter a username for the new user: 輸入新使用者的名稱 - + Enter a new username: 輸入新的使用者名稱 - - Confirm Delete - 確認刪除 - - - - You are about to delete user with name "%1". Are you sure? - 您確定要移除使用者「%1」嗎? - - - + Select User Image 選擇使用者圖片 - + JPEG Images (*.jpg *.jpeg) JPEG圖片 (*.jpg *.jpeg) - + Error deleting image 刪除圖片時發生錯誤 - + Error occurred attempting to overwrite previous image at: %1. 嘗試覆寫之前的圖片時發生錯誤:%1 - + Error deleting file 刪除檔案時發生錯誤 - + Unable to delete existing file: %1. 無法刪除檔案:%1 - + Error creating user image directory 建立使用者圖片資料夾時發生錯誤 - + Unable to create directory %1 for storing user images. 無法建立儲存使用者圖片的資料夾 %1 - + Error copying user image 複製使用者圖片時發生錯誤 - + Unable to copy image from %1 to %2 無法將圖片從 %1 複製到 %2 - + Error resizing user image 調整使用者圖片大小時發生錯誤 - + Unable to resize image 無法調整圖片大小 + + ConfigureProfileManagerDeleteDialog + + + Delete this user? All of the user's save data will be deleted. + 删除此用户?此用户保存的所有数据都将被删除。 + + + + Confirm Delete + 確認刪除 + + + + Name: %1 +UUID: %2 + 名称: %1 +UUID: %2 + + ConfigureRingController @@ -3528,47 +3659,47 @@ To invert the axes, first move your joystick vertically, and then horizontally.< <html><head/><body><p>通過讀取與 TAS-nx 腳本具有相同格式的腳本來讀取控制器的輸入。<br/>有關詳細資訊,請參閱 yuzu 官方網站的<a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">說明網頁</span></a>。</p></body></html> - + To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys). 要確認是哪些快速鍵控制播放/錄製,請參閱快速鍵設定。(設定 > 一般 > 快速鍵) - + WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method. 警告:這是實驗性功能。<br/>由於不完善的同步方式,可能無法完整的重現。 - + Settings 設定 - + Enable TAS features 啟用 TAS 功能 - + Loop script 循環腳本 - + Pause execution during loads 載入畫面時暫停執行 - + Script Directory 腳本資料夾 - + Path 路徑 - + ... ... @@ -3820,56 +3951,71 @@ Drag points to change position, or double-click table cells to edit values. + Show Compatibility List + 显示兼容性列表 + + + Show Add-Ons Column 顯示延伸模組欄位 - + + Show Size Column + 显示“文件大小”列 + + + + Show File Types Column + 显示“文件类型”列 + + + Game Icon Size: 遊戲圖示大小: - + Folder Icon Size: 資料夾圖示大小: - + Row 1 Text: 第一行顯示文字: - + Row 2 Text: 第二行顯示文字: - + Screenshots 螢幕截圖 - + Ask Where To Save Screenshots (Windows Only) 詢問儲存螢幕截圖的位置(僅限 Windows) - + Screenshots Path: 螢幕截圖位置 - + ... ... - + Select Screenshots Path... 選擇儲存螢幕截圖位置... - + <System> <System> @@ -3978,7 +4124,7 @@ Drag points to change position, or double-click table cells to edit values. - + Verify 驗證 @@ -4065,7 +4211,7 @@ Drag points to change position, or double-click table cells to edit values. - + Unspecified 未指定 @@ -4080,17 +4226,36 @@ Drag points to change position, or double-click table cells to edit values.Token 未驗證,因此未儲存您對使用者名稱和 Token 的修改。 - + + Unverified, please click Verify before saving configuration + Tooltip + 令牌未验证,请在保存配置之前进行验证。 + + + + Verifying... 驗證中... - + + Verified + Tooltip + 已验证 + + + + Verification failed + Tooltip + 驗證失敗 + + + Verification failed 驗證失敗 - + Verification failed. Check that you have entered your token correctly, and that your internet connection is working. 驗證失敗。請檢查您输入的 Token 是否正確,並確保您的網路連線正常。 @@ -4159,12 +4324,12 @@ Drag points to change position, or double-click table cells to edit values. DirectConnectWindow - + Connecting 连接中 - + Connect 连接 @@ -4172,912 +4337,914 @@ Drag points to change position, or double-click table cells to edit values. GMainWindow - + <a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous data is collected</a> to help improve yuzu. <br/><br/>Would you like to share your usage data with us? 我們<a href='https://yuzu-emu.org/help/feature/telemetry/'>蒐集匿名的資料</a>以幫助改善 yuzu。<br/><br/>您願意和我們分享您的使用資料嗎? - + Telemetry 遙測 - + Broken Vulkan Installation Detected 检测到 Vulkan 的安装已损坏 - + Vulkan initialization failed during boot.<br><br>Click <a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for instructions to fix the issue</a>. Vulkan 初始化失败。<br><br>点击<a href='https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>这里</a>获取此问题的相关信息。 - + Loading Web Applet... 載入 Web Applet... - - + + Disable Web Applet 停用 Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) 禁用 Web 应用程序可能会导致未知的行为,且只能在《超级马里奥 3D 全明星》中使用。您确定要禁用 Web 应用程序吗? (您可以在调试选项中重新启用它。) - + The amount of shaders currently being built 目前正在建構的著色器數量 - + The current selected resolution scaling multiplier. 目前選擇的解析度縮放比例。 - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. 目前的模擬速度。高於或低於 100% 表示比實際 Switch 執行速度更快或更慢。 - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 遊戲即時 FPS。會因遊戲和場景的不同而改變。 - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 在不考慮幀數限制和垂直同步的情況下模擬一個 Switch 畫格的實際時間,若要全速模擬,此數值不得超過 16.67 毫秒。 - + VULKAN VULKAN - + OPENGL OPENGL - + &Clear Recent Files 清除最近的檔案(&C) - + &Continue 繼續(&C) - + &Pause &暫停 - + yuzu is running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping yuzu 正在執行中 - + Warning Outdated Game Format 過時遊戲格式警告 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br><br>For an explanation of the various Switch formats yuzu supports, <a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our wiki</a>. This message will not be shown again. 此遊戲為解構的 ROM 資料夾格式,這是一種過時的格式,已被其他格式取代,如 NCA、NAX、XCI、NSP。解構的 ROM 目錄缺少圖示、中繼資料和更新支援。<br><br>有關 yuzu 支援的各種 Switch 格式說明,<a href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>請參閱我們的 wiki </a>。此訊息將不再顯示。 - - + + Error while loading ROM! 載入 ROM 時發生錯誤! - + The ROM format is not supported. 此 ROM 格式不支援 - + An error occurred initializing the video core. 初始化視訊核心時發生錯誤 - + yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://yuzu-emu.org/help/reference/log-files/'>How to Upload the Log File</a>. yuzu 在執行視訊核心時發生錯誤。 這可能是 GPU 驅動程序過舊造成的。 詳細資訊請查閱日誌檔案。 關於日誌檔案的更多資訊,請參考以下頁面:<a href='https://yuzu-emu.org/help/reference/log-files/'>如何上傳日誌檔案</a>。 - + Error while loading ROM! %1 %1 signifies a numeric error code. 載入 ROM 時發生錯誤!%1 - + %1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to redump your files.<br>You can refer to the yuzu wiki</a> or the yuzu Discord</a> for help. %1 signifies an error string. %1<br>請參閱 <a href='https://yuzu-emu.org/help/quickstart/'>yuzu 快速指引</a>以重新傾印檔案。<br>您可以前往 yuzu 的 wiki</a> 或 Discord 社群</a>以獲得幫助。 - + An unknown error occurred. Please see the log for more details. 發生未知錯誤,請檢視紀錄了解細節。 - + (64-bit) (64-bit) - + (32-bit) (32-bit) - + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Save Data 儲存資料 - + Mod Data 模組資料 - + Error Opening %1 Folder 開啟資料夾 %1 時發生錯誤 - - + + Folder does not exist! 資料夾不存在 - + Error Opening Transferable Shader Cache 開啟通用著色器快取位置時發生錯誤 - + Failed to create the shader cache directory for this title. 無法新增此遊戲的著色器快取資料夾。 - - Contents - 內容 + + Error Removing Contents + 删除内容时出错 - - Update - 遊戲更新 + + Error Removing Update + 删除更新时出错 - - DLC - DLC + + Error Removing DLC + 删除 DLC 时出错 - + + Remove Installed Game Contents? + 删除已安装的游戏内容? + + + + Remove Installed Game Update? + 删除已安装的游戏更新? + + + + Remove Installed Game DLC? + 删除已安装的游戏 DLC 内容? + + + Remove Entry 移除項目 - - Remove Installed Game %1? - 移除已安裝的遊戲「%1」? - - - - - - - - + + + + + + Successfully Removed 移除成功 - + Successfully removed the installed base game. 成功移除已安裝的遊戲。 - - - - Error Removing %1 - 移除 %1 失敗 - - - + The base game is not installed in the NAND and cannot be removed. 此遊戲並非安裝在內部儲存空間,因此無法移除。 - + Successfully removed the installed update. 成功移除已安裝的遊戲更新。 - + There is no update installed for this title. 此遊戲沒有已安裝的更新。 - + There are no DLC installed for this title. 此遊戲沒有已安裝的 DLC。 - + Successfully removed %1 installed DLC. 成功移除遊戲 %1 已安裝的 DLC。 - + Delete OpenGL Transferable Shader Cache? 刪除 OpenGL 模式的著色器快取? - + Delete Vulkan Transferable Shader Cache? 刪除 Vulkan 模式的著色器快取? - + Delete All Transferable Shader Caches? 刪除所有的著色器快取? - + Remove Custom Game Configuration? 移除額外遊戲設定? - + Remove File 刪除檔案 - - + + Error Removing Transferable Shader Cache 刪除通用著色器快取時發生錯誤 - - + + A shader cache for this title does not exist. 此遊戲沒有著色器快取 - + Successfully removed the transferable shader cache. 成功刪除著色器快取。 - + Failed to remove the transferable shader cache. 刪除通用著色器快取失敗。 - - + + Error Removing Transferable Shader Caches 刪除通用著色器快取時發生錯誤 - + Successfully removed the transferable shader caches. 成功刪除通用著色器快取。 - + Failed to remove the transferable shader cache directory. 無法刪除著色器快取資料夾。 - - + + Error Removing Custom Configuration 移除額外遊戲設定時發生錯誤 - + A custom configuration for this title does not exist. 此遊戲沒有額外設定。 - + Successfully removed the custom game configuration. 成功移除額外遊戲設定。 - + Failed to remove the custom game configuration. 移除額外遊戲設定失敗。 - - + + RomFS Extraction Failed! RomFS 抽取失敗! - + There was an error copying the RomFS files or the user cancelled the operation. 複製 RomFS 檔案時發生錯誤或使用者取消動作。 - + Full 全部 - + Skeleton 部分 - + Select RomFS Dump Mode 選擇RomFS傾印模式 - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. 請選擇如何傾印 RomFS。<br>「全部」會複製所有檔案到新資料夾中,而<br>「部分」只會建立資料夾結構。 - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1 沒有足夠的空間用於抽取 RomFS。請確保有足夠的空間或於模擬 > 設定 >系統 >檔案系統 > 傾印根目錄中選擇其他資料夾。 - + Extracting RomFS... 抽取 RomFS 中... - - + + Cancel 取消 - + RomFS Extraction Succeeded! RomFS 抽取完成! - + The operation completed successfully. 動作已成功完成 - + Error Opening %1 開啟 %1 時發生錯誤 - + Select Directory 選擇資料夾 - + Properties 屬性 - + The game properties could not be loaded. 無法載入遊戲屬性 - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch 執行檔 (%1);;所有檔案 (*.*) - + Load File 開啟檔案 - + Open Extracted ROM Directory 開啟已抽取的 ROM 資料夾 - + Invalid Directory Selected 選擇的資料夾無效 - + The directory you have selected does not contain a 'main' file. 選擇的資料夾未包含「main」檔案。 - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) 可安装的 Switch 檔案 (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX 卡帶映像 (*.xci) - + Install Files 安裝檔案 - + %n file(s) remaining 剩餘 %n 個檔案 - + Installing file "%1"... 正在安裝檔案「%1」... - - + + Install Results 安裝結果 - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. 為了避免潛在的衝突,不建議將遊戲本體安裝至內部儲存空間。 此功能僅用於安裝遊戲更新和 DLC。 - + %n file(s) were newly installed 最近安裝了 %n 個檔案 - + %n file(s) were overwritten %n 個檔案被取代 - + %n file(s) failed to install %n 個檔案安裝失敗 - + System Application 系統應用程式 - + System Archive 系統檔案 - + System Application Update 系統應用程式更新 - + Firmware Package (Type A) 韌體包(A型) - + Firmware Package (Type B) 韌體包(B型) - + Game 遊戲 - + Game Update 遊戲更新 - + Game DLC 遊戲 DLC - + Delta Title Delta Title - + Select NCA Install Type... 選擇 NCA 安裝類型... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) 請選擇此 NCA 的安裝類型: (在多數情況下,選擇預設的「遊戲」即可。) - + Failed to Install 安裝失敗 - + The title type you selected for the NCA is invalid. 選擇的 NCA 安裝類型無效。 - + File not found 找不到檔案 - + File "%1" not found 找不到「%1」檔案 - + OK 確定 - + + Hardware requirements not met + 硬件不满足要求 + + + + Your system does not meet the recommended hardware requirements. Compatibility reporting has been disabled. + 您的系统不满足运行 yuzu 推荐的推荐配置。兼容性报告已被禁用。 + + + Missing yuzu Account 未設定 yuzu 帳號 - + In order to submit a game compatibility test case, you must link your yuzu account.<br><br/>To link your yuzu account, go to Emulation &gt; Configuration &gt; Web. 為了上傳相容性測試結果,您必須登入 yuzu 帳號。<br><br/>欲登入 yuzu 帳號請至模擬 &gt; 設定 &gt; 網路。 - + Error opening URL 開啟 URL 時發生錯誤 - + Unable to open the URL "%1". 無法開啟 URL:「%1」。 - + TAS Recording TAS 錄製 - + Overwrite file of player 1? 覆寫玩家 1 的檔案? - + Invalid config detected 偵測到無效設定 - + Handheld controller can't be used on docked mode. Pro controller will be selected. 掌機手把無法在主機模式中使用。將會選擇 Pro 手把。 - - - Error - 错误 - - - - - The current game is not looking for amiibos - 当前游戏并没有在寻找 Amiibos - - - - + + Amiibo Amiibo - - + + The current amiibo has been removed 当前的 Amiibo 已被移除。 - + + Error + 错误 + + + + + The current game is not looking for amiibos + 当前游戏并没有在寻找 Amiibos + + + Amiibo File (%1);; All Files (*.*) Amiibo 檔案 (%1);; 所有檔案 (*.*) - + Load Amiibo 開啟 Amiibo - - Error opening Amiibo data file - 開啟 Amiibo 檔案時發生錯誤 - - - - Unable to open Amiibo file "%1" for reading. - 無法開啟 Amiibo 檔案 %1。 - - - - Error reading Amiibo data file - 讀取 Amiibo 檔案時發生錯誤 - - - - Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes. - 無法讀取完整的 Amiibo 資料。應讀取 %1 位元組,但實際僅讀取到 %2 位元組。 - - - + Error loading Amiibo data 載入 Amiibo 資料時發生錯誤 - - Unable to load Amiibo data. - 無法載入 Amiibo 資料。 + + The selected file is not a valid amiibo + 选择的文件并不是有效的 amiibo - + + The selected file is already on use + 选择的文件已在使用中 + + + + An unknown error occurred + 发生了未知错误 + + + Capture Screenshot 截圖 - + PNG Image (*.png) PNG 圖片 (*.png) - + TAS state: Running %1/%2 TAS 狀態:正在執行 %1/%2 - + TAS state: Recording %1 TAS 狀態:正在錄製 %1 - + TAS state: Idle %1/%2 TAS 狀態:閒置 %1/%2 - + TAS State: Invalid TAS 狀態:無效 - + &Stop Running &停止執行 - + &Start 開始(&S) - + Stop R&ecording 停止錄製 - + R&ecord 錄製 (&E) - + Building: %n shader(s) 正在編譯 %n 個著色器檔案 - + Scale: %1x %1 is the resolution scaling factor 縮放比例:%1x - + Speed: %1% / %2% 速度:%1% / %2% - + Speed: %1% 速度:%1% - + Game: %1 FPS (Unlocked) 遊戲: %1 FPS(未限制) - + Game: %1 FPS 遊戲:%1 FPS - + Frame: %1 ms 畫格延遲:%1 ms - + GPU NORMAL GPU 一般效能 - + GPU HIGH GPU 高效能 - + GPU EXTREME GPU 最高效能 - + GPU ERROR GPU 錯誤 - + DOCKED 主机模式 - + HANDHELD 掌机模式 - + NEAREST 最近鄰域 - - + + BILINEAR 雙線性 - + BICUBIC 雙三次 - + GAUSSIAN 高斯 - + SCALEFORCE 強制縮放 - + FSR FSR - - + + NO AA 抗鋸齒關 - + FXAA FXAA - + The game you are trying to load requires additional files from your Switch to be dumped before playing.<br/><br/>For more information on dumping these files, please see the following wiki page: <a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. 此遊戲需要從您的 Switch 傾印額外檔案。<br/><br/>有關傾印這些檔案的更多資訊,請參閱以下 wiki 網頁:Dumping System Archives and the Shared Fonts from a Switch Console<a href='https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>。<br/><br/>您要停止並回到遊戲清單嗎?繼續模擬可能會導致當機、存檔損毀或其他錯誤。 - + yuzu was unable to locate a Switch system archive. %1 Yuzu 找不到 Switch 系統檔案 %1 - + yuzu was unable to locate a Switch system archive: %1. %2 Yuzu 找不到 Switch 系統檔案:%1。%2 - + System Archive Not Found 找不到系統檔案 - + System Archive Missing 系統檔案遺失 - + yuzu was unable to locate the Switch shared fonts. %1 Yuzu 找不到 Switch 共享字型 %1 - + Shared Fonts Not Found 找不到共享字型 - + Shared Font Missing 遺失共享字型 - + Fatal Error 嚴重錯誤 - + yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: <a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs. yuzu 發生嚴重錯誤,請檢視紀錄以了解細節。更多資訊請參閱網頁:<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to Upload the Log File</a>。<br/><br/>您要停止模擬並回到遊戲清單嗎?繼續模擬可能會導致當機、存檔損毀或其他錯誤。 - + Fatal Error encountered 發生嚴重錯誤 - + Confirm Key Rederivation 確認重新產生金鑰 - + You are about to force rederive all of your keys. If you do not know what this means or what you are doing, this is a potentially destructive action. @@ -5093,37 +5260,37 @@ This will delete your autogenerated key files and re-run the key derivation modu 這將刪除您自動產生的金鑰檔案並重新執行產生金鑰模組。 - + Missing fuses 遺失項目 - + - Missing BOOT0 - 遺失 BOOT0 - + - Missing BCPKG2-1-Normal-Main - 遺失 BCPKG2-1-Normal-Main - + - Missing PRODINFO - 遺失 PRODINFO - + Derivation Components Missing 遺失產生元件 - + Encryption keys are missing. <br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu quickstart guide</a> to get all your keys, firmware and games.<br><br><small>(%1)</small> 缺少加密金鑰。 <br>請按照<a href='https://yuzu-emu.org/help/quickstart/'>《Yuzu快速入門指南》來取得所有金鑰、韌體、遊戲<br><br><small>(%1)。 - + Deriving keys... This may take up to a minute depending on your system's performance. @@ -5132,39 +5299,39 @@ on your system's performance. 您的系統效能。 - + Deriving Keys 產生金鑰 - + Select RomFS Dump Target 選擇 RomFS 傾印目標 - + Please select which RomFS you would like to dump. 請選擇希望傾印的 RomFS。 - + Are you sure you want to close yuzu? 您確定要關閉 yuzu 嗎? - - - + + + yuzu yuzu - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. 您確定要停止模擬嗎?未儲存的進度將會遺失。 - + The currently running application has requested yuzu to not exit. Would you like to bypass this and exit anyway? @@ -5176,38 +5343,38 @@ Would you like to bypass this and exit anyway? GRenderWindow - + OpenGL not available! 無法使用 OpenGL 模式! - + yuzu has not been compiled with OpenGL support. yuzu 未以支援 OpenGL 的方式編譯。 - - + + Error while initializing OpenGL! 初始化 OpenGL 時發生錯誤! - + Your GPU may not support OpenGL, or you do not have the latest graphics driver. 您的 GPU 可能不支援 OpenGL,或是未安裝最新的圖形驅動程式 - + Error while initializing OpenGL 4.6! 初始化 OpenGL 4.6 時發生錯誤! - + Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1 您的 GPU 可能不支援 OpenGL 4.6,或是未安裝最新的圖形驅動程式<br><br>GL 渲染器:<br>%1 - + Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2 您的 GPU 可能不支援某些必需的 OpenGL 功能。請確保您已安裝最新的圖形驅動程式。<br><br>GL 渲染器:<br>%1<br><br>不支援的功能:<br>%2 @@ -5215,153 +5382,153 @@ Would you like to bypass this and exit anyway? GameList - + Favorite 我的最愛 - + Start Game 開始遊戲 - + Start Game without Custom Configuration 開始遊戲(不使用額外設定) - + Open Save Data Location 開啟存檔位置 - + Open Mod Data Location 開啟模組位置 - + Open Transferable Pipeline Cache 開啟通用著色器管線快取位置 - + Remove 移除 - + Remove Installed Update 移除已安裝的遊戲更新 - + Remove All Installed DLC 移除所有安裝的遊戲更新 - + Remove Custom Configuration 移除額外設定 - + Remove OpenGL Pipeline Cache 刪除 OpenGL 著色器管線快取 - + Remove Vulkan Pipeline Cache 刪除 Vulkan 著色器管線快取 - + Remove All Pipeline Caches 刪除所有著色器管線快取 - + Remove All Installed Contents 移除所有安裝項目 - + Dump RomFS 傾印 RomFS - + Dump RomFS to SDMC 傾印 RomFS 到 SDMC - + Copy Title ID to Clipboard 複製遊戲 ID 到剪貼簿 - + Navigate to GameDB entry 檢視遊戲相容性報告 - + Properties 屬性 - + Scan Subfolders 包含子資料夾 - + Remove Game Directory 移除遊戲資料夾 - + ▲ Move Up ▲ 向上移動 - + ▼ Move Down ▼ 向下移動 - + Open Directory Location 開啟資料夾位置 - + Clear 清除 - + Name 名稱 - + Compatibility 相容性 - + Add-ons 延伸模組 - + File type 檔案格式 - + Size 大小 @@ -5370,76 +5537,61 @@ Would you like to bypass this and exit anyway? GameListItemCompat + Ingame + 进入游戏 + + + + Game starts, but crashes or major glitches prevent it from being completed. + 游戏可以开始,但会出现崩溃或严重故障导致游戏无法继续。 + + + Perfect 完美 - - - Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without -any workarounds needed. - 遊戲能完整進行。不會出現任何圖形或音訊錯誤,所有功能測試皆正常,也不須使用任何替代方案。 - - - - Great - 極佳 - - - - Game functions with minor graphical or audio glitches and is playable from start to finish. May require some -workarounds. - 遊戲能進行。會出現少量圖形或音訊錯誤,可能需要使用一些替代方案以破完遊戲。 - - Okay - 尚可 - - - - Game functions with major graphical or audio glitches, but game is playable from start to finish with -workarounds. - 遊戲能進行。會出現大量圖形或音訊錯誤,但是使用一些替代方案能順利破完遊戲。 + Game can be played without issues. + 游戏可以毫无问题地运行。 - Bad - 不佳 + Playable + 可运行 - Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches -even with workarounds. - 遊戲能進行。但是會出現大量圖形或音訊錯誤,即使使用一些替代方案也無法通過遊戲的某些區域。 + Game functions with minor graphical or audio glitches and is playable from start to finish. + 游戏可以从头到尾完整地运行,但可能出现轻微的图形或音频故障。 - + Intro/Menu 開始畫面/選單 - - Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start -Screen. - 遊戲完全無法進行。因為圖形或音訊的大量錯誤,在通過開始畫面後無法繼續遊戲。 + + Game loads, but is unable to progress past the Start Screen. + 游戏可以加载,但无法通过标题页面。 - + Won't Boot 無法啟動 - + The game crashes when attempting to startup. 啟動遊戲時異常關閉 - + Not Tested 未測試 - + The game has not yet been tested. 此遊戲尚未經過測試 @@ -5447,7 +5599,7 @@ Screen. GameListPlaceholder - + Double-click to add a new folder to the game list 連點兩下以新增資料夾至遊戲清單 @@ -5460,12 +5612,12 @@ Screen. %1 / %n 個結果 - + Filter: 搜尋: - + Enter pattern to filter 輸入文字以搜尋 @@ -5541,12 +5693,12 @@ Screen. HostRoomWindow - + Error 错误 - + Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -> Configure -> Web. If you do not want to publish a room in the public lobby, then select Unlisted instead. Debug Message: 向公共大厅公开房间时失败。为了管理公开房间,您必须在模拟 -> 设置 -> 网络中配置有效的 yuzu 帐户。如果不想在公共大厅中公开房间,请选择“未列出”。 @@ -5556,11 +5708,12 @@ Debug Message: Hotkeys - + Audio Mute/Unmute 静音/关闭静音 + @@ -5582,112 +5735,111 @@ Debug Message: - Main Window 主窗口 - + Audio Volume Down 调低音量 - + Audio Volume Up 调高音量 - + Capture Screenshot 截圖 - + Change Adapting Filter 更改窗口滤镜 - + Change Docked Mode 更改运行模式 - + Change GPU Accuracy 更改 GPU 精度 - + Continue/Pause Emulation 继续/暂停模拟 - + Exit Fullscreen 退出全屏 - + Exit yuzu 退出 yuzu - + Fullscreen 全屏 - + Load File 開啟檔案 - + Load/Remove Amiibo 加载/移除 Amiibo - + Restart Emulation 重新启动模拟 - + Stop Emulation 停止模拟 - + TAS Record TAS 录制 - + TAS Reset 重设 TAS - + TAS Start/Stop TAS 开始/停止 - + Toggle Filter Bar 切换搜索栏 - + Toggle Framerate Limit 切换帧率限制 - + Toggle Mouse Panning 切换鼠标平移 - + Toggle Status Bar 切换状态栏 @@ -5801,42 +5953,42 @@ Debug Message: 刷新游戏大厅 - + Password Required to Join 加入此房间需要密码 - + Password: 密码: - - Room Name - 房间名称 - - - - Preferred Game - 首选游戏 - - - - Host - 管理 - - - + Players 玩家 - + + Room Name + 房间名称 + + + + Preferred Game + 首选游戏 + + + + Host + 管理 + + + Refreshing 刷新中 - + Refresh List 刷新列表 @@ -5859,232 +6011,237 @@ Debug Message: 開啟最近的檔案(&R) - + &Emulation 模擬 (&E) - + &View 檢視 (&V) - + &Reset Window Size 重設視窗大小(&R) - + &Debugging 偵錯 (&D) - + Reset Window Size to &720p 重設視窗大小為 &720p - + Reset Window Size to 720p 重設視窗大小為 720p - + Reset Window Size to &900p 重設視窗大小為 &900p - + Reset Window Size to 900p 重設視窗大小為 900p - + Reset Window Size to &1080p 重設視窗大小為 &1080p - + Reset Window Size to 1080p 重設視窗大小為 1080p - + + &Multiplayer + 多人游戏 (&M) + + + &Tools 工具 (&T) - + &TAS TAS (&T) - + &Help 說明 (&H) - + &Install Files to NAND... &安裝檔案至內部儲存空間 - + L&oad File... 開啟檔案(&O)... - + Load &Folder... 開啟資料夾(&F)... - + E&xit 結束(&X) - + &Pause 暫停(&P) - + &Stop 停止(&S) - + &Reinitialize keys... 重新初始化金鑰(&R)... - + &About yuzu 關於 yuzu(&A) - + Single &Window Mode 單一視窗模式(&W) - + Con&figure... 設定 (&F) - + Display D&ock Widget Headers 顯示 Dock 小工具標題 (&O) - + Show &Filter Bar 顯示搜尋列(&F) - + Show &Status Bar 顯示狀態列(&S) - + Show Status Bar 顯示狀態列 - - - Browse Public Game Lobby - 浏览公共游戏大厅 - - - - Create Room - 创建房间 - - Leave Room - 离开房间 + &Browse Public Game Lobby + 浏览公共游戏大厅 (&B) - - Direct Connect to Room - 直接连接到房间 + + &Create Room + 创建房间 (&C) - - Show Current Room - 显示当前的房间 + + &Leave Room + 离开房间 (&L) + &Direct Connect to Room + 直接连接到房间 (&D) + + + + &Show Current Room + 显示当前房间 (&S) + + + F&ullscreen 全螢幕(&U) - + &Restart 重新啟動(&R) - + Load/Remove &Amiibo... 加载/移除 Amiibo... (&A) - + &Report Compatibility 回報相容性(&R) - + Open &Mods Page 模組資訊 (&M) - + Open &Quickstart Guide 快速入門 (&Q) - + &FAQ 常見問題 (&F) - + Open &yuzu Folder 開啟 yuzu 資料夾(&Y) - + &Capture Screenshot 截圖 (&C) - + &Configure TAS... 設定 &TAS… - + Configure C&urrent Game... 目前遊戲設定...(&U) - + &Start 開始(&S) - + &Reset 重設 (&R) - + R&ecord 錄製 (&E) @@ -6149,47 +6306,42 @@ Debug Message: MultiplayerState - - + Current connection status 当前连接状态 - - + Not Connected. Click here to find a room! 未连接。点击此处查找一个房间! - - - Connected - 已連線 - - - - Not Connected 未连接 - + + Connected + 已連線 + + + + New Messages Received + 收到了新消息 + + + Error 错误 - + Failed to update the room information. Please check your Internet connection and try hosting the room again. Debug Message: 更新房间信息时失败。请检查网络连接并尝试重开房间。 调试信息: - - - New Messages Received - 收到了新消息 - NetworkMessage @@ -6291,22 +6443,41 @@ They may have left the room. 他们可能已经离开了房间。 - + + No valid network interface is selected. +Please go to Configure -> System -> Network and make a selection. + 未选择有效的网络接口。 +请于设置 -> 系统 -> 网络中进行相关设置。 + + + + Game already running + 游戏已在运行中 + + + + Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly. +Proceed anyway? + 不推荐游戏正在运行时加入房间,这可能会导致房间的某些功能出现异常。 +是否继续? + + + Leave Room 离开房间 - + You are about to close the room. Any network connections will be closed. 您正要关闭房间。所有的网络连接都将关闭。 - + Disconnect 断开连接 - + You are about to leave the room. Any network connections will be closed. 您正要离开房间。所有的网络连接都将关闭。 @@ -6314,7 +6485,7 @@ They may have left the room. NetworkMessage::ErrorManager - + Error 错误 @@ -6363,42 +6534,42 @@ p, li { white-space: pre-wrap; } QObject - + %1 is not playing a game %1 不在玩游戏 - + %1 is playing %2 %1 正在玩 %2 - + Not playing a game 不在玩游戏 - + Installed SD Titles 安裝在 SD 卡中的遊戲 - + Installed NAND Titles 安裝在內部儲存空間中的遊戲 - + System Titles 系統項目 - + Add New Game Directory 加入遊戲資料夾 - + Favorites 我的最愛 @@ -6428,7 +6599,7 @@ p, li { white-space: pre-wrap; } - + [not set] [未設定] @@ -6443,10 +6614,10 @@ p, li { white-space: pre-wrap; } - - - - + + + + Axis %1%2 Axis %1%2 @@ -6460,9 +6631,9 @@ p, li { white-space: pre-wrap; } - - - + + + [unknown] [未知] @@ -6627,15 +6798,15 @@ p, li { white-space: pre-wrap; } - + [invalid] [無效] - - + + %1%2Hat %3 %1%2Hat 控制器 %3 @@ -6643,35 +6814,35 @@ p, li { white-space: pre-wrap; } - - - + + + %1%2Axis %3 %1%2軸 %3 - + %1%2Axis %3,%4,%5 %1%2軸 %3,%4,%5 - + %1%2Motion %3 %1%2體感 %3 - - + + %1%2Button %3 %1%2按鈕 %3 - + [unused] [未使用] @@ -6712,11 +6883,124 @@ p, li { white-space: pre-wrap; } 額外按鍵 - + %1%2%3 %1%2%3 + + QtAmiiboSettingsDialog + + + Amiibo Settings + Amiibo 设置 + + + + Amiibo Info + Amiibo 信息 + + + + Series + 系列 + + + + Type + 类型 + + + + Name + 名稱 + + + + Amiibo Data + Amiibo 数据 + + + + Custom Name + 自定义名称 + + + + Owner + 所有者 + + + + Creation Date + 创建日期 + + + + dd/MM/yyyy + 日/月/年 + + + + Modification Date + 修改日期 + + + + dd/MM/yyyy + dd/MM/yyyy + + + + Game Data + 游戏数据 + + + + Game Id + 游戏 ID + + + + Mount Amiibo + 挂载 Amiibo + + + + ... + ... + + + + File Path + 文件路径 + + + + No game data present + 没有游戏数据 + + + + The following amiibo data will be formatted: + 将格式化以下 amiibo 数据: + + + + The following game data will removed: + 将删除以下游戏数据: + + + + Set nickname and owner: + 设置昵称及所有者: + + + + Do you wish to restore this amiibo? + 您想要恢复这个 amiibo 吗? + + QtControllerSelectorDialog @@ -6820,6 +7104,7 @@ p, li { white-space: pre-wrap; } + Handheld 掌機模式 @@ -6859,11 +7144,6 @@ p, li { white-space: pre-wrap; } Docked TV - - - Undocked - 掌機 - Vibration diff --git a/dist/qt_themes/colorful/icons/48x48/bad_folder.png b/dist/qt_themes/colorful/icons/48x48/bad_folder.png index a7ab7a1f635d6c2aaee7ce3490ecbf0082d34bba..34069c6b230de579d436fa9f0c21ae37af905b2e 100644 GIT binary patch delta 503 zcmVMLN`2`L5i1E*bZ0xZA+EWiRRzyd75 z0+!u4eEZa7(PBUW_oJiOcj^3O-H(qLOCa)pFZSO!HzS~4t7FCya6CA!z)2mZY~eTSYnn^Nw}$ z>v}KiDEQb+U3CvQ9-g=)N*xLEKM|z&|4-Fq68}Xh(|h^CH3}8`9|*OzB>g7(FJHJ? tG64xtEsHs}s+0m{xeJ{8Cpa7q$popE`M?eo;|+1` literal 15494 zcmeI3e{2+G8po%F0G9AWW_E!ci(Q` zG*RO}?`C&)pZEJd&olFR-{;x+XP#(WS~sQQ)(VQErZm+1n&3BUd?rnR#}(y+kHfDU z;`J*vikkYa@hRJW@6=fo<+`G@1oVJ^i6E&_izurhU`a*ea5hD`=cVGJ6b8B}1lpBY zt@-NPN6jWht~IZ4_*sA43p$kgo&;#_S=u7?ge8}3p697>rvw-v3Utwwibi6Zkg7GO z;|lP%G0d1v=_z`+*1W(-XbSinOp-L?nG`vc!N0h=Q1|K|ijfU%W%rRjorEC1qY7H8EThzdxV5%FW~VA-M|nVCoozbY$j-6djJYXojsjG~!n3|K$1sEcEYQaraPjw}gn zY9cD?9wjQa11278cQb{-qM7+!C3w|{nt-PTJT^B|YPiTNz0-t-n5K&{2{iaTaKNG{ zvLHLe5HE!!TCzzlT9oY^?X&}UkQ^e*+hnVYv!@~Y{l)GhLi<#y)2J30I@4HWRf6FQ zr)34K!)lY-Xcx=1(HsYyvOKNEGh8yeot82<$kpQ0Q!-qRfQhk%3qI#+*YU5)R zHTWI;zD!Y_@Tt-MAVmo~C~Btq?3(xPr>OE74ZZ~}ssCI#yDWSre#^vb7k6Ksf4C(% z{lix!_M=6WCpY)qwC&u$k>h=j4pd#5b$b4Q=~VB{T{|xSE$0QTRZ!` zci(lx=O4W?V{?Eyc&Xy^|KCNw$eVA!wr%yd{=v7VH&F9Vl{;16(Bju_zBXijy{WwQ z#Kx-jTUXPzgLfbKV%Ld-y=5;xNG+-U>eCzR>q2uMJXAR?P<>(lx{V#nuDm-gaqZvF zPhwBZp7wWT&{S@s^6&3v9ozOs&6@Z7*4@TVo^*@*P}!>G!F?4Iwj7u_`IA|_mrgp$ z=d1h6&hMBz_1OBx$^Y2$DIb21N*&l63@;iV3|Ac9^XH*)&;OyPap%Nw*VorLlfUdA zoG=veKi+ljeDag~&U?d0A2>5OKEC(Ka}U{GezWc!%jQFCyy3UMZa?-y|79xj!8i8z zKf9;z6n~gmwRPZs(sugn(-U7?zIu;~ud9s3g4YL|rDf1R^*r*x+E z7qd64+j4v$`S9V-_;7IFo6{B_dhW_sJ#};5s5%;4+|^vUYwf>gbluU{)Vuxx_klZZ z^vU5T*EW4HPO6;vw=bQ)-&(zU-^sq)4xTuEa!TsItE8*GWxL+#tvuGcecIj)`X5*R s^QP0D`*wfPI<#j7RW|A3*VLll%v>_i@YJ7-CxsgpF7@sIb@0*e0i}o=LjV8( diff --git a/dist/qt_themes/default/icons/256x256/plus_folder.png b/dist/qt_themes/default/icons/256x256/plus_folder.png index 3a49669a3ae3f0ea46ebd5ddd0e51f2b31b37d47..f44c80c3ae25ce0c12c83d13a1ae84efc81d4018 100644 GIT binary patch literal 1948 zcma)-eKZsLAIHD5nN5aU-9;WUY$P#{Ym?Uzu!5(^E>B#&inOwpZ8zye?IvXKW~)!Ms)xH zinPlk0077;LI47`YG2aBX8=%(Bzd?~Q{*$RpiC?YcqtZz=o>c=z5GXg`t?63-4fe! zbFgeWf%WwJvdQ=(-W<2)YQ8C{o& zg%KN&u{8KkWTW87Pi~m!JOZ~0vCs!|#E3H1>6G~+t=_=;aI0LIErC@esf7Lra1lT< zUL{8U2vAuo{Vtqwgu!s|aBrP{@QutN5^`F0_o6&8;gQ@@I?pWGjd-lrrD{pw+McyA z<{Ydy^Jk#r2UV2syE+EOj$OiLBbl@NQp?+UMdpXKjail`u+IF!iDa{DftUI1h+kZE z0GS_^&O^mf2*W1qiNGM7b_(}mY%2?y`xRqy99f!HVTYN)XAGU`*d|ya^VQ;K8|kM- z3jerlBsC(&R(~b-X`bGWviEq7Ot&ga$msHIAeU8&^~lmUa?B{)?D9VH$U6Glq-j0I zVG~O986~Y8NtT8=+mA}G-v6l5Xy=%v5EiO@dM;7SYKk|(E>9S;k=Kh^VA*K0504J-a*hl;+5T^(q55GJg8L zGrVw>)<4d=*HGgH&Kl3&7W3+0{OI$?Hl(;#17=+B&dzr*eq8Ocx&Cu7$Vloc3+V!b zP5W29IX^(D)6G2y-le$U*necf?0F{^?!L6QC%M~=`WTkR0F&k&tS2iKEB;v@=;>aU z-V+rDpKb4vQb{)wg-;N~kLg`4QvOemgBk}HJ|)X2qj0LNQ2wtP0Y>E&w! zprWFIzR*eh{r&s!Yj?tGiji5!4`ydL~0$sKS0scSF>V;8?|7YEM!LB`Z zmR?&w2CJplJim6mDjn2JvbJo64=~OI&6E3Cl|#V!5_WX9Vhaa7s!LFjwY@$Fl+UoH z!XufdDz`gmVpLBCenlBaSoUaK2;YacVe4EgDcFNDl4e|pph5F&ey*#OgNQ;e3okpi z4wTdlpm|Ewy7@s6ac7qW&7Q5pDlTwYt+!~HszTdNn=fl7+iVt9T5A~fapw5%(-^z@ z`jK-=F<0UZ9SfCUs4p-fZ=17S4zl3uEJTv&55+ds3MzUm`s!cN=rO8R;iUfWc$X<9 zsHeJb(GnUqf6lYkc$cu%veQrhJ|p)lL@?r(Hy^ASWmYcejvVWma3e}yRmhqczNHRO z{Qt4szjrOkIM=WYFDgk~|MV3Im{s>|PXL=!^J)zp*`=;MF@^q%nbPwfpN7S=&TLA! z8pY?XtLtm?407y*TQt>JhtO6}*dj29k5*mfg&F@?*%z~H2o=l8TDBV4$XGz}1A^vH zzEO3f|0uK+_ChkgZRnUc5Z^Bf11ISl0O5G96Z=%#_uHYp5KA)D=r+*I%`|aKfKlB{ zJVPF|PtDbeloVc9See_AZ0NV*!?2-@vmgV5Y)oyzM`0m??Zw;f;=2m$5-iYGG0_Ua4T1^8k)8-V#duMR9 zXIl;?XR=ymU5;TUze(1MNnXErYbC>*;oPc336Ad{ZFR^bC;vJVxp7IVgjGB}tzbzn z|4S0x3?Yj*F|neAqv$MK)#FI3Q>ci5<$JoYif+Gx6oLfX?61r1Xu?Ra9W4v{9TJU1 z7g$?YVl`fyh@!qiB4@4tI$$Khwz>EEyCjhGB-mC*$NZF^>vz)^DdIR-#gN}6ekRh2 zXW-4fceM7Yf2c})q?U2LpH}v@xm!)PT~#?UM8l2rg3XhB#r7I^Sa2I^bnl6af#l9= zSa97AF_~Graq9C%$({)7W-ABh(CV_(f)Stg4S(jqR|HN@)z*jx=9Dzo(vvF)-y8In zN;B_{gg0;}oOt89<$}b7@)%a%IE|*ryp<|@O!;-otCVvUqLYVTaL0Al{MS!3CGi%} Yh89WFHBF|yt6vw8JpDWxh~e3P1CC-@r2qf` literal 3521 zcmcIn`8(9xAOFlSc8SJPQ^t~-#a412SGn9)BW(6c;x@zY3(fN#^;bhX} z-;XL2dv@-copp=8Thv%TXkJ%nASc|*KN!b?DWUyoX`CL8EB@xQbA2|R9-e1B{U#O! zFnc`&6%~gfxf1C<^p_8TLN9M)jImpTXM)ST@}1XjbvTw%yqjNhXVFvyT@Mj*SQ!CB zVPe8iZ*pL{yt}t!?yz!<5E$rSkcCaddC4S@gJ7-xSsnOTs68L0wN$FPC1ndx=3ATY zWtiR8$wUTdHW!ns10>HW>LjP8dI9$6@J*G} zJd9OYJ+S$`@!5td$=|S6R6)N9GPgI40*|(IQUl?dz<6EJ^Ztak zN9Loe6P_!qrv-EiF@<*Ou~Knqvsc0nKyvAyt`?LjhS# z1UTV!`JSXv|BI#wx>2x+05uTE+m(SaH+FuMv|K~gU7uka7F4Zv9U5MAOK3LXDvzs0 zq@z6$Fl8s}DA46Gcm%Wd{(}S1WCC-^pqdv=?V)ep*_%%+IgcD zZ^4&I$8A|YqAS>KVQ=yI$maOq9vzS}Pz(uf!+nHKFLtntg`G{(1uLVXO%8nnx5%DI z5Ox&a%e-6f<@`sW)<45A0$Qs$^g1K0KW0)PNYfq=>!n|gC zC*&FeSPO_#lbb{z&V6p+0xWdQU{v#vGYgSoQo!c}U2T7HvbIGLz>}M})aGP~kbd_y ztntQCDc}%OV(JkyQsUu+3O;v|hyecNutZph^Ko+26E8e=A%dKsz-hsg1=V)(TLTcq z_fhAL>tF%>8yk^xg?Mwr%NYye?IQI|0U}FJfMjyU{odqa&uBV}cjXFLMy`XEt3O{l z<-BP8F3n%$9Fzih%@I&)$MOS(e&2vmuhJG>fG0~OO*C&0RhxZ-*LI13BEIbvhE-?g z)FUH`h|3Vm#Kh}hAchNLGdjYbYdxXvSc8D|R@;`5-{av;vgT?IAmHk2mWb+dxomsI z02HA45!`!%!~zy0UP&VX410hcPoo-Y(*a;>@lEKyFCD@V5}_{^ri2B^+w;|Z5j5Z; zr{q{7;?dX2_@dkS8-e)aln8T(8hjF2e3qce*~6L6$iIJUY|CKB!lHv}8`rSl9I=6H}yBv~jC=e$#4DV9U%(ocrqAo{o4kRaW0ykZN z_jPGer3)(ok5nx_u_Q7xv<{|&v&$eEdgzFNwpevzzxgnYBT(|OYHVKKbIM%U+VR^e zQkbg);X9{{zaZG72-co)b)n(+rLDo;?9sa%&xhB**?DP>mt~mOnH!bPLIW9`trST+ zVu54-(G5A{C#{&egeN#X;?scJ1c*6wrnJJG7l82_t z(I$R;Q{yXXEzKS->74v~@`N~@MEc1}N45f@1Lws2mzwt`w_&nJ^xnJ`EkkzBP z;9C_RPONPcT3%A#W#6#_sZe&L&R1%q@_2_B(TqeduqF-28aw;AT)D$ z)`PyGlO(Z*{VL)6AvD$ETP8IHwsHiw?<^k?bahcv&NRn!sYLyamvm(pI_Mp}EjRIvr%WbT|9-UM_Iga` zWYIQYhXJX4mE_20BtEq@eOc4g(=>AgEQK*R)L7lzaL($qZ|9z`HGwuz@~BVs)I)7t z4b~USlVLb9a13uoAS3Kp%gHbCKIDZXfu0BM)m@sF299|A@iq~vcb-m<5P`}MxjILxP>IsjKF})q! z9;sx?a0ea_HaZ5wLQU)wlxfvC)BLkI;CWb>cfxzfx4)vy8xK?>$$iu3oC6aFApUq` z_*c^^p`os>8v12>#W8q?HnBRsfK~)j5?~E}k`dOUU4B$}x8;0w>I=%B3D|pA;JkZ|8@3!?I#l^-3s3sxAJUG?SkIeG^Tm5p^e zUic|%GV7{~3NjsW@2bn7V%HV$f=eP;nw&=nxebh-3$K4iK!pfTn66% z_>kp|YClkn%O9B8YJaN$Oqf{Td-*kS{p5N+Dh}CfvK!-7mAY`uZMMMrs(J7Y~Vl)dTFbW{qH*mRbhD2EJnc_%Crd!Bl z<3(UT{?nlHz}xW+&0es6amtRBv9y18lDp&|n+6pS|80_m$DoHcNX32(DW3P-;H^6l zF2cWxuL_WCIWeD>WQuHlWf&}c@uk}5rsM==Y5^m!?6>YBMU05~Z>E{eF+6d3v(vpl zy<~vKF&NRjtiVtEZ9t7+WQfL8#BiH2J4r70a9r=Un64|i!+~Iyq7$$_ei7eu@{;Tq zO>P?J1E;xNfo#6kI85Ij4gU=GpK1MZ0Z&Q`1Y-m*xwFKRXApVUvE{&@rqioc!x}Ax zDVVrJJ-vm#+Oi?N>~G$PG1Pu#pkENw4r9*_Ju1B9xDI@LSHTeXUnbnz z$>*qZthX)CJI}PS*v}O?MB$n$19w)+bMiAwi>_E2pPMgC#U-k1rDiEb&Rv?JwkOB= z1x%sE891vQ6|1g+sFJ7(VPhL9p~Eien{sope=!#2fNqZc3aOfz*gS;P6?LiYE0{~R zg2U%*Z8i|5h`@IRAdN*a;+=C1D_5*Z9=y8r*YDP# zpWw`2c{AUP4cOoQGgEx}`MH;q-J2%nhd+GlFASKtI&tCo+Zp#XB@M-C>!+^Ogyfhm zezd0F)TPr2vX=1oXWpVl_gGHAIC;OF+ z;|?qRaI?q0(M5`TtQe;WaQ%~p(tmZlEEyJR3F5$tHI)UM#il%IC}vJb6Ezm&hTbyF z6L%D+H*|&qzNnpW}a)D60y@A3MW=L!@P(YY}2|}^`pN|s8uEA(^ zYeZli3m2|=cSq+KyM6*fxm1+cDzOsmas_wqFj(B_z>!Tq&*8AR&S({q7IxJDdhAvIKi2oM%+hPDtItv0jVEva)A;e^EQ~ zt3iYzTj~>?AEx*1DPq7s#cXLB$=uO7Uryl;H9}xAV`}eN%m7AT3!Xi-FVPMHLVBPU z^c0+YmhC6S_B#o|5A}Zfx7ObkBmgG`o4#kmi0aL(Bn(`^fjNYX@mlHJXmy4bf&&6V zznIjlEo5*(WHCPCCJLGoXIc*;E^{f{RHLu<{jG-|DsZ`tC}>tY#->h0V!Z)_Gk!Rq z4=tOrrK`!{n4$y_`pA&3Q!~3l87+RUFf9Hd{mnMt6h0Sbc5dhOf|x)0tULvXHhV4p z^`3e9M>Sv@1FQE!@(g5U%BM7HIB%cC^@1Iu$hQnQm`^N8n>2`N zwxGIYxs@#Isf0u8k#S~t+DgCEt@~%~^dKF6(}cxfhUf9_v{`<;-N7q^)|NCELW+&s zA6xI4n0t`Rh`s#tP5hFD=NOlYb(8PB>}|xRz11K<`?LTzAF%Be#PCE{Z?;XJsm1+5 zu~^_Xm^EzdGRTlm6;ZDS9Z!L)uNax8E`zuZ<4G^!)gb3D?mY_E|MPSmlcP^)$#Dh# zVms>)Y{cnFN0kp35DxnX8ft)O;%1xaOj`1i6ttk?L@)1nzqz1XhsvG z>q7S++a3`51uu3fy)1ZTlMV4tKqWRdoQY_*b$>KxV}YWF9|MUkr++MqUUWO;8x50ErY3Y1C^nvlbS{baxtM!0WByOYmeeV6 z_fa8?BR&A1kt#fr|Cb|EKTQ>$pgxCq-(Vne9&4UG)BKk|7PM(0jtWnHRLV7AZN8NS zmTDwhQ2KCxvoV>GZQ-pfYal4_Vemg}IslcKORT<02*{GZXeDI6s`RbyfGx$>DxHVB zTM4~b6<()415r*VOlM{=;+`_(sXAp6w5Cb6w_~dh`$a%oiq|C&a^i_InKvyx07#f; ziI#sq%tdLrv(IRq;DO^!tRe5XOmM6QxB_7yQNF*enNPjY35McAP;R6lU4_p2nnqT2 z0IOZ{;8SSdv1!36+EV9KjQKGWWK~_At6<*E%j~8coypJp`vQ+TD7GKE5z%-WS}f}$ ztkm>C_fl{hQp!M-I$qovoQ7XpDFjLFlCQpjAdA(;oJ^;=U7vt+O2z967;xyzP*Qe2 z-5DflB<~hNrP=x|q-#8kks!I5?gMKlp)1dD)_yg~IpdXAi9EL7e^mMDG1I0eT z8b(!*JbdeMsvY-t=meGQv4#bSv4BhM4Y1~2ckyEBnO%j7*%r30Fe63V4LB=~_u{&q zv14FN{VlT9>xucAT(L_AZ$j6p!vA%Hu+E3ZoE6tpxY}xIyCh%<+S6M9jY?vKjK+Gw zZ?xocER=1~@1k6c&QFN~iL|Y_RFD9jR*qZnXhjWfuHB%Yg^sGrG#byClJJqzFr@S( zfdreD8%fht;lIS96m6*GiKvP+BRWxKFxF57Lwa)ke(rbd^HV}VxmKzQw{{1`CqRZs z|Ktz@FjeerAMFg8snYGQSyC>kf=!U3WLn3gN(79X+wR# zepFIwB&%&gfN7Cpu0b+NP{|>p<&)t@Hn`etAuajgGo*LQr>pLMyqQ#pcWWfJ1&0y) z;Z;S7*IPwc2H@yXJGoxwEfegcpPx3U=H|W>cV9+DQ8G-7WnLq3xvhvwLs7fj3n#kj zikg_Rtt9yTqaAqq1AOs8w`+ny;9{QZfyt1qpey z1=@9LvSPkt=G@!+Jiwo6d=b@7Cc|yc{>md7Iu2QaxrXTzNrzcTd!dICg9W(T3nE)5 z-O&;IP|xYAtFPt2SbVkk)Z$wx=KM+6WP%$p(j-Kd`yRojzP9bEE3Gw#uyiec`E%p} zR#v!us4W%GT-uI*to}zQ!JI;3N$v||B$y4sueA8{+`Akb_>jTIT{&1U-%6cl#)i3} z&x=b3isyrqhAB+W@ZCHJ(EUMouW*Et_fUdC!7dWISez~dTWL#qF)Yl`48k~9v2pV% z9G0P#n%RE>##Yt`5@dqUkDNxd+leq=wAIk;T*Vy1WtA8BI#ndXHp4D0b$U)qrsc7Y zpv@|2%fA|sMLkm$zNRT_CMxU+bwMjS5yBsghwfC6vo0X=tjXX9Y4A0NMcjb*N>$;_ zE#N0Hw|swGFJd8n#H6e4Pj7q3!Wpe)_EBiksnNQtZpsD=D2joN-3qB*=czbj4GWo= z;kLbG&UW=T5XZa+hVEo$mY+p&i$p{cDl%yjEqT6s<#M)k{Ocm^>Ov1xXw+NSA&H8c z5UAsqAFzcox^AV;dgzW;VMg!CFo}xX*;DEXd*mAc>YoNc3L6Wn)ZFdLH82x0VI?fw z^ZlrnHzkyU5%%18;1-1Cm<-*KHgSQ6J34B}g%}C%MfX`r?{C%CZYeZ^jof6Y@4mbcoxxk;DZHldTCDC4s&}SBQY6M>!(S2l zwi#-IrJY^vJ4pp$y60^_^+sXj$eH$18BqftLb0m!d3r9@X4ZZf9s%>+Z)H{U;7e!V zMMwzZD=ZK;(-T1S1+)B`ySg$K%z68p{Y^Iz#mhTogA#1C)MN%!VbueXf|=NEi_X!0 zPAb+D56V56Pi;_HVIIl>ad&`52qsMh$B-6QX~fZgMbPV*b2Un+qQBFeJAdML8HaF) zHhdGD1rt4@w)d+v*)U{OtW-d6*bER1lddVFJ6*ev+yaRt}3!7&pZ)}W)B^#xrJ zY#_y4wHYMPHWlIKAA}vsM<&st6xcROWw|M4^mYkufyE^lzS?=F2EWAiT!6)zCrW)%3Vd}I-rlwlo=!SkY1-z6S6?yoS#K)1R`D$U z#syA*JJ@~6fTxR;cbHHbH7h$LgkQ{Azu=3jw&VO|k9{v{9a>KUr$-T_bSK?m1IlU* zWY*D2V#%x@(O~vxt-Yu^Y48z;+J)=*JRNxmZpMPpFCt~>nhqCRCmoGWv9<@;Ab>50V{X}U~nxxppn z-NykXbTb+JqcZ9^?!cxeZ49EWOB>IFh<99SEY;}fzIpne>)^1W<_}QL!!|9i(R?#p zl&day2r2}G6b(wk9Hu9{k6b|@e2Wt6G|r3R5Gy-k9<}B1@t1Hq!ZPv()rT&2ve}eV z6xM6irit%iyK9|8AgtPLpzvg)Nn#%7qf%7su7A{re6H=0rwWmyeP*(hCIs!{MbW?{0TK9r3o=8VHo^36WjFU|2F_z!@&u6 zh_3{%J280&8sEgWNm{F7MB3M+Ewm_%0XsU^%4ajFC}wxtbR{>)l&KpZtbYHT$i!2(>lkd{xN7YbLlgpu6sXI^2Y6s tKTd1St6%SOTRYg0|G9cv<<;Nq_gMQ|kEfdP;lD^gWLQk-xnNn|{{X?lcliJS literal 6751 zcmXw8c{o)6_rJ3-wlMa6kL-J7U$SH`MKu^}$~KgpamSWjB9ts8Bz>%fEE!}8p%f$A zNT`e@!ekl0>H9ptzwUj`dA(ofocH_voafxvO|`c*XJO=L1OR}=(!%s206=K35CBO> zd!R#}`v3s{X-iWh$B3`%#o>il`gR8l+U4cFUGhgiIVvL^y5qV00F8crE|q*4hm2JI zax+W+hIjXoyjF?Cr$j(QhMI_gvqRX89AN$YoN$j`WNRz=ZI{8Qh9;7{G17T^^YB=g zmGynDvfAdX+Jf5d<9M{9d4R8Gq_+5#M=&99&+vMgpbTW_X;%%HPbeZRU;XzG^zgpmk6zApIWHTEtb^*_oZ(sMD;G(=~prYaGkForZaZ1^Axacy(IJ0j( zKJJ7Sb0~ku)ZIqI@50dFgFNM}hYcBg(Put8nk0yq8Rm;aEcB!ZwuF7W5I&HgNtoP~ z*+F2Y9HWoXS!l(9uiIJnmp?qaZnspX&1$rSk_qrUsk-*H25tytgDw+F4Baq1m>U?T zfzT0Hm{rqMoj-6B(DD;Im|Fo+pc4sQ;Y7ycs@NRpwi zP476CDJy7u7I+DF>z1?2JVTm*%3`w^bEluod6u-YNFAHMQb2cu&8x~dkyfP4KRmfH7yN6W_5z+38f8F zp*_8%Qq>r?;~NIgKIk(T+D{xGf>#agVsT>$CvSqs8}4czN&-e$=6X&oqtZEr1^uN` zRk9Wfjs|s&L;o>9yRXYk-F*CeEY3QC77c!k50}_f(q+LF|F!~*ztdqDxy`I7f=6SQ ze`L`7%(K+|;0)$1xD>F{;f!c~sNEbi9=YB?(So z*$1ZRzBcode)?KAy8Kz?$2_9zx(9sDzDT|}_8!t4pda?7RSgR$>uyusKB?OX70ZD` z-0Rvgdy@SN5s&F;#}(WL)M2KgOcs}X%Yx|tKmz-}G5BMxE8ZX01za;*%Pvqk7HqFw21`<~JlpD5UsMui{@Z(bUGsQu_u_Iv`jkU9;Q+91uHtCTp{ zt_B{1^MnOZsnJLDWS2TTzw9)okKv2TE4G5(W21*O!JG~3dOu;;S#@*0VSnrPOLu{a zVQ!NNRhR7?K(YtGTgFN0y3X)EWOR z{q!oe@;JbwHe9iu3u~?~kn}IcP2tSSh2ZUI467#Yw%xp&1>iy2@I4UXsj1&NlJh#X zO+|rTZ+Nw&`VUG5LV(eGX9f?gfcwi9q+h&i?HoiQiiMBprV_*=Kal8h&`MFjKGN^{ zN6aiN`u5(OG;HeU#PDIdG8_Z1zs|h-vbUBqd-6uXK94(+@PzP&cKUA#jwZXFl8tXL9MFs-$$Y5KWA-zxO`|){0C{`K!c=enjbYr6G!T;PECBt1@lyd#G z%BwT2dD~J!!J{wfmye}@24&7JOPwm%ji0%Jr?2NJdu{ZpyR|)Up6}w>f{|VO-V(A1 z&BS@zhI3b-^5pe22-4vK(lI<G63eogO%6sk478RUjp&p28{q8&O;&r5DNvXaP_{Pv`>rnHE6+D_U$l8_!gwxM7@{?GdwTN+#|lCLG$I~nEjy1BJ!yUL5f*_FAtCL&?ZoGi z#^Qo$Z2gufI?S0iP`dH0|)T`Y|#E;)PiJfrva^O{8Anq^eVyvnIlWkAn zX?;5Xs*MzL00;G7d@}oshS{>o1_3M2DQL6m9u6A_? z4+#6c^DRjC!h+Q7Z!0qLw?6}TJ!}8-sDM{fkBSXg1jc#T6-fcR&k#68sxITm{Z2-^ zI|C(}3o!Kuz~^F#t=o2oSEiq*;By=N`pqqYU;Qe=U(dn{PtS0wIPiba8dyAwJHG-i zV`_%{c;wJ%nF9=9su1)RfM&J`ltrjzU4b0X%4Rt(`zUr^6hc(j?>I^LY;cglw!0jI zQ4H(746SFc5F|YbV12)5DoRf7Qza`wD2j(m)GJ>kNg|EyBe}gj;BnTLTSUIc$5h4p zKuj)9`0H*;znQB&*!jDaJNo5>8Y=N;j#lDeD4`ey)?s7QZwV$Ai%IVz2FB=+KKqiA44vdHY{p`^z&XK|D!M}1XdZz@CV5_R7I9@q4f3j*YYTcN z*51(#LYxbZE-`uXwr@oRW_7wzp!sH@q>Y5iSLsrCS<~I>F||fN2dfqLqU7lFC))2D z{#3{;CjtK1hj}(?-eXU*MpAuX=AJqD+%nN?JsSnA(E5a>=9gLdfbr+M=J*iOi^N|_ zJ@agTKC}U?B~&FqKmVA~9G$%WY0TOeZL>#`OhAFcSu?6`nsS0Wr|Olkr+*vB`=z-E z&8wzE?b8kunV7uldN?E+3c1Dx&_V6Kr|t7KAVkLf#V+n)+a&XR_kl^LesoYc#&{B5 zR5?1 z6NSHSA!ZAFATr&@420-Lqa5XZkM4jP&*wEtcvFGebl}yVGR#@*kOQJD#s8RQUyC7O z7yw5|=@9MajPwa}^#xl~rhIiPE&vxAG7X(#Or4*UBsyofh*7gwqt4}GH-_7ghAMmH zk6fki#y=W=&UfDo#E+$IiCjF3V;WYQNITqCH(;0hxk^N7ODoQl~BTmq9ct z9io|2E3}GmI{kvMiR&?+>%40;+kq3emYN<2Rl)Cs%l?2aTE?{3;de(*{ ztY*!OJJYQF>ZE<)VFM208Ak}Vn5oQkPx9g*R}LHH+f#c4VYk60$KUcY-S?>m=8JX4SVn*CF8dftG6&T@2)7Iq)m07@e?uh%24?jGA)%U!ES*D zV$jiKxgvJHr;HKfCU5AIMS;3+dk**DhF&>$b@QfKiQ+dQ6y}KO->fq{Nen#G9%9OK z!4Ln#-Z}HUzT|`&Ibh%~;DJn#G#$yOpG$}u@ZR8w2S#95bg1rk z2VG73ouSsj=#?z3i1XLoAQVEcY8h)Ar(~lx6ZXS~B@;kkW;GzoOs5a>+C|FT&SrxX z23U2>{*if*5HvDD8d*;)c(3Fu6C2;V-C#^mnaw9Cfa?)07L8`-^UPn`e&kVvPh}!V zvKDKw5wB@VsJ`22wUe5ZfI}h}_uYHa>V2OZdnGLsxuOclsCeKdLU?D~-JmyC(?jh3 zyInKVq!z>l}W-Og=wYIJvWAZOp_zt2S+X&uCl>_Bi$$> z#LkjE(E|3D7`cu#e#R+A?wGya@C`|<=S1rsh% zgK$}RSOh?eMWfF2?hxK%@dVNm{T2;NYt#6wE zfG+H~q1!@LKwMsCDi|y@BVND_0a`_q&*2h^Pa)a)S%SNqUd!4*w@_Fd^DY{H9)%K@ z90Ixjw!ta)7Y)zTYzVY$5eMYs=x=6>MrekLeIeGIxqF_8vT}iRYq}VjIGHtk2v1kJ zcMcN4V|Bts&*K>kPqcV+Dr@+MQK zF*;)Yue zgWe7Rkd?KExKFVNS~~gJIH<%}&mmKA)TDpXs*ETO1GZv}L*0bV_!~^uVhLfb3qXw2 z8gvVR72UdKyc>?Fgy!nT=;pYQ7oUt2G${fYOU{GSGEjgX5`-~NUiTV)dWyW>+Oglj z6!4G-sNWyef!sy~4pb8>DrO|%Pq{fE23Yh;hi~#@9w=bXx6WW~)hdJ_j7cDbS^Sj_ zxr;DvWlc+}Jd-V84``e-I)(^f)&gx+RqW3P5j- zQJr1my$PX&=Elh18mB_h^Rj|oGqTo^Rn+7N52nfF5OU;>AMy8cq(9}NgRQ4{5OJbc zHCU6IL8cA?$TyR+pH>+8lGEnoV+3aE-v|=r4i+YT(cf7HB4l=MI{6XhwVS2{DjqYTiRHF;xa3A}yZRZp*%{jBW2Iee_T=5~BMb-n` z2;uky43qd~E8IZm!7#=e9`??bxw2a4olm_QwWRXoi&ME2a8ktQ{O5@)LGQB~ z_0gcZQ>5&^5Q!!%G+$#sQ2p^0(

  • JPa8m5Yr$xgK)@(1w5gf%#jdc)j!Uh#5W{-0HM=WA9{1)v82HbQ}Zd7D7an zFFNNvRcHXqZkP`z6(H5_RTFhL2#Qn+DKz_#5d|hb??GwLq7-<-(oEe;efzPO)T1Mk z+WHh_uJ8=>YqZ+!?1t9%Y*4KL)zJ&2Y2PuD(?_b268W`9ki6WsC>i%xM50cOKSD4) zQ5RMyvVPV-?9q>SGz+KEHjD)$??TCU74LPA3HKjsVCz~R??yuPOE+|cz0VHO0pP&&- z=FEw7EM?FBK_C6tJg(P@QG(P#raZ#;P5grlzQtKfLf8cOj?QP&l+5SVwlibZ-96Y& zgl;Bc=!XzC+AZ5hdZN+84h67KpRI2KWqyobqYTD5ob#oM?gPGC={C1d@0A$bzEML% z;2MJG(>kbwFR6wMAYB(xxdk$V5x8#wm>!8pJN33b^^nzhy=uWiZKVJ+%O~euiG^>& z%S0+GA8CAe)b#eUTvoAuN!NmwMZ$tX3#@o>T#+V6RV2iP?r47J=TiGw*`3jOlTH2B zi|)8caXFggoNLd^({C{ORurNVSY=ddUG28-(=R2w?YI*Yi z`g6JMgi7gk*?KmY==1+Xy`IADxY(wko&Xcxs44q2e$Ja)Q>7QkUjyyr)hY6!sc_NO zUFuoQ!xcToxhTV6H5O`H6H-gSH$iVU(#Ay`f(-98s535#-Cc!|xG`rj?LQ3auC&m2 zi?&xWp*5$}{lWkC%}-7GxXn?JtpZxi?Tyiz-08H)(30&If2;D>C+#9(<` q24WP=5QAdb3a7{(wID43sz~k{y7DmLP6+MC5ny@V*0k0b6aRl+8gWDb diff --git a/dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png b/dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png index 002101114d150e304765a3466be9bf267ae96382..14c90fea54ea60d0e2eb024cf5bcca81a366a93f 100644 GIT binary patch literal 1924 zcmb`Hc{JPU8pq!sGLl>bO%ZJc4Wi=K76~$%(ummCcGBuJ*0$4DscJG+Gof}VN~*E6 zwl1SFmP;$0nn6@>Ek%tgVk~oODasf|tGLM@_s{#+z285c&-eMh=e*B3?>X<2?&C#Q zMd~5}0ICevV}1ZYG7AB?k_;PhtR(=H-5JM@25?3frHcGeh7Nu8@V%NEv$%8f5_!W6`ML-MdQ+D4C0#gVnC2wcWNh)*pa!N_g0 zY2G2D7EXh#QnMTwcQsk=IH{6O(T?j+h$tvGQo(}`W00V~A071Mnr>>ztQSpYDciTN z?qTgMBqqHeXXaVk{&bSx(z>5tSx0iHHR!geeEJ1xvZjYfaOty3!^{~*maH1i);rSH z==-oU?=2&2QEsK}-!zFKk4Q%W>Ws@=2 zLUSP>G<73~@t>xv^DND~`Cxp!-xjLyYm?^Z$Gc7X4>#_%6^q_`w>p(7O;TLycEzJ2 zhi-A1(*01UE%wEP7esTM+hqKsy6@YnMMcBna;576=?+GDDzc&F184ben8NFkBUAWd zW?BQ9^8wokBiomO20OCbD0Ge%<+T9e6V?aSsAh@Y0>Tn#K#NS*0l($YdF9f*tq#BM zZ7JC^hchq2bB_e;e#*%ub(wOHly!djAnX8ag~aEi28=%jJCI%e6ybg_JaGDWxF1aY zn+JPp_2d3fCl=P7nS|ihR}^D!{H%Tyi2tb~_P(=f2&W3N95p^oDMaaiy=opM0&G1dx)O(q;pYoA57 zC}K4-v(a4TNCT}#JU}fv5w0BvdIG`a^wvg?D$o>VXDJ$|m~zuUCiTZl!c3J**Ah<6 zU5)Z5C*~X|GfOO#I0nFdljE;?vfOLQmo*DX%%BsnMirPF#pRBAz_&b2*lDeaD`;lA z)Q*>TkFH#8PjUR0#kI3W8N<3rYGaD&|MT@AJU1>n*4snIeNnN_aym!Hy2@!3{WMHY z%69ZXNz=DJpsq?uji`*&2Rs_-*21ficT`9z;nyDq(sYFdiZFE*we;|yWNdre(%T0;FQF?&0WBHLAn2qGp}aP#phG0Aod^Jc0>yiTAMbw$zk zxyL95em))E9AZ>RAa!cQa_5xydNj()2g| z`OW;U#aALFM#smE6Y|CCTu{06fvnAFo7Xcz<8-$%9XpsJgVnMA9aXzX`t-HTZEZ;f zR;_vZs7wcO=VRv&8Lcn0J&ec%U6Stky2*D{R9TJ7Eys1$jDePbL&_0HS-QC-_I_EKiO$qU{vF zlPMgK6gH)WjlI9fn7EEwi&49ov-nYPftgUe?&uN`+czD%(3y5(me|J%ba^&)3&Fe- zFG{0ximo|6O8N5_UF_?=B1#W!vGyP-jD19K4e&=u3bIj!KKN2AL3sCM=O*T%R NFlb)Ks-0Mg{|0pPAie+q literal 3931 zcmcIn`8O0?AHOrmt}sa$Bx#H+St>-6Z4hN-%}!)5TV%;dg|SxViJoHYYX*sIV?>q>P=B$vSwa_b+&VdG9&jb3fnp+;i`_pZmQDX4lcYTq0Zm0A3?QJ#zpcOb7uS zN0^g+V6n#^Eyx_L11fsN7MKd_ovWr-0jNslX1K61bW zjhc-M3tJcOZqb8@e^&lK42@#`@iDc(yOW2Pi%qO#WCii2=^^PM_}}=7{iwX5M#>k8 z8)Xf@l&+s_kXIrkOsM56Xv{n#@5JetE<6&9Ig|1}6B$1e*@U|h z(33tT!1J^hB1rW?)xhRD3!MjtD!ip}guwd=K1Dn*6zpJCR++I52ss$_o@aQuNvCD< zCxe=+VsEL{N^v|&0{?vS=EiEYr2d*JD7QR^Qec)8t_8&#du^K0=CueR$&0XIRKfEfwLG$VR3xEw?NA$+BE;By?S*)6(z;Al!WaXG8TKG>d$X>2)6F zCs#4Or&|prAz#oSTq^2RZ!}Vjp(`iMndG|9jU|q*T|OaW z5)qR~PKEiSlvVHc9$GqLvaH21{gGCF*1r{jb*cKfuHi+$S?}2nitAh0g{@uF(V?&9)NM+j|u3HriJ!ekJIjotwua}5`c0RbV?!L)* z6o%hzA9S=SAVDi*MVyA~uFoOg(X;9M%Vq-llD4ZSxQ|Ek9BTX)JQ0_&6mf#QwOBUj zyB~KwZ=>$UQXl)&CZ^jTjw>z|Gi!zVF42PgYVLG+1p|G_2NKp) z*`uW#%TDJO(nt>2v%`^HgU2_a9z(i8+C386+$OHt)WTuNQ$<4)e#vs5+H>RSZ%0)YKc!U6M zr%c57+7qOdro*8yKwsetlb%lXIc<6~UAOHg7Yx8$`M+LUe}5{ia8QlQW}!)f3;dPX z$f7AXLArdov?@99UIq^W*d*Y{iIh8P>beh#ZQ|3#;Nws>5Npm)b1!0aDocodFtjb- z9$2^kCnR{>sSzT#dQgz5Zah0fWU0>UZpra2Ue4cMZnDFN;{DanA9y7JY^j&(u!W=9 z0l~g}n?ibV+)0Wks+dA5bdG_7;20d)4Y#%yM)+l4RjIA%2{Ze~mc+C(kEPrD{>{b6 zz2UR0rW>qmU|#t+B=li+vhAE9;e)6!(*UXB5Gxr9F>yY7s0#t%df(s~5>(_|*%1uD zimRYYeqJY+*|d!M`kOGl@dl98Qd<}mc`*SB=vhu3wceu4xXB-b6-!E4Op|!DY9k*E zE5;<%DvF-_i13ZnWG90y`9#0r&lsSb!$+$4WjbrsO)9*c8& zVq@Z_4Hs%=D0AQY(7{x@z`k_3i08iMCB=;qhSrUpJB)V+&;fmjVTMK}kv%#tFvdJ` z+wC!<`5;qs#!V6f!|Ij%@UNgMxTm^aRX-SQ4=?9*5h}?6(uVN+O(NM=V+kosI)QGH zCelx#X@&JT#Y_FN56@kZ5zvJgCOMX9`DUM_YIu7@`bs~BrezJ{nUBEasfS&IAEnK& zLJZ>_b1qt`%xg_+{6y`in9isQpdf~cL`Cb_pv8`gYBIqChOJpXHjW%#*EAF4Lfxv3 z_ro)4B?Xwd*Wh!s)I|9|8Q0*wXU9!+!jxl@m>c(h6pivx-wWwplqZyC$_7Q#c$U6Y z^rR)%{GesHb*XEqXQ^*#prsBso^>Y~+IMvfRa9ond{e}T%Z0whNtiwnq6$%ys7t&? z94rWc=jkErM?1bC^0Y zqBVwsc#%z8bF z6NVM2Y28^V9(Cc*7wVq$^pu#U8aakKZ}fP#1o^MM^Lk#)++tAX?58cCahq> zuV-pKA#8-#Z(S27t3rJHpvca%{2ZDC=lsVT#Au~X+ z6|kf8WxWtUfLVq@?L55hlaM?Cqz5H`48Tr)pPLy|O0Y5m#-6Go+DDWWyK>mO5_>ss z=|K={JhOPE1ch+R??ETeV1O}>cnKY+9^8LETp=rA(378yz&l10D*irP6ydR?J{=DH z2%yGDbb8eLy4j?x5P{EeYxl|7NI`1#0s&4-?T87@?uXKWL? zdy~TB{_I!9qaIoVUE_Ddgm#5bF!rwNdGt!AORYxuKy^b&?7wA-R`q$xn00o`&$5<# zrPU8I%DZk-hFz-7iLzhP(RlWnou>IG^?jQ^M4K&E&T*l1==QtRla^EPc|I-t-X|}M z{0b;p0i>Ph`hN=Q)?|_&elq^QH{i>)s<#7Qs`Q}<;AJZ^jtpS``Pd8$1X}x+1&8`R z1I(+lYMI6R0#9?>3zg)DVIQlC@J^wl{WHowecRSd-_5v5o&Q4-ZCQEL;DR^Lu+f|J zZ5zX5Y?3ZjKg9FX)n%r$)#H1zFEA-+rZ1{G@Tv0iI*xs>-2sqG`hs&$cZ{P@ zqCyhV5G&oNK)4z)uHN{~vIDkZa%@z(7Cg1Kc6uLBUC6? z=j03FAy$y8yV#~Xl^p7I-`{;__4*yqmBT{H&>AJfyLj_yzIX-1j-vQ6@=#^`qZ`nSZwHK9F?MFr7Df6k(li_#SM1@L!E%; zJKJ~Zx!;+MHNLn~1g-27WHxh7QQ78^`QoTo3@e6Ez);IfI0!JDcHzVZJM@q2_!acd z`@kwoM6yMi?pFbn@uay-F}N(~VO=kkMw%{bwdbb-X=>5FW()5J<5UBP_L*&dcVA{C z*Q8ag6=WNo!F!}>rSL9If$Z+Whmuloe7`Y{<;Qx#%;w?*fW?=_&4Xi;Sf6N~@R?*% z|5|Id7!@Q1cJThYz$?ZM({XWc6)3{sdYS22U_#ckORRVbnj$O);{#n z$fj2}+r+Tq8&R|QO6tn;$za!0`d-p238eh-V*pRnJ~RrCAxUIBZu)Em+GH_(7w-BO{*{lyOJali+h+b%nc(3+&L~%3W$YA;gtYwXZ z5Wea|DmWBh34Z9~!`hc^z(SfFwV%Z@iy^<2MB2d*Tb0JMQsAEkCFsSkZpg*S9*sDO zO|dQQGAj%z-LUI00Q+95(;t7SyVw3QQe-is2+Tiz1arasj@hxTRTT2s7JEbN=L zm)*hB+0%O$5!!#RT?k=NuD21J03CBGEL%iE;;YlB^{7?J$`*@O7yKKMUME}EU|B?* zWeIaw!Xi0DOV(FgZ@04_J1a|4+||Gl4S#NMU+U+OrJdP|x|%}zd*X7@c19+#dEtlq oR#{sMLFj+@N-{%|Tz$&dqmf1)&@uP)=YMjH^snnx=r}+64+>Wf+5i9m diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index e80fd12..4ffafd1 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -6,19 +6,16 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/externals/find-modules") include(DownloadExternals) # xbyak -if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) - add_library(xbyak INTERFACE) - file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include) - file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include) - target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include) - target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) +if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak) + add_subdirectory(xbyak EXCLUDE_FROM_ALL) endif() # Dynarmic -if (ARCHITECTURE_x86_64) +if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) AND NOT TARGET dynarmic::dynarmic) set(DYNARMIC_NO_BUNDLED_FMT ON) set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE) - add_subdirectory(dynarmic) + add_subdirectory(dynarmic EXCLUDE_FROM_ALL) + add_library(dynarmic::dynarmic ALIAS dynarmic) endif() # getopt @@ -30,7 +27,9 @@ endif() add_subdirectory(glad) # inih -add_subdirectory(inih) +if (NOT TARGET inih::INIReader) + add_subdirectory(inih) +endif() # mbedtls add_subdirectory(mbedtls EXCLUDE_FROM_ALL) @@ -46,8 +45,8 @@ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER endif() # libusb -if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB) - add_subdirectory(libusb) +if (NOT TARGET libusb::usb) + add_subdirectory(libusb EXCLUDE_FROM_ALL) endif() # SDL2 @@ -68,35 +67,46 @@ if (YUZU_USE_EXTERNAL_SDL2) endif() set(SDL_STATIC ON) set(SDL_SHARED OFF) + if (APPLE) + set(SDL_FILE ON) + endif() add_subdirectory(SDL EXCLUDE_FROM_ALL) - add_library(SDL2 ALIAS SDL2-static) endif() # ENet -add_subdirectory(enet) -target_include_directories(enet INTERFACE ./enet/include) +if (NOT TARGET enet::enet) + add_subdirectory(enet EXCLUDE_FROM_ALL) + target_include_directories(enet INTERFACE ./enet/include) + add_library(enet::enet ALIAS enet) +endif() # Cubeb -if(ENABLE_CUBEB) +if (ENABLE_CUBEB AND NOT TARGET cubeb::cubeb) set(BUILD_TESTS OFF CACHE BOOL "") add_subdirectory(cubeb EXCLUDE_FROM_ALL) + add_library(cubeb::cubeb ALIAS cubeb) endif() # DiscordRPC -if (USE_DISCORD_PRESENCE) +if (USE_DISCORD_PRESENCE AND NOT TARGET DiscordRPC::discord-rpc) add_subdirectory(discord-rpc EXCLUDE_FROM_ALL) target_include_directories(discord-rpc INTERFACE ./discord-rpc/include) + add_library(DiscordRPC::discord-rpc ALIAS discord-rpc) endif() # Sirit -add_subdirectory(sirit) +add_subdirectory(sirit EXCLUDE_FROM_ALL) -if (ENABLE_WEB_SERVICE) - find_package(OpenSSL 1.1) - if (OPENSSL_FOUND) - set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) - else() +if (ENABLE_WEB_SERVICE AND NOT TARGET httplib::httplib) + if (NOT WIN32) + find_package(OpenSSL 1.1) + if (OPENSSL_FOUND) + set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) + endif() + endif() + + if (WIN32 OR NOT OPENSSL_FOUND) # LibreSSL set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "") set(OPENSSLDIR "/etc/ssl/") @@ -116,18 +126,20 @@ if (ENABLE_WEB_SERVICE) if (WIN32) target_link_libraries(httplib INTERFACE crypt32 cryptui ws2_32) endif() - - # cpp-jwt + add_library(httplib::httplib ALIAS httplib) +endif() + +# cpp-jwt +if (ENABLE_WEB_SERVICE AND NOT TARGET cpp-jwt::cpp-jwt) add_library(cpp-jwt INTERFACE) target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include) target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON) + add_library(cpp-jwt::cpp-jwt ALIAS cpp-jwt) endif() # Opus -if (YUZU_USE_BUNDLED_OPUS) +if (NOT TARGET Opus::opus) add_subdirectory(opus EXCLUDE_FROM_ALL) -else() - find_package(Opus 1.3 REQUIRED) endif() # FFMpeg @@ -138,3 +150,8 @@ if (YUZU_USE_BUNDLED_FFMPEG) set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE) set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE) endif() + +# Vulkan-Headers +if (NOT TARGET Vulkan::Headers) + add_subdirectory(Vulkan-Headers EXCLUDE_FROM_ALL) +endif() diff --git a/externals/find-modules/FindDiscordRPC.cmake b/externals/find-modules/FindDiscordRPC.cmake new file mode 100644 index 0000000..44ca990 --- /dev/null +++ b/externals/find-modules/FindDiscordRPC.cmake @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2022 Alexandre Bouvier +# +# SPDX-License-Identifier: GPL-3.0-or-later + +find_path(DiscordRPC_INCLUDE_DIR discord_rpc.h) + +find_library(DiscordRPC_LIBRARY discord-rpc) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(DiscordRPC + REQUIRED_VARS + DiscordRPC_LIBRARY + DiscordRPC_INCLUDE_DIR +) + +if (DiscordRPC_FOUND AND NOT TARGET DiscordRPC::discord-rpc) + add_library(DiscordRPC::discord-rpc UNKNOWN IMPORTED) + set_target_properties(DiscordRPC::discord-rpc PROPERTIES + IMPORTED_LOCATION "${DiscordRPC_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${DiscordRPC_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced( + DiscordRPC_INCLUDE_DIR + DiscordRPC_LIBRARY +) diff --git a/externals/find-modules/FindFFmpeg.cmake b/externals/find-modules/FindFFmpeg.cmake index add5b2c..eedf28a 100644 --- a/externals/find-modules/FindFFmpeg.cmake +++ b/externals/find-modules/FindFFmpeg.cmake @@ -185,3 +185,11 @@ foreach(c ${_FFmpeg_ALL_COMPONENTS}) endforeach() unset(_FFmpeg_ALL_COMPONENTS) unset(_FFmpeg_REQUIRED_VARS) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FFmpeg + REQUIRED_VARS + FFmpeg_LIBRARIES + FFmpeg_INCLUDE_DIR + HANDLE_COMPONENTS +) diff --git a/externals/find-modules/FindLibUSB.cmake b/externals/find-modules/FindLibUSB.cmake deleted file mode 100644 index 617daf9..0000000 --- a/externals/find-modules/FindLibUSB.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# SPDX-FileCopyrightText: 2009 Michal Cihar -# SPDX-License-Identifier: GPL-2.0-or-later - -# - Find libusb-1.0 library -# This module defines -# LIBUSB_INCLUDE_DIR, where to find bluetooth.h -# LIBUSB_LIBRARIES, the libraries needed to use libusb-1.0. -# LIBUSB_FOUND, If false, do not try to use libusb-1.0. -# -# vim: expandtab sw=4 ts=4 sts=4: - -if(ANDROID) - set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found") - message(STATUS "libusb-1.0 not found.") -elseif (NOT LIBUSB_FOUND) - pkg_check_modules (LIBUSB_PKG libusb-1.0) - - find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h - PATHS - ${LIBUSB_PKG_INCLUDE_DIRS} - /usr/include/libusb-1.0 - /usr/include - /usr/local/include/libusb-1.0 - /usr/local/include - ) - - find_library(LIBUSB_LIBRARIES NAMES usb-1.0 usb - PATHS - ${LIBUSB_PKG_LIBRARY_DIRS} - /usr/lib - /usr/local/lib - ) - - if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) - set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found") - message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}") - else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) - set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found") - message(STATUS "libusb-1.0 not found.") - endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) - - mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES) -endif () - diff --git a/externals/find-modules/FindOpus.cmake b/externals/find-modules/FindOpus.cmake index b68a604..25a44fd 100644 --- a/externals/find-modules/FindOpus.cmake +++ b/externals/find-modules/FindOpus.cmake @@ -1,19 +1,15 @@ # SPDX-FileCopyrightText: 2022 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -find_package(PkgConfig) - -if (PKG_CONFIG_FOUND) - pkg_search_module(opus IMPORTED_TARGET GLOBAL opus) - if (opus_FOUND) - add_library(Opus::opus ALIAS PkgConfig::opus) - endif() -endif() +find_package(PkgConfig QUIET) +pkg_search_module(OPUS QUIET IMPORTED_TARGET opus) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Opus - REQUIRED_VARS - opus_LINK_LIBRARIES - opus_FOUND - VERSION_VAR opus_VERSION + REQUIRED_VARS OPUS_LINK_LIBRARIES + VERSION_VAR OPUS_VERSION ) + +if (Opus_FOUND AND NOT TARGET Opus::opus) + add_library(Opus::opus ALIAS PkgConfig::OPUS) +endif() diff --git a/externals/find-modules/Findenet.cmake b/externals/find-modules/Findenet.cmake new file mode 100644 index 0000000..859a6f3 --- /dev/null +++ b/externals/find-modules/Findenet.cmake @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2022 Alexandre Bouvier +# +# SPDX-License-Identifier: GPL-3.0-or-later + +find_package(PkgConfig QUIET) +pkg_search_module(ENET QUIET IMPORTED_TARGET libenet) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(enet + REQUIRED_VARS ENET_LINK_LIBRARIES + VERSION_VAR ENET_VERSION +) + +if (enet_FOUND AND NOT TARGET enet::enet) + add_library(enet::enet ALIAS PkgConfig::ENET) +endif() diff --git a/externals/find-modules/Findhttplib.cmake b/externals/find-modules/Findhttplib.cmake new file mode 100644 index 0000000..4d17cb3 --- /dev/null +++ b/externals/find-modules/Findhttplib.cmake @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2022 Andrea Pappacoda +# +# SPDX-License-Identifier: GPL-2.0-or-later + +include(FindPackageHandleStandardArgs) + +find_package(httplib QUIET CONFIG) +if (httplib_FOUND) + find_package_handle_standard_args(httplib CONFIG_MODE) +else() + find_package(PkgConfig QUIET) + pkg_search_module(HTTPLIB QUIET IMPORTED_TARGET cpp-httplib) + find_package_handle_standard_args(httplib + REQUIRED_VARS HTTPLIB_INCLUDEDIR + VERSION_VAR HTTPLIB_VERSION + ) +endif() + +if (httplib_FOUND AND NOT TARGET httplib::httplib) + add_library(httplib::httplib ALIAS PkgConfig::HTTPLIB) +endif() diff --git a/externals/find-modules/Findinih.cmake b/externals/find-modules/Findinih.cmake new file mode 100644 index 0000000..b8d38dc --- /dev/null +++ b/externals/find-modules/Findinih.cmake @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2022 Alexandre Bouvier +# +# SPDX-License-Identifier: GPL-3.0-or-later + +find_package(PkgConfig QUIET) +pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(inih + REQUIRED_VARS INIREADER_LINK_LIBRARIES + VERSION_VAR INIREADER_VERSION +) + +if (inih_FOUND AND NOT TARGET inih::INIReader) + add_library(inih::INIReader ALIAS PkgConfig::INIREADER) +endif() diff --git a/externals/find-modules/Findlibusb.cmake b/externals/find-modules/Findlibusb.cmake new file mode 100644 index 0000000..0eadce9 --- /dev/null +++ b/externals/find-modules/Findlibusb.cmake @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2022 Alexandre Bouvier +# +# SPDX-License-Identifier: GPL-3.0-or-later + +find_package(PkgConfig QUIET) +pkg_search_module(LIBUSB QUIET IMPORTED_TARGET libusb-1.0) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(libusb + REQUIRED_VARS LIBUSB_LINK_LIBRARIES + VERSION_VAR LIBUSB_VERSION +) + +if (libusb_FOUND AND NOT TARGET libusb::usb) + add_library(libusb::usb ALIAS PkgConfig::LIBUSB) +endif() diff --git a/externals/find-modules/Findlz4.cmake b/externals/find-modules/Findlz4.cmake index 13ca5de..c82405c 100644 --- a/externals/find-modules/Findlz4.cmake +++ b/externals/find-modules/Findlz4.cmake @@ -1,19 +1,26 @@ # SPDX-FileCopyrightText: 2022 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -find_package(PkgConfig) +include(FindPackageHandleStandardArgs) -if (PKG_CONFIG_FOUND) - pkg_search_module(liblz4 IMPORTED_TARGET GLOBAL liblz4) - if (liblz4_FOUND) - add_library(lz4::lz4 ALIAS PkgConfig::liblz4) - endif() +find_package(lz4 QUIET CONFIG) +if (lz4_FOUND) + find_package_handle_standard_args(lz4 CONFIG_MODE) +else() + find_package(PkgConfig QUIET) + pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4) + find_package_handle_standard_args(lz4 + REQUIRED_VARS LZ4_LINK_LIBRARIES + VERSION_VAR LZ4_VERSION + ) endif() -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(lz4 - REQUIRED_VARS - liblz4_LINK_LIBRARIES - liblz4_FOUND - VERSION_VAR liblz4_VERSION -) +if (lz4_FOUND AND NOT TARGET lz4::lz4) + if (TARGET LZ4::lz4_shared) + add_library(lz4::lz4 ALIAS LZ4::lz4_shared) + elseif (TARGET LZ4::lz4_static) + add_library(lz4::lz4 ALIAS LZ4::lz4_static) + else() + add_library(lz4::lz4 ALIAS PkgConfig::LZ4) + endif() +endif() diff --git a/externals/find-modules/Findzstd.cmake b/externals/find-modules/Findzstd.cmake index f4031eb..f6eb964 100644 --- a/externals/find-modules/Findzstd.cmake +++ b/externals/find-modules/Findzstd.cmake @@ -1,19 +1,26 @@ # SPDX-FileCopyrightText: 2022 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -find_package(PkgConfig) +include(FindPackageHandleStandardArgs) -if (PKG_CONFIG_FOUND) - pkg_search_module(libzstd IMPORTED_TARGET GLOBAL libzstd) - if (libzstd_FOUND) - add_library(zstd::zstd ALIAS PkgConfig::libzstd) - endif() +find_package(zstd QUIET CONFIG) +if (zstd_FOUND) + find_package_handle_standard_args(zstd CONFIG_MODE) +else() + find_package(PkgConfig QUIET) + pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd) + find_package_handle_standard_args(zstd + REQUIRED_VARS ZSTD_LINK_LIBRARIES + VERSION_VAR ZSTD_VERSION + ) endif() -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(zstd - REQUIRED_VARS - libzstd_LINK_LIBRARIES - libzstd_FOUND - VERSION_VAR libzstd_VERSION -) +if (zstd_FOUND AND NOT TARGET zstd::zstd) + if (TARGET zstd::libzstd_shared) + add_library(zstd::zstd ALIAS zstd::libzstd_shared) + elseif (TARGET zstd::libzstd_static) + add_library(zstd::zstd ALIAS zstd::libzstd_static) + else() + add_library(zstd::zstd ALIAS PkgConfig::ZSTD) + endif() +endif() diff --git a/externals/inih/CMakeLists.txt b/externals/inih/CMakeLists.txt index b686e3c..ebb60a9 100644 --- a/externals/inih/CMakeLists.txt +++ b/externals/inih/CMakeLists.txt @@ -9,4 +9,5 @@ add_library(inih ) create_target_directory_groups(inih) -target_include_directories(inih INTERFACE .) +target_include_directories(inih INTERFACE inih/cpp) +add_library(inih::INIReader ALIAS inih) diff --git a/externals/libusb/CMakeLists.txt b/externals/libusb/CMakeLists.txt index 055b892..6317ea8 100644 --- a/externals/libusb/CMakeLists.txt +++ b/externals/libusb/CMakeLists.txt @@ -108,7 +108,7 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE) target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}") if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") - Include(FindPkgConfig) + find_package(PkgConfig) pkg_check_modules(LIBUDEV REQUIRED libudev) if (LIBUDEV_FOUND) @@ -273,3 +273,5 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") configure_file(config.h.in config.h) endif() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + +add_library(libusb::usb ALIAS usb) diff --git a/externals/microprofile/microprofileui.h b/externals/microprofile/microprofileui.h index 1357a08..ca9fe70 100644 --- a/externals/microprofile/microprofileui.h +++ b/externals/microprofile/microprofileui.h @@ -845,8 +845,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int MicroProfile& S = *MicroProfileGet(); MP_DEBUG_DUMP_RANGE(); int nY = nBaseY - UI.nOffsetY; - int64_t nNumBoxes = 0; - int64_t nNumLines = 0; uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY; MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent]; @@ -1149,7 +1147,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int } } #endif - ++nNumBoxes; } else { @@ -1165,7 +1162,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int } nLinesDrawn[nStackPos] = nLineX; MicroProfileDrawLineVertical(nLineX, fYStart + 0.5f, fYEnd + 0.5f, nColor|UI.nOpacityForeground); - ++nNumLines; } } nStackPos--; diff --git a/sirit/include/sirit/sirit.h b/sirit/include/sirit/sirit.h index 589ea06..6ced1a8 100644 --- a/sirit/include/sirit/sirit.h +++ b/sirit/include/sirit/sirit.h @@ -309,12 +309,16 @@ public: /// Return a value from a function. Id OpReturnValue(Id value); - /// Fragment-shader discard. + /// Deprecated fragment-shader discard. void OpKill(); /// Demote fragment shader invocation to a helper invocation + void OpDemoteToHelperInvocation(); void OpDemoteToHelperInvocationEXT(); + /// Fragment-shader discard. + void OpTerminateInvocation(); + // Debug /// Assign a name string to a reference. @@ -1156,6 +1160,13 @@ public: /// TBD Id OpSubgroupAllEqualKHR(Id result_type, Id predicate); + // Result is the Value of the invocation identified by the id Id to all active invocations in + // the group. + Id OpGroupNonUniformBroadcast(Id result_type, Id scope, Id value, Id id); + + // Result is the Value of the invocation identified by the id Id. + Id OpGroupNonUniformShuffle(Id result_type, Id scope, Id value, Id id); + /// Return the value of the invocation identified by the current invocation's id within the /// group xor'ed with mask. Id OpGroupNonUniformShuffleXor(Id result_type, Id scope, Id value, Id mask); diff --git a/sirit/src/instructions/flow.cpp b/sirit/src/instructions/flow.cpp index e462dcb..5f6b693 100644 --- a/sirit/src/instructions/flow.cpp +++ b/sirit/src/instructions/flow.cpp @@ -92,9 +92,18 @@ void Module::OpKill() { *code << spv::Op::OpKill << EndOp{}; } -void Module::OpDemoteToHelperInvocationEXT() { +void Module::OpDemoteToHelperInvocation() { code->Reserve(1); - *code << spv::Op::OpDemoteToHelperInvocationEXT << EndOp{}; + *code << spv::Op::OpDemoteToHelperInvocation << EndOp{}; +} + +void Module::OpDemoteToHelperInvocationEXT() { + OpDemoteToHelperInvocation(); +} + +void Module::OpTerminateInvocation() { + code->Reserve(1); + *code << spv::Op::OpTerminateInvocation << EndOp{}; } } // namespace Sirit diff --git a/sirit/src/instructions/group.cpp b/sirit/src/instructions/group.cpp index 274b5b6..3b6f71a 100644 --- a/sirit/src/instructions/group.cpp +++ b/sirit/src/instructions/group.cpp @@ -36,6 +36,18 @@ Id Module::OpSubgroupAllEqualKHR(Id result_type, Id predicate) { return *code << OpId{spv::Op::OpSubgroupAllEqualKHR, result_type} << predicate << EndOp{}; } +Id Module::OpGroupNonUniformBroadcast(Id result_type, Id scope, Id value, Id id) { + code->Reserve(6); + return *code << OpId{spv::Op::OpGroupNonUniformBroadcast, result_type} << scope << value + << id << EndOp{}; +} + +Id Module::OpGroupNonUniformShuffle(Id result_type, Id scope, Id value, Id id) { + code->Reserve(6); + return *code << OpId{spv::Op::OpGroupNonUniformShuffle, result_type} << scope << value << id + << EndOp{}; +} + Id Module::OpGroupNonUniformShuffleXor(Id result_type, Id scope, Id value, Id mask) { code->Reserve(6); return *code << OpId{spv::Op::OpGroupNonUniformShuffleXor, result_type} << scope << value diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54de1dc..1404154 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -58,13 +58,11 @@ if (MSVC) # Warnings /W3 - /we4018 # 'expression': signed/unsigned mismatch + /WX + /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled - /we4101 # 'identifier': unreferenced local variable /we4189 # 'identifier': local variable is initialized but not referenced /we4265 # 'class': class has virtual functions, but destructor is not virtual - /we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data - /we4305 # 'context': truncation from 'type1' to 'type2' /we4388 # 'expression': signed/unsigned mismatch /we4389 # 'operator': signed/unsigned mismatch /we4456 # Declaration of 'identifier' hides previous local declaration @@ -75,14 +73,18 @@ if (MSVC) /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? /we4555 # Expression has no effect; expected expression with side-effect - /we4715 # 'function': not all control paths return a value - /we4834 # Discarding return value of function with 'nodiscard' attribute + /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. /we5038 # data member 'member1' will be initialized after data member 'member2' + /we5233 # explicit lambda capture 'identifier' is not used /we5245 # 'function': unreferenced function with internal linkage has been removed + + /wd4100 # 'identifier': unreferenced formal parameter + /wd4324 # 'struct_name': structure was padded due to __declspec(align()) ) - if (USE_CCACHE) + if (USE_CCACHE OR YUZU_USE_PRECOMPILED_HEADERS) # when caching, we need to use /Z7 to downgrade debug info to use an older but more cachable format + # Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21 add_compile_options(/Z7) else() add_compile_options(/Zi) @@ -99,28 +101,25 @@ if (MSVC) set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) else() add_compile_options( - -Wall - -Werror=array-bounds - -Werror=implicit-fallthrough + -Werror=all + -Werror=extra -Werror=missing-declarations - -Werror=missing-field-initializers - -Werror=reorder -Werror=shadow - -Werror=sign-compare - -Werror=switch - -Werror=uninitialized - -Werror=unused-function - -Werror=unused-result - -Werror=unused-variable - -Wextra - -Wmissing-declarations + -Werror=unused + -Wno-attributes -Wno-invalid-offsetof -Wno-unused-parameter + + $<$:-Wno-braced-scalar-init> + $<$:-Wno-unused-private-field> + $<$:-Wno-braced-scalar-init> + $<$:-Wno-unused-private-field> ) if (ARCHITECTURE_x86_64) add_compile_options("-mcx16") + add_compile_options("-fwrapv") endif() if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 144f1ba..420ba62 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -31,6 +31,7 @@ add_library(audio_core STATIC out/audio_out.h out/audio_out_system.cpp out/audio_out_system.h + precompiled_headers.h renderer/adsp/adsp.cpp renderer/adsp/adsp.h renderer/adsp/audio_renderer.cpp @@ -206,35 +207,30 @@ if (MSVC) /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data - /we4456 # Declaration of 'identifier' hides previous local declaration - /we4457 # Declaration of 'identifier' hides function parameter - /we4458 # Declaration of 'identifier' hides class member - /we4459 # Declaration of 'identifier' hides global declaration + /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) else() target_compile_options(audio_core PRIVATE -Werror=conversion - -Werror=ignored-qualifiers - -Werror=shadow - -Werror=unused-variable - - $<$:-Werror=unused-but-set-parameter> - $<$:-Werror=unused-but-set-variable> -Wno-sign-conversion ) endif() target_link_libraries(audio_core PUBLIC common core) -if (ARCHITECTURE_x86_64) - target_link_libraries(audio_core PRIVATE dynarmic) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) + target_link_libraries(audio_core PRIVATE dynarmic::dynarmic) endif() if(ENABLE_CUBEB) - target_link_libraries(audio_core PRIVATE cubeb) + target_link_libraries(audio_core PRIVATE cubeb::cubeb) target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1) endif() if(ENABLE_SDL2) - target_link_libraries(audio_core PRIVATE SDL2) + target_link_libraries(audio_core PRIVATE SDL2::SDL2) target_compile_definitions(audio_core PRIVATE HAVE_SDL2) endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(audio_core PRIVATE precompiled_headers.h) +endif() diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index c845330..07a679c 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -8,7 +8,7 @@ namespace AudioCore { -AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique(system)} { +AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique()} { CreateSinks(); // Must be created after the sinks adsp = std::make_unique(system, *output_sink); diff --git a/src/audio_core/audio_event.cpp b/src/audio_core/audio_event.cpp index 424049c..d15568e 100644 --- a/src/audio_core/audio_event.cpp +++ b/src/audio_core/audio_event.cpp @@ -3,6 +3,7 @@ #include "audio_core/audio_event.h" #include "common/assert.h" +#include "common/polyfill_ranges.h" namespace AudioCore { diff --git a/src/audio_core/audio_manager.cpp b/src/audio_core/audio_manager.cpp index 2f1bba9..2acde66 100644 --- a/src/audio_core/audio_manager.cpp +++ b/src/audio_core/audio_manager.cpp @@ -1,14 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio_core/audio_in_manager.h" #include "audio_core/audio_manager.h" -#include "audio_core/audio_out_manager.h" #include "core/core.h" +#include "core/hle/service/audio/errors.h" namespace AudioCore { -AudioManager::AudioManager(Core::System& system_) : system{system_} { +AudioManager::AudioManager() { thread = std::jthread([this]() { ThreadFunc(); }); } @@ -27,7 +26,7 @@ Result AudioManager::SetOutManager(BufferEventFunc buffer_func) { const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)}; if (buffer_events[index] == nullptr) { - buffer_events[index] = buffer_func; + buffer_events[index] = std::move(buffer_func); needs_update = true; events.SetAudioEvent(Event::Type::AudioOutManager, true); } @@ -43,7 +42,7 @@ Result AudioManager::SetInManager(BufferEventFunc buffer_func) { const auto index{events.GetManagerIndex(Event::Type::AudioInManager)}; if (buffer_events[index] == nullptr) { - buffer_events[index] = buffer_func; + buffer_events[index] = std::move(buffer_func); needs_update = true; events.SetAudioEvent(Event::Type::AudioInManager, true); } @@ -60,19 +59,21 @@ void AudioManager::ThreadFunc() { running = true; while (running) { - auto timed_out{events.Wait(l, std::chrono::seconds(2))}; + const auto timed_out{events.Wait(l, std::chrono::seconds(2))}; if (events.CheckAudioEventSet(Event::Type::Max)) { break; } for (size_t i = 0; i < buffer_events.size(); i++) { - if (events.CheckAudioEventSet(Event::Type(i)) || timed_out) { + const auto event_type = static_cast(i); + + if (events.CheckAudioEventSet(event_type) || timed_out) { if (buffer_events[i]) { buffer_events[i](); } } - events.SetAudioEvent(Event::Type(i), false); + events.SetAudioEvent(event_type, false); } } } diff --git a/src/audio_core/audio_manager.h b/src/audio_core/audio_manager.h index 8cbd95e..0227024 100644 --- a/src/audio_core/audio_manager.h +++ b/src/audio_core/audio_manager.h @@ -9,23 +9,14 @@ #include #include -#include "audio_core/audio_event.h" -#include "core/hle/service/audio/errors.h" +#include "common/polyfill_thread.h" -namespace Core { -class System; -} +#include "audio_core/audio_event.h" + +union Result; namespace AudioCore { -namespace AudioOut { -class Manager; -} - -namespace AudioIn { -class Manager; -} - /** * The AudioManager's main purpose is to wait for buffer events for the audio in and out managers, * and call an associated callback to release buffers. @@ -43,7 +34,7 @@ class AudioManager { using BufferEventFunc = std::function; public: - explicit AudioManager(Core::System& system); + explicit AudioManager(); /** * Shutdown the audio manager. @@ -80,10 +71,6 @@ private: */ void ThreadFunc(); - /// Core system - Core::System& system; - /// Have sessions started palying? - bool sessions_started{}; /// Is the main thread running? std::atomic running{}; /// Unused diff --git a/src/audio_core/audio_render_manager.h b/src/audio_core/audio_render_manager.h index bf48371..fffa594 100644 --- a/src/audio_core/audio_render_manager.h +++ b/src/audio_core/audio_render_manager.h @@ -7,6 +7,8 @@ #include #include +#include "common/polyfill_thread.h" + #include "audio_core/common/common.h" #include "audio_core/renderer/system_manager.h" #include "core/hle/service/audio/errors.h" diff --git a/src/audio_core/common/feature_support.h b/src/audio_core/common/feature_support.h index 55c9e69..e71905a 100644 --- a/src/audio_core/common/feature_support.h +++ b/src/audio_core/common/feature_support.h @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/polyfill_ranges.h" namespace AudioCore { constexpr u32 CurrentRevision = 11; diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h index 3dae1a3..15082f6 100644 --- a/src/audio_core/device/audio_buffers.h +++ b/src/audio_core/device/audio_buffers.h @@ -91,9 +91,10 @@ public: * @param core_timing - The CoreTiming instance * @param session - The device session * - * @return Is the buffer was released. + * @return If any buffer was released. */ - bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session) { + bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session, + bool force) { std::scoped_lock l{lock}; bool buffer_released{false}; while (registered_count > 0) { @@ -103,7 +104,8 @@ public: } // Check with the backend if this buffer can be released yet. - if (!session.IsBufferConsumed(buffers[index])) { + // If we're shutting down, we don't care if it's been played or not. + if (!force && !session.IsBufferConsumed(buffers[index])) { break; } diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index 9950604..5a327a6 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp @@ -73,6 +73,12 @@ void DeviceSession::Stop() { } } +void DeviceSession::ClearBuffers() { + if (stream) { + stream->ClearQueue(); + } +} + void DeviceSession::AppendBuffers(std::span buffers) const { for (const auto& buffer : buffers) { Sink::SinkBuffer new_buffer{ diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h index 74f4dc0..75f766c 100644 --- a/src/audio_core/device/device_session.h +++ b/src/audio_core/device/device_session.h @@ -90,6 +90,11 @@ public: */ void Stop(); + /** + * Clear out the underlying audio buffers in the backend stream. + */ + void ClearBuffers(); + /** * Set this device session's volume. * diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index e7f918a..934ef8c 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp @@ -23,7 +23,6 @@ System::~System() { void System::Finalize() { Stop(); session->Finalize(); - buffer_event->GetWritableEvent().Signal(); } void System::StartSession() { @@ -56,7 +55,7 @@ Result System::IsConfigValid(const std::string_view device_name, return ResultSuccess; } -Result System::Initialize(std::string& device_name, const AudioInParameter& in_params, +Result System::Initialize(std::string device_name, const AudioInParameter& in_params, const u32 handle_, const u64 applet_resource_user_id_) { auto result{IsConfigValid(device_name, in_params)}; if (result.IsError()) { @@ -102,6 +101,10 @@ Result System::Stop() { if (state == State::Started) { session->Stop(); session->SetVolume(0.0f); + session->ClearBuffers(); + if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { + buffer_event->Signal(); + } state = State::Stopped; } @@ -138,11 +141,11 @@ void System::RegisterBuffers() { } void System::ReleaseBuffers() { - bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; + bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)}; if (signal) { // Signal if any buffer was released, or if none are registered, we need more. - buffer_event->GetWritableEvent().Signal(); + buffer_event->Signal(); } } @@ -159,7 +162,7 @@ bool System::FlushAudioInBuffers() { buffers.FlushBuffers(buffers_released); if (buffers_released > 0) { - buffer_event->GetWritableEvent().Signal(); + buffer_event->Signal(); } return true; } diff --git a/src/audio_core/in/audio_in_system.h b/src/audio_core/in/audio_in_system.h index b9dc0e6..1c51546 100644 --- a/src/audio_core/in/audio_in_system.h +++ b/src/audio_core/in/audio_in_system.h @@ -97,7 +97,7 @@ public: * @param applet_resource_user_id - Unused. * @return Result code. */ - Result Initialize(std::string& device_name, const AudioInParameter& in_params, u32 handle, + Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle, u64 applet_resource_user_id); /** diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index 8b90759..e096a1d 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp @@ -24,7 +24,6 @@ System::~System() { void System::Finalize() { Stop(); session->Finalize(); - buffer_event->GetWritableEvent().Signal(); } std::string_view System::GetDefaultOutputDeviceName() const { @@ -49,8 +48,8 @@ Result System::IsConfigValid(std::string_view device_name, return Service::Audio::ERR_INVALID_CHANNEL_COUNT; } -Result System::Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle_, - u64& applet_resource_user_id_) { +Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_, + u64 applet_resource_user_id_) { auto result = IsConfigValid(device_name, in_params); if (result.IsError()) { return result; @@ -102,6 +101,10 @@ Result System::Stop() { if (state == State::Started) { session->Stop(); session->SetVolume(0.0f); + session->ClearBuffers(); + if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { + buffer_event->Signal(); + } state = State::Stopped; } @@ -138,10 +141,10 @@ void System::RegisterBuffers() { } void System::ReleaseBuffers() { - bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; + bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)}; if (signal) { // Signal if any buffer was released, or if none are registered, we need more. - buffer_event->GetWritableEvent().Signal(); + buffer_event->Signal(); } } @@ -158,7 +161,7 @@ bool System::FlushAudioOutBuffers() { buffers.FlushBuffers(buffers_released); if (buffers_released > 0) { - buffer_event->GetWritableEvent().Signal(); + buffer_event->Signal(); } return true; } diff --git a/src/audio_core/out/audio_out_system.h b/src/audio_core/out/audio_out_system.h index 0817b2f..b95cb91 100644 --- a/src/audio_core/out/audio_out_system.h +++ b/src/audio_core/out/audio_out_system.h @@ -88,8 +88,8 @@ public: * @param applet_resource_user_id - Unused. * @return Result code. */ - Result Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle, - u64& applet_resource_user_id); + Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle, + u64 applet_resource_user_id); /** * Start this system. diff --git a/src/audio_core/precompiled_headers.h b/src/audio_core/precompiled_headers.h new file mode 100644 index 0000000..aabae73 --- /dev/null +++ b/src/audio_core/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp index bafe482..d982ef6 100644 --- a/src/audio_core/renderer/adsp/audio_renderer.cpp +++ b/src/audio_core/renderer/adsp/audio_renderer.cpp @@ -47,7 +47,7 @@ RenderMessage AudioRenderer_Mailbox::ADSPWaitMessage() { return msg; } -CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const s32 session_id) { +CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const u32 session_id) { return command_buffers[session_id]; } @@ -132,7 +132,7 @@ void AudioRenderer::CreateSinkStreams() { } void AudioRenderer::ThreadFunc() { - constexpr char name[]{"yuzu:AudioRenderer"}; + constexpr char name[]{"AudioRenderer"}; MicroProfileOnThreadCreate(name); Common::SetCurrentThreadName(name); Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); diff --git a/src/audio_core/renderer/adsp/audio_renderer.h b/src/audio_core/renderer/adsp/audio_renderer.h index 02e923c..151f38c 100644 --- a/src/audio_core/renderer/adsp/audio_renderer.h +++ b/src/audio_core/renderer/adsp/audio_renderer.h @@ -83,7 +83,7 @@ public: * @param session_id - The session id to get (0 or 1). * @return The command buffer. */ - CommandBuffer& GetCommandBuffer(s32 session_id); + CommandBuffer& GetCommandBuffer(u32 session_id); /** * Set the command buffer with the given session id (0 or 1). diff --git a/src/audio_core/renderer/behavior/info_updater.cpp b/src/audio_core/renderer/behavior/info_updater.cpp index c0a307b..574cf09 100644 --- a/src/audio_core/renderer/behavior/info_updater.cpp +++ b/src/audio_core/renderer/behavior/info_updater.cpp @@ -91,7 +91,7 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context, voice_info.Initialize(); for (u32 channel = 0; channel < in_param.channel_count; channel++) { - std::memset(voice_states[channel], 0, sizeof(VoiceState)); + *voice_states[channel] = {}; } } diff --git a/src/audio_core/renderer/command/command_buffer.cpp b/src/audio_core/renderer/command/command_buffer.cpp index 2ef879e..8c6fe97 100644 --- a/src/audio_core/renderer/command/command_buffer.cpp +++ b/src/audio_core/renderer/command/command_buffer.cpp @@ -460,21 +460,23 @@ void CommandBuffer::GenerateDeviceSinkCommand(const s32 node_id, const s16 buffe cmd.session_id = session_id; + cmd.input_count = parameter.input_count; + s16 max_input{0}; + for (u32 i = 0; i < parameter.input_count; i++) { + cmd.inputs[i] = buffer_offset + parameter.inputs[i]; + max_input = std::max(max_input, cmd.inputs[i]); + } + if (state.upsampler_info != nullptr) { const auto size_{state.upsampler_info->sample_count * parameter.input_count}; const auto size_bytes{size_ * sizeof(s32)}; const auto addr{memory_pool->Translate(state.upsampler_info->samples_pos, size_bytes)}; cmd.sample_buffer = {reinterpret_cast(addr), - parameter.input_count * state.upsampler_info->sample_count}; + (max_input + 1) * state.upsampler_info->sample_count}; } else { cmd.sample_buffer = samples_buffer; } - cmd.input_count = parameter.input_count; - for (u32 i = 0; i < parameter.input_count; i++) { - cmd.inputs[i] = buffer_offset + parameter.inputs[i]; - } - GenerateEnd(cmd); } diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp index 1baae74..edb30ce 100644 --- a/src/audio_core/renderer/command/effect/biquad_filter.cpp +++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp @@ -94,7 +94,7 @@ void BiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { auto state_{reinterpret_cast(state)}; if (needs_init) { - std::memset(state_, 0, sizeof(VoiceState::BiquadFilterState)); + *state_ = {}; } auto input_buffer{ diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp index c4bf394..2187d8a 100644 --- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp +++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp @@ -5,6 +5,7 @@ #include "audio_core/renderer/adsp/command_list_processor.h" #include "audio_core/renderer/command/effect/i3dl2_reverb.h" +#include "common/polyfill_ranges.h" namespace AudioCore::AudioRenderer { diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp index b3c3ba4..48a7cba 100644 --- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp +++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp @@ -30,7 +30,7 @@ void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& proc for (u32 i = 0; i < filter_tap_count; i++) { auto state{reinterpret_cast(states[i])}; if (needs_init[i]) { - std::memset(state, 0, sizeof(VoiceState::BiquadFilterState)); + *state = {}; } ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state, diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp index fe2b1eb..4274892 100644 --- a/src/audio_core/renderer/command/effect/reverb.cpp +++ b/src/audio_core/renderer/command/effect/reverb.cpp @@ -6,6 +6,7 @@ #include "audio_core/renderer/adsp/command_list_processor.h" #include "audio_core/renderer/command/effect/reverb.h" +#include "common/polyfill_ranges.h" namespace AudioCore::AudioRenderer { diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp index 2427c83..35b748e 100644 --- a/src/audio_core/renderer/mix/mix_context.cpp +++ b/src/audio_core/renderer/mix/mix_context.cpp @@ -5,6 +5,7 @@ #include "audio_core/renderer/mix/mix_context.h" #include "audio_core/renderer/splitter/splitter_context.h" +#include "common/polyfill_ranges.h" namespace AudioCore::AudioRenderer { diff --git a/src/audio_core/renderer/performance/detail_aspect.h b/src/audio_core/renderer/performance/detail_aspect.h index ee4ac2f..736c331 100644 --- a/src/audio_core/renderer/performance/detail_aspect.h +++ b/src/audio_core/renderer/performance/detail_aspect.h @@ -16,7 +16,6 @@ class CommandGenerator; */ class DetailAspect { public: - DetailAspect() = default; DetailAspect(CommandGenerator& command_generator, PerformanceEntryType entry_type, s32 node_id, PerformanceDetailType detail_type); diff --git a/src/audio_core/renderer/performance/entry_aspect.h b/src/audio_core/renderer/performance/entry_aspect.h index 01c1eb3..14c9e3b 100644 --- a/src/audio_core/renderer/performance/entry_aspect.h +++ b/src/audio_core/renderer/performance/entry_aspect.h @@ -16,7 +16,6 @@ class CommandGenerator; */ class EntryAspect { public: - EntryAspect() = default; EntryAspect(CommandGenerator& command_generator, PerformanceEntryType type, s32 node_id); /// Command generator the command will be generated into diff --git a/src/audio_core/renderer/performance/performance_manager.cpp b/src/audio_core/renderer/performance/performance_manager.cpp index fd5873e..8aa0f5e 100644 --- a/src/audio_core/renderer/performance/performance_manager.cpp +++ b/src/audio_core/renderer/performance/performance_manager.cpp @@ -26,6 +26,7 @@ void PerformanceManager::CreateImpl(const size_t version) { impl = std::make_unique< PerformanceManagerImpl>(); + break; } } diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp index 7a21796..4fac30c 100644 --- a/src/audio_core/renderer/system.cpp +++ b/src/audio_core/renderer/system.cpp @@ -98,9 +98,8 @@ System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_) : core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {} Result System::Initialize(const AudioRendererParameterInternal& params, - Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size, - const u32 process_handle_, const u64 applet_resource_user_id_, - const s32 session_id_) { + Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, + u32 process_handle_, u64 applet_resource_user_id_, s32 session_id_) { if (!CheckValidRevision(params.revision)) { return Service::Audio::ERR_INVALID_REVISION; } @@ -354,6 +353,8 @@ Result System::Initialize(const AudioRendererParameterInternal& params, render_time_limit_percent = 100; drop_voice = params.voice_drop_enabled && params.execution_mode == ExecutionMode::Auto; + drop_voice_param = 1.0f; + num_voices_dropped = 0; allocator.Align(0x40); command_workbuffer_size = allocator.GetRemainingSize(); @@ -534,7 +535,7 @@ Result System::Update(std::span input, std::span performance, std: return result; } - adsp_rendered_event->GetWritableEvent().Clear(); + adsp_rendered_event->Clear(); num_times_updated++; const auto end_time{core.CoreTiming().GetClockTicks()}; @@ -547,7 +548,7 @@ u32 System::GetRenderingTimeLimit() const { return render_time_limit_percent; } -void System::SetRenderingTimeLimit(const u32 limit) { +void System::SetRenderingTimeLimit(u32 limit) { render_time_limit_percent = limit; } @@ -625,7 +626,7 @@ void System::SendCommandToDsp() { reset_command_buffers = false; command_buffer_size = command_size; if (remaining_command_count == 0) { - adsp_rendered_event->GetWritableEvent().Signal(); + adsp_rendered_event->Signal(); } } else { adsp.ClearRemainCount(session_id); @@ -635,7 +636,7 @@ void System::SendCommandToDsp() { } u64 System::GenerateCommand(std::span in_command_buffer, - [[maybe_unused]] const u64 command_buffer_size_) { + [[maybe_unused]] u64 command_buffer_size_) { PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count); const auto start_time{core.CoreTiming().GetClockTicks()}; @@ -693,7 +694,8 @@ u64 System::GenerateCommand(std::span in_command_buffer, voice_context.SortInfo(); - const auto start_estimated_time{command_buffer.estimated_process_time}; + const auto start_estimated_time{drop_voice_param * + static_cast(command_buffer.estimated_process_time)}; command_generator.GenerateVoiceCommands(); command_generator.GenerateSubMixCommands(); @@ -712,11 +714,16 @@ u64 System::GenerateCommand(std::span in_command_buffer, render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported(); time_limit_percent = 70.0f; } + + const auto end_estimated_time{drop_voice_param * + static_cast(command_buffer.estimated_process_time)}; + const auto estimated_time{start_estimated_time - end_estimated_time}; + const auto time_limit{static_cast( - static_cast(start_estimated_time - command_buffer.estimated_process_time) + - (((time_limit_percent / 100.0f) * 2'880'000.0) * - (static_cast(render_time_limit_percent) / 100.0f)))}; - num_voices_dropped = DropVoices(command_buffer, start_estimated_time, time_limit); + estimated_time + (((time_limit_percent / 100.0f) * 2'880'000.0) * + (static_cast(render_time_limit_percent) / 100.0f)))}; + num_voices_dropped = + DropVoices(command_buffer, static_cast(start_estimated_time), time_limit); } command_list_header->buffer_size = command_buffer.size; @@ -737,24 +744,33 @@ u64 System::GenerateCommand(std::span in_command_buffer, return command_buffer.size; } -u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_process_time, - const u32 time_limit) { +f32 System::GetVoiceDropParameter() const { + return drop_voice_param; +} + +void System::SetVoiceDropParameter(f32 voice_drop_) { + drop_voice_param = voice_drop_; +} + +u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit) { u32 i{0}; auto command_list{command_buffer.command_list.data() + sizeof(CommandListHeader)}; - ICommand* cmd{}; + ICommand* cmd{nullptr}; - for (; i < command_buffer.count; i++) { + // Find a first valid voice to drop + while (i < command_buffer.count) { cmd = reinterpret_cast(command_list); - if (cmd->type != CommandId::Performance && - cmd->type != CommandId::DataSourcePcmInt16Version1 && - cmd->type != CommandId::DataSourcePcmInt16Version2 && - cmd->type != CommandId::DataSourcePcmFloatVersion1 && - cmd->type != CommandId::DataSourcePcmFloatVersion2 && - cmd->type != CommandId::DataSourceAdpcmVersion1 && - cmd->type != CommandId::DataSourceAdpcmVersion2) { + if (cmd->type == CommandId::Performance || + cmd->type == CommandId::DataSourcePcmInt16Version1 || + cmd->type == CommandId::DataSourcePcmInt16Version2 || + cmd->type == CommandId::DataSourcePcmFloatVersion1 || + cmd->type == CommandId::DataSourcePcmFloatVersion2 || + cmd->type == CommandId::DataSourceAdpcmVersion1 || + cmd->type == CommandId::DataSourceAdpcmVersion2) { break; } command_list += cmd->size; + i++; } if (cmd == nullptr || command_buffer.count == 0 || i >= command_buffer.count) { @@ -767,6 +783,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces const auto node_id_type{cmd->node_id >> 28}; const auto node_id_base{cmd->node_id & 0xFFF}; + // If the new estimated process time falls below the limit, we're done dropping. if (estimated_process_time <= time_limit) { break; } @@ -775,6 +792,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces break; } + // Don't drop voices marked with the highest priority. auto& voice_info{voice_context.GetInfo(node_id_base)}; if (voice_info.priority == HighestVoicePriority) { break; @@ -783,18 +801,23 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces voices_dropped++; voice_info.voice_dropped = true; - if (i < command_buffer.count) { - while (cmd->node_id == node_id) { - if (cmd->type == CommandId::DepopPrepare) { - cmd->enabled = true; - } else if (cmd->type == CommandId::Performance || !cmd->enabled) { - cmd->enabled = false; - } - i++; - command_list += cmd->size; - cmd = reinterpret_cast(command_list); + // First iteration should drop the voice, and then iterate through all of the commands tied + // to the voice. We don't need reverb on a voice which we've just removed, for example. + // Depops can't be removed otherwise we'll introduce audio popping, and we don't + // remove perf commands. Lower the estimated time for each command dropped. + while (i < command_buffer.count && cmd->node_id == node_id) { + if (cmd->type == CommandId::DepopPrepare) { + cmd->enabled = true; + } else if (cmd->enabled && cmd->type != CommandId::Performance) { + cmd->enabled = false; + estimated_process_time -= static_cast( + drop_voice_param * static_cast(cmd->estimated_process_time)); } + command_list += cmd->size; + cmd = reinterpret_cast(command_list); + i++; } + i++; } return voices_dropped; } diff --git a/src/audio_core/renderer/system.h b/src/audio_core/renderer/system.h index bcbe65b..429196e 100644 --- a/src/audio_core/renderer/system.h +++ b/src/audio_core/renderer/system.h @@ -196,6 +196,20 @@ public: */ u32 DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit); + /** + * Get the current voice drop parameter. + * + * @return The current voice drop. + */ + f32 GetVoiceDropParameter() const; + + /** + * Set the voice drop parameter. + * + * @param The new voice drop. + */ + void SetVoiceDropParameter(f32 voice_drop); + private: /// Core system Core::System& core; @@ -301,6 +315,8 @@ private: u32 num_voices_dropped{}; /// Tick that rendering started u64 render_start_tick{}; + /// Parameter to control the threshold for dropping voices if the audio graph gets too large + f32 drop_voice_param{1.0f}; }; } // namespace AudioRenderer diff --git a/src/audio_core/renderer/system_manager.cpp b/src/audio_core/renderer/system_manager.cpp index 9c1331e..f66b2b8 100644 --- a/src/audio_core/renderer/system_manager.cpp +++ b/src/audio_core/renderer/system_manager.cpp @@ -94,7 +94,7 @@ bool SystemManager::Remove(System& system_) { } void SystemManager::ThreadFunc() { - constexpr char name[]{"yuzu:AudioRenderSystemManager"}; + constexpr char name[]{"AudioRenderSystemManager"}; MicroProfileOnThreadCreate(name); Common::SetCurrentThreadName(name); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp index eafb51b..16a3e83 100644 --- a/src/audio_core/renderer/voice/voice_context.cpp +++ b/src/audio_core/renderer/voice/voice_context.cpp @@ -4,6 +4,7 @@ #include #include "audio_core/renderer/voice/voice_context.h" +#include "common/polyfill_ranges.h" namespace AudioCore::AudioRenderer { @@ -74,8 +75,8 @@ void VoiceContext::SortInfo() { } std::ranges::sort(sorted_voice_info, [](const VoiceInfo* a, const VoiceInfo* b) { - return a->priority != b->priority ? a->priority < b->priority - : a->sort_order < b->sort_order; + return a->priority != b->priority ? a->priority > b->priority + : a->sort_order > b->sort_order; }); } diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index 36b115a..32c1b1c 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp @@ -66,10 +66,10 @@ public: const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &minimum_latency); if (latency_error != CUBEB_OK) { LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); - minimum_latency = 256U; + minimum_latency = TargetSampleCount * 2; } - minimum_latency = std::max(minimum_latency, 256u); + minimum_latency = std::max(minimum_latency, TargetSampleCount * 2); LOG_INFO(Service_Audio, "Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) " @@ -326,4 +326,31 @@ std::vector ListCubebSinkDevices(bool capture) { return device_list; } +u32 GetCubebLatency() { + cubeb* ctx; + + if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) { + LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); + // Return a large latency so we choose SDL instead. + return 10000u; + } + + cubeb_stream_params params{}; + params.rate = TargetSampleRate; + params.channels = 2; + params.format = CUBEB_SAMPLE_S16LE; + params.prefs = CUBEB_STREAM_PREF_NONE; + params.layout = CUBEB_LAYOUT_STEREO; + + u32 latency{0}; + const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency); + if (latency_error != CUBEB_OK) { + LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); + latency = TargetSampleCount * 2; + } + latency = std::max(latency, TargetSampleCount * 2); + cubeb_destroy(ctx); + return latency; +} + } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/cubeb_sink.h b/src/audio_core/sink/cubeb_sink.h index 4b0cb16..3302cb9 100644 --- a/src/audio_core/sink/cubeb_sink.h +++ b/src/audio_core/sink/cubeb_sink.h @@ -96,4 +96,11 @@ private: */ std::vector ListCubebSinkDevices(bool capture); +/** + * Get the reported latency for this sink. + * + * @return Minimum latency for this sink. + */ +u32 GetCubebLatency(); + } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index 1bd001b..c138dc6 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp @@ -47,11 +47,7 @@ public: spec.freq = TargetSampleRate; spec.channels = static_cast(device_channels); spec.format = AUDIO_S16SYS; - if (type == StreamType::Render) { - spec.samples = TargetSampleCount; - } else { - spec.samples = 1024; - } + spec.samples = TargetSampleCount * 2; spec.callback = &SDLSinkStream::DataCallback; spec.userdata = this; @@ -234,10 +230,16 @@ std::vector ListSDLSinkDevices(bool capture) { const int device_count = SDL_GetNumAudioDevices(capture); for (int i = 0; i < device_count; ++i) { - device_list.emplace_back(SDL_GetAudioDeviceName(i, 0)); + if (const char* name = SDL_GetAudioDeviceName(i, capture)) { + device_list.emplace_back(name); + } } return device_list; } +u32 GetSDLLatency() { + return TargetSampleCount * 2; +} + } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl2_sink.h index f01eddc..27ed1ab 100644 --- a/src/audio_core/sink/sdl2_sink.h +++ b/src/audio_core/sink/sdl2_sink.h @@ -87,4 +87,11 @@ private: */ std::vector ListSDLSinkDevices(bool capture); +/** + * Get the reported latency for this sink. + * + * @return Minimum latency for this sink. + */ +u32 GetSDLLatency(); + } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp index 67bdab7..39ea6d9 100644 --- a/src/audio_core/sink/sink_details.cpp +++ b/src/audio_core/sink/sink_details.cpp @@ -21,58 +21,80 @@ namespace { struct SinkDetails { using FactoryFn = std::unique_ptr (*)(std::string_view); using ListDevicesFn = std::vector (*)(bool); + using LatencyFn = u32 (*)(); /// Name for this sink. - const char* id; + std::string_view id; /// A method to call to construct an instance of this type of sink. FactoryFn factory; /// A method to call to list available devices. ListDevicesFn list_devices; + /// Method to get the latency of this backend. + LatencyFn latency; }; // sink_details is ordered in terms of desirability, with the best choice at the top. constexpr SinkDetails sink_details[] = { #ifdef HAVE_CUBEB - SinkDetails{"cubeb", - [](std::string_view device_id) -> std::unique_ptr { - return std::make_unique(device_id); - }, - &ListCubebSinkDevices}, + SinkDetails{ + "cubeb", + [](std::string_view device_id) -> std::unique_ptr { + return std::make_unique(device_id); + }, + &ListCubebSinkDevices, + &GetCubebLatency, + }, #endif #ifdef HAVE_SDL2 - SinkDetails{"sdl2", - [](std::string_view device_id) -> std::unique_ptr { - return std::make_unique(device_id); - }, - &ListSDLSinkDevices}, + SinkDetails{ + "sdl2", + [](std::string_view device_id) -> std::unique_ptr { + return std::make_unique(device_id); + }, + &ListSDLSinkDevices, + &GetSDLLatency, + }, #endif SinkDetails{"null", [](std::string_view device_id) -> std::unique_ptr { return std::make_unique(device_id); }, - [](bool capture) { return std::vector{"null"}; }}, + [](bool capture) { return std::vector{"null"}; }, []() { return 0u; }}, }; const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { - auto iter = - std::find_if(std::begin(sink_details), std::end(sink_details), - [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); + const auto find_backend{[](std::string_view id) { + return std::find_if(std::begin(sink_details), std::end(sink_details), + [&id](const auto& sink_detail) { return sink_detail.id == id; }); + }}; - if (sink_id == "auto" || iter == std::end(sink_details)) { - if (sink_id != "auto") { - LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); + auto iter = find_backend(sink_id); + + if (sink_id == "auto") { + // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which + // causes audio issues, in that case go with SDL. +#if defined(HAVE_CUBEB) && defined(HAVE_SDL2) + iter = find_backend("cubeb"); + if (iter->latency() > TargetSampleCount * 3) { + iter = find_backend("sdl2"); } - // Auto-select. - // sink_details is ordered in terms of desirability, with the best choice at the front. +#else iter = std::begin(sink_details); +#endif + LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id); + } + + if (iter == std::end(sink_details)) { + LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); + iter = find_backend("null"); } return *iter; } } // Anonymous namespace -std::vector GetSinkIDs() { - std::vector sink_ids(std::size(sink_details)); +std::vector GetSinkIDs() { + std::vector sink_ids(std::size(sink_details)); std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), [](const auto& sink) { return sink.id; }); diff --git a/src/audio_core/sink/sink_details.h b/src/audio_core/sink/sink_details.h index 3ebdb1e..e759328 100644 --- a/src/audio_core/sink/sink_details.h +++ b/src/audio_core/sink/sink_details.h @@ -19,7 +19,7 @@ class Sink; * * @return Vector of available sink names. */ -std::vector GetSinkIDs(); +std::vector GetSinkIDs(); /** * Gets the list of devices for a particular sink identified by the given ID. diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index 37fe725..06c2a87 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp @@ -170,8 +170,8 @@ void SinkStream::ProcessAudioIn(std::span input_buffer, std::size_t n // Get the minimum frames available between the currently playing buffer, and the // amount we have left to fill - size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, - num_frames - frames_written)}; + size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, + num_frames - frames_written)}; samples_buffer.Push(&input_buffer[frames_written * frame_size], frames_available * frame_size); @@ -214,8 +214,13 @@ void SinkStream::ProcessAudioOutAndRender(std::span output_buffer, std::siz // video play out without attempting to stall. // Can hopefully remove this later with a more complete NVDEC implementation. const auto nvdec_active{system.AudioCore().IsNVDECActive()}; - if (!nvdec_active && queued_buffers > max_queue_size) { + + // Core timing cannot be paused in single-core mode, so Stall ends up being called over and over + // and never recovers to a normal state, so just skip attempting to sync things on single-core. + if (system.IsMulticore() && !nvdec_active && queued_buffers > max_queue_size) { Stall(); + } else if (system.IsMulticore() && queued_buffers <= max_queue_size) { + Unstall(); } while (frames_written < num_frames) { @@ -236,8 +241,8 @@ void SinkStream::ProcessAudioOutAndRender(std::span output_buffer, std::siz // Get the minimum frames available between the currently playing buffer, and the // amount we have left to fill - size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, - num_frames - frames_written)}; + size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, + num_frames - frames_written)}; samples_buffer.Pop(&output_buffer[frames_written * frame_size], frames_available * frame_size); @@ -255,25 +260,26 @@ void SinkStream::ProcessAudioOutAndRender(std::span output_buffer, std::siz std::memcpy(&last_frame[0], &output_buffer[(frames_written - 1) * frame_size], frame_size_bytes); - if (stalled && queued_buffers <= max_queue_size) { + if (system.IsMulticore() && queued_buffers <= max_queue_size) { Unstall(); } } void SinkStream::Stall() { - if (stalled) { + std::scoped_lock lk{stall_guard}; + if (stalled_lock) { return; } - stalled = true; - system.StallProcesses(); + stalled_lock = system.StallProcesses(); } void SinkStream::Unstall() { - if (!stalled) { + std::scoped_lock lk{stall_guard}; + if (!stalled_lock) { return; } system.UnstallProcesses(); - stalled = false; + stalled_lock.unlock(); } } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h index 38a4b2f..5fea72a 100644 --- a/src/audio_core/sink/sink_stream.h +++ b/src/audio_core/sink/sink_stream.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -240,8 +241,8 @@ private: f32 system_volume{1.0f}; /// Set via IAudioDevice service calls f32 device_volume{1.0f}; - /// True if coretiming has been stalled - bool stalled{false}; + std::mutex stall_guard; + std::unique_lock stalled_lock; }; using SinkStreamPtr = std::unique_ptr; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 68436a4..25b22a2 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -14,34 +14,11 @@ if (DEFINED ENV{DISPLAYVERSION}) set(DISPLAY_VERSION $ENV{DISPLAYVERSION}) endif () -# Pass the path to git to the GenerateSCMRev.cmake as well -find_package(Git QUIET) - -add_custom_command(OUTPUT scm_rev.cpp - COMMAND ${CMAKE_COMMAND} - -DSRC_DIR=${PROJECT_SOURCE_DIR} - -DBUILD_REPOSITORY=${BUILD_REPOSITORY} - -DTITLE_BAR_FORMAT_IDLE=${TITLE_BAR_FORMAT_IDLE} - -DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING} - -DBUILD_TAG=${BUILD_TAG} - -DBUILD_ID=${DISPLAY_VERSION} - -DGIT_REF_SPEC=${GIT_REF_SPEC} - -DGIT_REV=${GIT_REV} - -DGIT_DESC=${GIT_DESC} - -DGIT_BRANCH=${GIT_BRANCH} - -DBUILD_FULLNAME=${BUILD_FULLNAME} - -DGIT_EXECUTABLE=${GIT_EXECUTABLE} - -P ${PROJECT_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake - DEPENDS - # Check that the scm_rev files haven't changed - "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" - "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h" - # technically we should regenerate if the git version changed, but its not worth the effort imo - "${PROJECT_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake" - VERBATIM -) +include(GenerateSCMRev) add_library(common STATIC + address_space.cpp + address_space.h algorithm.h alignment.h announce_multiplayer_room.h @@ -58,6 +35,7 @@ add_library(common STATIC cityhash.cpp cityhash.h common_funcs.h + common_precompiled_headers.h common_types.h concepts.h div_ceil.h @@ -106,6 +84,8 @@ add_library(common STATIC microprofile.cpp microprofile.h microprofileui.h + multi_level_page_table.cpp + multi_level_page_table.h nvidia_flags.cpp nvidia_flags.h page_table.cpp @@ -114,10 +94,11 @@ add_library(common STATIC param_package.h parent_of_member.h point.h + precompiled_headers.h quaternion.h reader_writer_queue.h ring_buffer.h - scm_rev.cpp + ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp scm_rev.h scope_exit.h settings.cpp @@ -166,7 +147,7 @@ if(ARCHITECTURE_x86_64) x64/xbyak_abi.h x64/xbyak_util.h ) - target_link_libraries(common PRIVATE xbyak) + target_link_libraries(common PRIVATE xbyak::xbyak) endif() if (MSVC) @@ -177,12 +158,13 @@ if (MSVC) ) target_compile_options(common PRIVATE /W4 - /WX + + /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data + /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) else() target_compile_options(common PRIVATE - -Werror - $<$:-fsized-deallocation> ) endif() @@ -190,10 +172,8 @@ endif() create_target_directory_groups(common) target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) -target_link_libraries(common PRIVATE lz4::lz4) -if (TARGET zstd::zstd) - target_link_libraries(common PRIVATE zstd::zstd) -else() - target_link_libraries(common PRIVATE - $,zstd::libzstd_shared,zstd::libzstd_static>) +target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd) + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(common PRIVATE precompiled_headers.h) endif() diff --git a/src/common/address_space.cpp b/src/common/address_space.cpp new file mode 100644 index 0000000..866e78d --- /dev/null +++ b/src/common/address_space.cpp @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/address_space.inc" + +namespace Common { + +template class Common::FlatAllocator; + +} diff --git a/src/common/address_space.h b/src/common/address_space.h new file mode 100644 index 0000000..9222b2f --- /dev/null +++ b/src/common/address_space.h @@ -0,0 +1,150 @@ +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "common/common_types.h" + +namespace Common { +template +concept AddressSpaceValid = std::is_unsigned_v && sizeof(VaType) * 8 >= AddressSpaceBits; + +struct EmptyStruct {}; + +/** + * @brief FlatAddressSpaceMap provides a generic VA->PA mapping implementation using a sorted vector + */ +template +requires AddressSpaceValid +class FlatAddressSpaceMap { +public: + /// The maximum VA that this AS can technically reach + static constexpr VaType VaMaximum{(1ULL << (AddressSpaceBits - 1)) + + ((1ULL << (AddressSpaceBits - 1)) - 1)}; + + explicit FlatAddressSpaceMap(VaType va_limit, + std::function unmap_callback = {}); + + FlatAddressSpaceMap() = default; + + void Map(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info = {}) { + std::scoped_lock lock(block_mutex); + MapLocked(virt, phys, size, extra_info); + } + + void Unmap(VaType virt, VaType size) { + std::scoped_lock lock(block_mutex); + UnmapLocked(virt, size); + } + + VaType GetVALimit() const { + return va_limit; + } + +protected: + /** + * @brief Represents a block of memory in the AS, the physical mapping is contiguous until + * another block with a different phys address is hit + */ + struct Block { + /// VA of the block + VaType virt{UnmappedVa}; + /// PA of the block, will increase 1-1 with VA until a new block is encountered + PaType phys{UnmappedPa}; + [[no_unique_address]] ExtraBlockInfo extra_info; + + Block() = default; + + Block(VaType virt_, PaType phys_, ExtraBlockInfo extra_info_) + : virt(virt_), phys(phys_), extra_info(extra_info_) {} + + bool Valid() const { + return virt != UnmappedVa; + } + + bool Mapped() const { + return phys != UnmappedPa; + } + + bool Unmapped() const { + return phys == UnmappedPa; + } + + bool operator<(const VaType& p_virt) const { + return virt < p_virt; + } + }; + + /** + * @brief Maps a PA range into the given AS region + * @note block_mutex MUST be locked when calling this + */ + void MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info); + + /** + * @brief Unmaps the given range and merges it with other unmapped regions + * @note block_mutex MUST be locked when calling this + */ + void UnmapLocked(VaType virt, VaType size); + + std::mutex block_mutex; + std::vector blocks{Block{}}; + + /// a soft limit on the maximum VA of the AS + VaType va_limit{VaMaximum}; + +private: + /// Callback called when the mappings in an region have changed + std::function unmap_callback{}; +}; + +/** + * @brief FlatMemoryManager specialises FlatAddressSpaceMap to work as an allocator, with an + * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block + */ +template +requires AddressSpaceValid +class FlatAllocator + : public FlatAddressSpaceMap { +private: + using Base = FlatAddressSpaceMap; + +public: + explicit FlatAllocator(VaType virt_start, VaType va_limit = Base::VaMaximum); + + /** + * @brief Allocates a region in the AS of the given size and returns its address + */ + VaType Allocate(VaType size); + + /** + * @brief Marks the given region in the AS as allocated + */ + void AllocateFixed(VaType virt, VaType size); + + /** + * @brief Frees an AS region so it can be used again + */ + void Free(VaType virt, VaType size); + + VaType GetVAStart() const { + return virt_start; + } + +private: + /// The base VA of the allocator, no allocations will be below this + VaType virt_start; + + /** + * The end address for the initial linear allocation pass + * Once this reaches the AS limit the slower allocation path will be used + */ + VaType current_linear_alloc_end; +}; +} // namespace Common diff --git a/src/common/address_space.inc b/src/common/address_space.inc new file mode 100644 index 0000000..2195dab --- /dev/null +++ b/src/common/address_space.inc @@ -0,0 +1,366 @@ +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/address_space.h" +#include "common/assert.h" + +#define MAP_MEMBER(returnType) \ + template \ + requires AddressSpaceValid returnType FlatAddressSpaceMap< \ + VaType, UnmappedVa, PaType, UnmappedPa, PaContigSplit, AddressSpaceBits, ExtraBlockInfo> +#define MAP_MEMBER_CONST() \ + template \ + requires AddressSpaceValid FlatAddressSpaceMap< \ + VaType, UnmappedVa, PaType, UnmappedPa, PaContigSplit, AddressSpaceBits, ExtraBlockInfo> + +#define MM_MEMBER(returnType) \ + template \ + requires AddressSpaceValid returnType \ + FlatMemoryManager + +#define ALLOC_MEMBER(returnType) \ + template \ + requires AddressSpaceValid returnType \ + FlatAllocator +#define ALLOC_MEMBER_CONST() \ + template \ + requires AddressSpaceValid \ + FlatAllocator + +namespace Common { +MAP_MEMBER_CONST()::FlatAddressSpaceMap(VaType va_limit_, + std::function unmap_callback_) + : va_limit{va_limit_}, unmap_callback{std::move(unmap_callback_)} { + if (va_limit > VaMaximum) { + ASSERT_MSG(false, "Invalid VA limit!"); + } +} + +MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info) { + VaType virt_end{virt + size}; + + if (virt_end > va_limit) { + ASSERT_MSG(false, + "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}", + virt_end, va_limit); + } + + auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)}; + if (block_end_successor == blocks.begin()) { + ASSERT_MSG(false, "Trying to map a block before the VA start: virt_end: 0x{:X}", virt_end); + } + + auto block_end_predecessor{std::prev(block_end_successor)}; + + if (block_end_successor != blocks.end()) { + // We have blocks in front of us, if one is directly in front then we don't have to add a + // tail + if (block_end_successor->virt != virt_end) { + PaType tailPhys{[&]() -> PaType { + if constexpr (!PaContigSplit) { + // Always propagate unmapped regions rather than calculating offset + return block_end_predecessor->phys; + } else { + if (block_end_predecessor->Unmapped()) { + // Always propagate unmapped regions rather than calculating offset + return block_end_predecessor->phys; + } else { + return block_end_predecessor->phys + virt_end - block_end_predecessor->virt; + } + } + }()}; + + if (block_end_predecessor->virt >= virt) { + // If this block's start would be overlapped by the map then reuse it as a tail + // block + block_end_predecessor->virt = virt_end; + block_end_predecessor->phys = tailPhys; + block_end_predecessor->extra_info = block_end_predecessor->extra_info; + + // No longer predecessor anymore + block_end_successor = block_end_predecessor--; + } else { + // Else insert a new one and we're done + blocks.insert(block_end_successor, + {Block(virt, phys, extra_info), + Block(virt_end, tailPhys, block_end_predecessor->extra_info)}); + if (unmap_callback) { + unmap_callback(virt, size); + } + + return; + } + } + } else { + // block_end_predecessor will always be unmapped as blocks has to be terminated by an + // unmapped chunk + if (block_end_predecessor != blocks.begin() && block_end_predecessor->virt >= virt) { + // Move the unmapped block start backwards + block_end_predecessor->virt = virt_end; + + // No longer predecessor anymore + block_end_successor = block_end_predecessor--; + } else { + // Else insert a new one and we're done + blocks.insert(block_end_successor, + {Block(virt, phys, extra_info), Block(virt_end, UnmappedPa, {})}); + if (unmap_callback) { + unmap_callback(virt, size); + } + + return; + } + } + + auto block_start_successor{block_end_successor}; + + // Walk the block vector to find the start successor as this is more efficient than another + // binary search in most scenarios + while (std::prev(block_start_successor)->virt >= virt) { + block_start_successor--; + } + + // Check that the start successor is either the end block or something in between + if (block_start_successor->virt > virt_end) { + ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt); + } else if (block_start_successor->virt == virt_end) { + // We need to create a new block as there are none spare that we would overwrite + blocks.insert(block_start_successor, Block(virt, phys, extra_info)); + } else { + // Erase overwritten blocks + if (auto eraseStart{std::next(block_start_successor)}; eraseStart != block_end_successor) { + blocks.erase(eraseStart, block_end_successor); + } + + // Reuse a block that would otherwise be overwritten as a start block + block_start_successor->virt = virt; + block_start_successor->phys = phys; + block_start_successor->extra_info = extra_info; + } + + if (unmap_callback) { + unmap_callback(virt, size); + } +} + +MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) { + VaType virt_end{virt + size}; + + if (virt_end > va_limit) { + ASSERT_MSG(false, + "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}", + virt_end, va_limit); + } + + auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)}; + if (block_end_successor == blocks.begin()) { + ASSERT_MSG(false, "Trying to unmap a block before the VA start: virt_end: 0x{:X}", + virt_end); + } + + auto block_end_predecessor{std::prev(block_end_successor)}; + + auto walk_back_to_predecessor{[&](auto iter) { + while (iter->virt >= virt) { + iter--; + } + + return iter; + }}; + + auto erase_blocks_with_end_unmapped{[&](auto unmappedEnd) { + auto block_start_predecessor{walk_back_to_predecessor(unmappedEnd)}; + auto block_start_successor{std::next(block_start_predecessor)}; + + auto eraseEnd{[&]() { + if (block_start_predecessor->Unmapped()) { + // If the start predecessor is unmapped then we can erase everything in our region + // and be done + return std::next(unmappedEnd); + } else { + // Else reuse the end predecessor as the start of our unmapped region then erase all + // up to it + unmappedEnd->virt = virt; + return unmappedEnd; + } + }()}; + + // We can't have two unmapped regions after each other + if (eraseEnd != blocks.end() && + (eraseEnd == block_start_successor || + (block_start_predecessor->Unmapped() && eraseEnd->Unmapped()))) { + ASSERT_MSG(false, "Multiple contiguous unmapped regions are unsupported!"); + } + + blocks.erase(block_start_successor, eraseEnd); + }}; + + // We can avoid any splitting logic if these are the case + if (block_end_predecessor->Unmapped()) { + if (block_end_predecessor->virt > virt) { + erase_blocks_with_end_unmapped(block_end_predecessor); + } + + if (unmap_callback) { + unmap_callback(virt, size); + } + + return; // The region is unmapped, bail out early + } else if (block_end_successor->virt == virt_end && block_end_successor->Unmapped()) { + erase_blocks_with_end_unmapped(block_end_successor); + + if (unmap_callback) { + unmap_callback(virt, size); + } + + return; // The region is unmapped here and doesn't need splitting, bail out early + } else if (block_end_successor == blocks.end()) { + // This should never happen as the end should always follow an unmapped block + ASSERT_MSG(false, "Unexpected Memory Manager state!"); + } else if (block_end_successor->virt != virt_end) { + // If one block is directly in front then we don't have to add a tail + + // The previous block is mapped so we will need to add a tail with an offset + PaType tailPhys{[&]() { + if constexpr (PaContigSplit) { + return block_end_predecessor->phys + virt_end - block_end_predecessor->virt; + } else { + return block_end_predecessor->phys; + } + }()}; + + if (block_end_predecessor->virt >= virt) { + // If this block's start would be overlapped by the unmap then reuse it as a tail block + block_end_predecessor->virt = virt_end; + block_end_predecessor->phys = tailPhys; + + // No longer predecessor anymore + block_end_successor = block_end_predecessor--; + } else { + blocks.insert(block_end_successor, + {Block(virt, UnmappedPa, {}), + Block(virt_end, tailPhys, block_end_predecessor->extra_info)}); + if (unmap_callback) { + unmap_callback(virt, size); + } + + // The previous block is mapped and ends before + return; + } + } + + // Walk the block vector to find the start predecessor as this is more efficient than another + // binary search in most scenarios + auto block_start_predecessor{walk_back_to_predecessor(block_end_successor)}; + auto block_start_successor{std::next(block_start_predecessor)}; + + if (block_start_successor->virt > virt_end) { + ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt); + } else if (block_start_successor->virt == virt_end) { + // There are no blocks between the start and the end that would let us skip inserting a new + // one for head + + // The previous block is may be unmapped, if so we don't need to insert any unmaps after it + if (block_start_predecessor->Mapped()) { + blocks.insert(block_start_successor, Block(virt, UnmappedPa, {})); + } + } else if (block_start_predecessor->Unmapped()) { + // If the previous block is unmapped + blocks.erase(block_start_successor, block_end_predecessor); + } else { + // Erase overwritten blocks, skipping the first one as we have written the unmapped start + // block there + if (auto eraseStart{std::next(block_start_successor)}; eraseStart != block_end_successor) { + blocks.erase(eraseStart, block_end_successor); + } + + // Add in the unmapped block header + block_start_successor->virt = virt; + block_start_successor->phys = UnmappedPa; + } + + if (unmap_callback) + unmap_callback(virt, size); +} + +ALLOC_MEMBER_CONST()::FlatAllocator(VaType virt_start_, VaType va_limit_) + : Base{va_limit_}, virt_start{virt_start_}, current_linear_alloc_end{virt_start_} {} + +ALLOC_MEMBER(VaType)::Allocate(VaType size) { + std::scoped_lock lock(this->block_mutex); + + VaType alloc_start{UnmappedVa}; + VaType alloc_end{current_linear_alloc_end + size}; + + // Avoid searching backwards in the address space if possible + if (alloc_end >= current_linear_alloc_end && alloc_end <= this->va_limit) { + auto alloc_end_successor{ + std::lower_bound(this->blocks.begin(), this->blocks.end(), alloc_end)}; + if (alloc_end_successor == this->blocks.begin()) { + ASSERT_MSG(false, "First block in AS map is invalid!"); + } + + auto alloc_end_predecessor{std::prev(alloc_end_successor)}; + if (alloc_end_predecessor->virt <= current_linear_alloc_end) { + alloc_start = current_linear_alloc_end; + } else { + // Skip over fixed any mappings in front of us + while (alloc_end_successor != this->blocks.end()) { + if (alloc_end_successor->virt - alloc_end_predecessor->virt < size || + alloc_end_predecessor->Mapped()) { + alloc_start = alloc_end_predecessor->virt; + break; + } + + alloc_end_predecessor = alloc_end_successor++; + + // Use the VA limit to calculate if we can fit in the final block since it has no + // successor + if (alloc_end_successor == this->blocks.end()) { + alloc_end = alloc_end_predecessor->virt + size; + + if (alloc_end >= alloc_end_predecessor->virt && alloc_end <= this->va_limit) { + alloc_start = alloc_end_predecessor->virt; + } + } + } + } + } + + if (alloc_start != UnmappedVa) { + current_linear_alloc_end = alloc_start + size; + } else { // If linear allocation overflows the AS then find a gap + if (this->blocks.size() <= 2) { + ASSERT_MSG(false, "Unexpected allocator state!"); + } + + auto search_predecessor{this->blocks.begin()}; + auto search_successor{std::next(search_predecessor)}; + + while (search_successor != this->blocks.end() && + (search_successor->virt - search_predecessor->virt < size || + search_predecessor->Mapped())) { + search_predecessor = search_successor++; + } + + if (search_successor != this->blocks.end()) { + alloc_start = search_predecessor->virt; + } else { + return {}; // AS is full + } + } + + this->MapLocked(alloc_start, true, size, {}); + return alloc_start; +} + +ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) { + this->Map(virt, true, size); +} + +ALLOC_MEMBER(void)::Free(VaType virt, VaType size) { + this->Unmap(virt, size); +} +} // namespace Common diff --git a/src/common/algorithm.h b/src/common/algorithm.h index 9ddfd63..c27c924 100644 --- a/src/common/algorithm.h +++ b/src/common/algorithm.h @@ -24,4 +24,12 @@ template > return first != last && !comp(value, *first) ? first : last; } +template +T FoldRight(T initial_value, Func&& func, Args&&... args) { + T value{initial_value}; + const auto high_func = [&value, &func](U x) { value = func(value, x); }; + (std::invoke(high_func, std::forward(args)), ...); + return value; +} + } // namespace Common diff --git a/src/common/assert.h b/src/common/assert.h index 8c927fc..67e7e93 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -69,7 +69,7 @@ void assert_fail_impl(); #define ASSERT_OR_EXECUTE(_a_, _b_) \ do { \ ASSERT(_a_); \ - if (!(_a_)) { \ + if (!(_a_)) [[unlikely]] { \ _b_ \ } \ } while (0) @@ -78,7 +78,7 @@ void assert_fail_impl(); #define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \ do { \ ASSERT_MSG(_a_, __VA_ARGS__); \ - if (!(_a_)) { \ + if (!(_a_)) [[unlikely]] { \ _b_ \ } \ } while (0) diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h index bef5015..aef3b66 100644 --- a/src/common/atomic_helpers.h +++ b/src/common/atomic_helpers.h @@ -156,6 +156,7 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN { break; default: assert(false); + break; } } diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 7e1df62..e4e58ea 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -141,10 +141,6 @@ public: constexpr BitField(BitField&&) noexcept = default; constexpr BitField& operator=(BitField&&) noexcept = default; - [[nodiscard]] constexpr operator T() const { - return Value(); - } - constexpr void Assign(const T& value) { #ifdef _MSC_VER storage = static_cast((storage & ~mask) | FormatValue(value)); @@ -162,6 +158,17 @@ public: return ExtractValue(storage); } + template + [[nodiscard]] constexpr ConvertedToType As() const { + static_assert(!std::is_same_v, + "Unnecessary cast. Use Value() instead."); + return static_cast(Value()); + } + + [[nodiscard]] constexpr operator T() const { + return Value(); + } + [[nodiscard]] constexpr explicit operator bool() const { return Value() != 0; } diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h index 7e46554..2121780 100644 --- a/src/common/bounded_threadsafe_queue.h +++ b/src/common/bounded_threadsafe_queue.h @@ -21,11 +21,6 @@ constexpr size_t hardware_interference_size = std::hardware_destructive_interfer constexpr size_t hardware_interference_size = 64; #endif -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4324) -#endif - template class MPSCQueue { public: @@ -160,8 +155,4 @@ private: static_assert(std::is_nothrow_destructible_v, "T must be nothrow destructible"); }; -#ifdef _MSC_VER -#pragma warning(pop) -#endif - } // namespace Common diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index e1e2a90..0dad933 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -31,8 +31,10 @@ #ifndef _MSC_VER -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) #define Crash() __asm__ __volatile__("int $3") +#elif defined(ARCHITECTURE_arm64) +#define Crash() __asm__ __volatile__("brk #0") #else #define Crash() exit(1) #endif diff --git a/src/common/common_precompiled_headers.h b/src/common/common_precompiled_headers.h new file mode 100644 index 0000000..be7e5b5 --- /dev/null +++ b/src/common/common_precompiled_headers.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include + +#include "common/assert.h" +#include "common/common_types.h" diff --git a/src/common/concepts.h b/src/common/concepts.h index a97555f..a9acff3 100644 --- a/src/common/concepts.h +++ b/src/common/concepts.h @@ -3,24 +3,14 @@ #pragma once +#include #include namespace Common { -// Check if type is like an STL container +// Check if type satisfies the ContiguousContainer named requirement. template -concept IsSTLContainer = requires(T t) { - typename T::value_type; - typename T::iterator; - typename T::const_iterator; - // TODO(ogniK): Replace below is std::same_as when MSVC supports it. - t.begin(); - t.end(); - t.cbegin(); - t.cend(); - t.data(); - t.size(); -}; +concept IsContiguousContainer = std::contiguous_iterator; // TODO: Replace with std::derived_from when the header // is available on all supported platforms. @@ -34,4 +24,12 @@ concept DerivedFrom = requires { template concept ConvertibleTo = std::is_convertible_v; +// No equivalents in the stdlib + +template +concept IsArithmetic = std::is_arithmetic_v; + +template +concept IsIntegral = std::is_integral_v; + } // namespace Common diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index 4a0f72c..f899b0d 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h @@ -4,14 +4,7 @@ // From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h // See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math -#ifndef FIXED_H_ -#define FIXED_H_ - -#if __cplusplus >= 201402L -#define CONSTEXPR14 constexpr -#else -#define CONSTEXPR14 -#endif +#pragma once #include // for size_t #include @@ -19,6 +12,8 @@ #include #include +#include + namespace Common { template @@ -57,8 +52,8 @@ struct type_from_size<64> { static constexpr size_t size = 64; using value_type = int64_t; - using unsigned_type = std::make_unsigned::type; - using signed_type = std::make_signed::type; + using unsigned_type = std::make_unsigned_t; + using signed_type = std::make_signed_t; using next_size = type_from_size<128>; }; @@ -68,8 +63,8 @@ struct type_from_size<32> { static constexpr size_t size = 32; using value_type = int32_t; - using unsigned_type = std::make_unsigned::type; - using signed_type = std::make_signed::type; + using unsigned_type = std::make_unsigned_t; + using signed_type = std::make_signed_t; using next_size = type_from_size<64>; }; @@ -79,8 +74,8 @@ struct type_from_size<16> { static constexpr size_t size = 16; using value_type = int16_t; - using unsigned_type = std::make_unsigned::type; - using signed_type = std::make_signed::type; + using unsigned_type = std::make_unsigned_t; + using signed_type = std::make_signed_t; using next_size = type_from_size<32>; }; @@ -90,8 +85,8 @@ struct type_from_size<8> { static constexpr size_t size = 8; using value_type = int8_t; - using unsigned_type = std::make_unsigned::type; - using signed_type = std::make_signed::type; + using unsigned_type = std::make_unsigned_t; + using signed_type = std::make_signed_t; using next_size = type_from_size<16>; }; @@ -106,9 +101,9 @@ constexpr B next_to_base(N rhs) { struct divide_by_zero : std::exception {}; template -CONSTEXPR14 FixedPoint divide( +constexpr FixedPoint divide( FixedPoint numerator, FixedPoint denominator, FixedPoint& remainder, - typename std::enable_if::next_size::is_specialized>::type* = nullptr) { + std::enable_if_t::next_size::is_specialized>* = nullptr) { using next_type = typename FixedPoint::next_type; using base_type = typename FixedPoint::base_type; @@ -126,9 +121,9 @@ CONSTEXPR14 FixedPoint divide( } template -CONSTEXPR14 FixedPoint divide( +constexpr FixedPoint divide( FixedPoint numerator, FixedPoint denominator, FixedPoint& remainder, - typename std::enable_if::next_size::is_specialized>::type* = nullptr) { + std::enable_if_t::next_size::is_specialized>* = nullptr) { using unsigned_type = typename FixedPoint::unsigned_type; @@ -196,9 +191,9 @@ CONSTEXPR14 FixedPoint divide( // this is the usual implementation of multiplication template -CONSTEXPR14 FixedPoint multiply( +constexpr FixedPoint multiply( FixedPoint lhs, FixedPoint rhs, - typename std::enable_if::next_size::is_specialized>::type* = nullptr) { + std::enable_if_t::next_size::is_specialized>* = nullptr) { using next_type = typename FixedPoint::next_type; using base_type = typename FixedPoint::base_type; @@ -215,9 +210,9 @@ CONSTEXPR14 FixedPoint multiply( // it is slightly slower, but is more robust since it doesn't // require and upgraded type template -CONSTEXPR14 FixedPoint multiply( +constexpr FixedPoint multiply( FixedPoint lhs, FixedPoint rhs, - typename std::enable_if::next_size::is_specialized>::type* = nullptr) { + std::enable_if_t::next_size::is_specialized>* = nullptr) { using base_type = typename FixedPoint::base_type; @@ -272,19 +267,20 @@ public: static constexpr base_type one = base_type(1) << fractional_bits; public: // constructors - FixedPoint() = default; - FixedPoint(const FixedPoint&) = default; - FixedPoint(FixedPoint&&) = default; - FixedPoint& operator=(const FixedPoint&) = default; + constexpr FixedPoint() = default; - template - constexpr FixedPoint( - Number n, typename std::enable_if::value>::type* = nullptr) - : data_(static_cast(n * one)) {} + constexpr FixedPoint(const FixedPoint&) = default; + constexpr FixedPoint& operator=(const FixedPoint&) = default; + + constexpr FixedPoint(FixedPoint&&) noexcept = default; + constexpr FixedPoint& operator=(FixedPoint&&) noexcept = default; + + template + constexpr FixedPoint(Number n) : data_(static_cast(n * one)) {} public: // conversion template - CONSTEXPR14 explicit FixedPoint(FixedPoint other) { + constexpr explicit FixedPoint(FixedPoint other) { static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types"); using T = FixedPoint; @@ -308,36 +304,14 @@ public: } public: // comparison operators - constexpr bool operator==(FixedPoint rhs) const { - return data_ == rhs.data_; - } - - constexpr bool operator!=(FixedPoint rhs) const { - return data_ != rhs.data_; - } - - constexpr bool operator<(FixedPoint rhs) const { - return data_ < rhs.data_; - } - - constexpr bool operator>(FixedPoint rhs) const { - return data_ > rhs.data_; - } - - constexpr bool operator<=(FixedPoint rhs) const { - return data_ <= rhs.data_; - } - - constexpr bool operator>=(FixedPoint rhs) const { - return data_ >= rhs.data_; - } + friend constexpr auto operator<=>(FixedPoint lhs, FixedPoint rhs) = default; public: // unary operators - constexpr bool operator!() const { + [[nodiscard]] constexpr bool operator!() const { return !data_; } - constexpr FixedPoint operator~() const { + [[nodiscard]] constexpr FixedPoint operator~() const { // NOTE(eteran): this will often appear to "just negate" the value // that is not an error, it is because -x == (~x+1) // and that "+1" is adding an infinitesimally small fraction to the @@ -345,89 +319,87 @@ public: // unary operators return FixedPoint::from_base(~data_); } - constexpr FixedPoint operator-() const { + [[nodiscard]] constexpr FixedPoint operator-() const { return FixedPoint::from_base(-data_); } - constexpr FixedPoint operator+() const { + [[nodiscard]] constexpr FixedPoint operator+() const { return FixedPoint::from_base(+data_); } - CONSTEXPR14 FixedPoint& operator++() { + constexpr FixedPoint& operator++() { data_ += one; return *this; } - CONSTEXPR14 FixedPoint& operator--() { + constexpr FixedPoint& operator--() { data_ -= one; return *this; } - CONSTEXPR14 FixedPoint operator++(int) { + constexpr FixedPoint operator++(int) { FixedPoint tmp(*this); data_ += one; return tmp; } - CONSTEXPR14 FixedPoint operator--(int) { + constexpr FixedPoint operator--(int) { FixedPoint tmp(*this); data_ -= one; return tmp; } public: // basic math operators - CONSTEXPR14 FixedPoint& operator+=(FixedPoint n) { + constexpr FixedPoint& operator+=(FixedPoint n) { data_ += n.data_; return *this; } - CONSTEXPR14 FixedPoint& operator-=(FixedPoint n) { + constexpr FixedPoint& operator-=(FixedPoint n) { data_ -= n.data_; return *this; } - CONSTEXPR14 FixedPoint& operator*=(FixedPoint n) { + constexpr FixedPoint& operator*=(FixedPoint n) { return assign(detail::multiply(*this, n)); } - CONSTEXPR14 FixedPoint& operator/=(FixedPoint n) { + constexpr FixedPoint& operator/=(FixedPoint n) { FixedPoint temp; return assign(detail::divide(*this, n, temp)); } private: - CONSTEXPR14 FixedPoint& assign(FixedPoint rhs) { + constexpr FixedPoint& assign(FixedPoint rhs) { data_ = rhs.data_; return *this; } public: // binary math operators, effects underlying bit pattern since these // don't really typically make sense for non-integer values - CONSTEXPR14 FixedPoint& operator&=(FixedPoint n) { + constexpr FixedPoint& operator&=(FixedPoint n) { data_ &= n.data_; return *this; } - CONSTEXPR14 FixedPoint& operator|=(FixedPoint n) { + constexpr FixedPoint& operator|=(FixedPoint n) { data_ |= n.data_; return *this; } - CONSTEXPR14 FixedPoint& operator^=(FixedPoint n) { + constexpr FixedPoint& operator^=(FixedPoint n) { data_ ^= n.data_; return *this; } - template ::value>::type> - CONSTEXPR14 FixedPoint& operator>>=(Integer n) { + template + constexpr FixedPoint& operator>>=(Integer n) { data_ >>= n; return *this; } - template ::value>::type> - CONSTEXPR14 FixedPoint& operator<<=(Integer n) { + template + constexpr FixedPoint& operator<<=(Integer n) { data_ <<= n; return *this; } @@ -437,42 +409,42 @@ public: // conversion to basic types data_ += (data_ & fractional_mask) >> 1; } - constexpr int to_int() { + [[nodiscard]] constexpr int to_int() { round_up(); return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr unsigned int to_uint() const { + [[nodiscard]] constexpr unsigned int to_uint() { round_up(); return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr int64_t to_long() { + [[nodiscard]] constexpr int64_t to_long() { round_up(); return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr int to_int_floor() const { + [[nodiscard]] constexpr int to_int_floor() const { return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr int64_t to_long_floor() { + [[nodiscard]] constexpr int64_t to_long_floor() const { return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr unsigned int to_uint_floor() const { + [[nodiscard]] constexpr unsigned int to_uint_floor() const { return static_cast((data_ & integer_mask) >> fractional_bits); } - constexpr float to_float() const { + [[nodiscard]] constexpr float to_float() const { return static_cast(data_) / FixedPoint::one; } - constexpr double to_double() const { + [[nodiscard]] constexpr double to_double() const { return static_cast(data_) / FixedPoint::one; } - constexpr base_type to_raw() const { + [[nodiscard]] constexpr base_type to_raw() const { return data_; } @@ -480,27 +452,27 @@ public: // conversion to basic types data_ &= fractional_mask; } - constexpr base_type get_frac() const { + [[nodiscard]] constexpr base_type get_frac() const { return data_ & fractional_mask; } public: - CONSTEXPR14 void swap(FixedPoint& rhs) { + constexpr void swap(FixedPoint& rhs) noexcept { using std::swap; swap(data_, rhs.data_); } public: - base_type data_; + base_type data_{}; }; // if we have the same fractional portion, but differing integer portions, we trivially upgrade the // smaller type template -CONSTEXPR14 typename std::conditional= I2, FixedPoint, FixedPoint>::type -operator+(FixedPoint lhs, FixedPoint rhs) { +constexpr std::conditional_t= I2, FixedPoint, FixedPoint> operator+( + FixedPoint lhs, FixedPoint rhs) { - using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; + using T = std::conditional_t= I2, FixedPoint, FixedPoint>; const T l = T::from_base(lhs.to_raw()); const T r = T::from_base(rhs.to_raw()); @@ -508,10 +480,10 @@ operator+(FixedPoint lhs, FixedPoint rhs) { } template -CONSTEXPR14 typename std::conditional= I2, FixedPoint, FixedPoint>::type -operator-(FixedPoint lhs, FixedPoint rhs) { +constexpr std::conditional_t= I2, FixedPoint, FixedPoint> operator-( + FixedPoint lhs, FixedPoint rhs) { - using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; + using T = std::conditional_t= I2, FixedPoint, FixedPoint>; const T l = T::from_base(lhs.to_raw()); const T r = T::from_base(rhs.to_raw()); @@ -519,10 +491,10 @@ operator-(FixedPoint lhs, FixedPoint rhs) { } template -CONSTEXPR14 typename std::conditional= I2, FixedPoint, FixedPoint>::type -operator*(FixedPoint lhs, FixedPoint rhs) { +constexpr std::conditional_t= I2, FixedPoint, FixedPoint> operator*( + FixedPoint lhs, FixedPoint rhs) { - using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; + using T = std::conditional_t= I2, FixedPoint, FixedPoint>; const T l = T::from_base(lhs.to_raw()); const T r = T::from_base(rhs.to_raw()); @@ -530,10 +502,10 @@ operator*(FixedPoint lhs, FixedPoint rhs) { } template -CONSTEXPR14 typename std::conditional= I2, FixedPoint, FixedPoint>::type -operator/(FixedPoint lhs, FixedPoint rhs) { +constexpr std::conditional_t= I2, FixedPoint, FixedPoint> operator/( + FixedPoint lhs, FixedPoint rhs) { - using T = typename std::conditional= I2, FixedPoint, FixedPoint>::type; + using T = std::conditional_t= I2, FixedPoint, FixedPoint>; const T l = T::from_base(lhs.to_raw()); const T r = T::from_base(rhs.to_raw()); @@ -548,159 +520,133 @@ std::ostream& operator<<(std::ostream& os, FixedPoint f) { // basic math operators template -CONSTEXPR14 FixedPoint operator+(FixedPoint lhs, FixedPoint rhs) { +constexpr FixedPoint operator+(FixedPoint lhs, FixedPoint rhs) { lhs += rhs; return lhs; } template -CONSTEXPR14 FixedPoint operator-(FixedPoint lhs, FixedPoint rhs) { +constexpr FixedPoint operator-(FixedPoint lhs, FixedPoint rhs) { lhs -= rhs; return lhs; } template -CONSTEXPR14 FixedPoint operator*(FixedPoint lhs, FixedPoint rhs) { +constexpr FixedPoint operator*(FixedPoint lhs, FixedPoint rhs) { lhs *= rhs; return lhs; } template -CONSTEXPR14 FixedPoint operator/(FixedPoint lhs, FixedPoint rhs) { +constexpr FixedPoint operator/(FixedPoint lhs, FixedPoint rhs) { lhs /= rhs; return lhs; } -template ::value>::type> -CONSTEXPR14 FixedPoint operator+(FixedPoint lhs, Number rhs) { +template +constexpr FixedPoint operator+(FixedPoint lhs, Number rhs) { lhs += FixedPoint(rhs); return lhs; } -template ::value>::type> -CONSTEXPR14 FixedPoint operator-(FixedPoint lhs, Number rhs) { +template +constexpr FixedPoint operator-(FixedPoint lhs, Number rhs) { lhs -= FixedPoint(rhs); return lhs; } -template ::value>::type> -CONSTEXPR14 FixedPoint operator*(FixedPoint lhs, Number rhs) { +template +constexpr FixedPoint operator*(FixedPoint lhs, Number rhs) { lhs *= FixedPoint(rhs); return lhs; } -template ::value>::type> -CONSTEXPR14 FixedPoint operator/(FixedPoint lhs, Number rhs) { +template +constexpr FixedPoint operator/(FixedPoint lhs, Number rhs) { lhs /= FixedPoint(rhs); return lhs; } -template ::value>::type> -CONSTEXPR14 FixedPoint operator+(Number lhs, FixedPoint rhs) { +template +constexpr FixedPoint operator+(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp += rhs; return tmp; } -template ::value>::type> -CONSTEXPR14 FixedPoint operator-(Number lhs, FixedPoint rhs) { +template +constexpr FixedPoint operator-(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp -= rhs; return tmp; } -template ::value>::type> -CONSTEXPR14 FixedPoint operator*(Number lhs, FixedPoint rhs) { +template +constexpr FixedPoint operator*(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp *= rhs; return tmp; } -template ::value>::type> -CONSTEXPR14 FixedPoint operator/(Number lhs, FixedPoint rhs) { +template +constexpr FixedPoint operator/(Number lhs, FixedPoint rhs) { FixedPoint tmp(lhs); tmp /= rhs; return tmp; } // shift operators -template ::value>::type> -CONSTEXPR14 FixedPoint operator<<(FixedPoint lhs, Integer rhs) { +template +constexpr FixedPoint operator<<(FixedPoint lhs, Integer rhs) { lhs <<= rhs; return lhs; } -template ::value>::type> -CONSTEXPR14 FixedPoint operator>>(FixedPoint lhs, Integer rhs) { +template +constexpr FixedPoint operator>>(FixedPoint lhs, Integer rhs) { lhs >>= rhs; return lhs; } // comparison operators -template ::value>::type> +template constexpr bool operator>(FixedPoint lhs, Number rhs) { return lhs > FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator<(FixedPoint lhs, Number rhs) { return lhs < FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator>=(FixedPoint lhs, Number rhs) { return lhs >= FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator<=(FixedPoint lhs, Number rhs) { return lhs <= FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator==(FixedPoint lhs, Number rhs) { return lhs == FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator!=(FixedPoint lhs, Number rhs) { return lhs != FixedPoint(rhs); } -template ::value>::type> +template constexpr bool operator>(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) > rhs; } -template ::value>::type> +template constexpr bool operator<(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) < rhs; } -template ::value>::type> +template constexpr bool operator>=(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) >= rhs; } -template ::value>::type> +template constexpr bool operator<=(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) <= rhs; } -template ::value>::type> +template constexpr bool operator==(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) == rhs; } -template ::value>::type> +template constexpr bool operator!=(Number lhs, FixedPoint rhs) { return FixedPoint(lhs) != rhs; } } // namespace Common - -#undef CONSTEXPR14 - -#endif diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp index fa8422c..656b03c 100644 --- a/src/common/fs/file.cpp +++ b/src/common/fs/file.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/fs/file.h" #include "common/fs/fs.h" #include "common/logging/log.h" diff --git a/src/common/fs/file.h b/src/common/fs/file.h index 69b5338..167c4d8 100644 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h @@ -209,8 +209,8 @@ public: /** * Helper function which deduces the value type of a contiguous STL container used in ReadSpan. - * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls - * ReadObject and T must be a trivially copyable object. + * If T is not a contiguous container as defined by the concept IsContiguousContainer, this + * calls ReadObject and T must be a trivially copyable object. * * See ReadSpan for more details if T is a contiguous container. * See ReadObject for more details if T is a trivially copyable object. @@ -223,7 +223,7 @@ public: */ template [[nodiscard]] size_t Read(T& data) const { - if constexpr (IsSTLContainer) { + if constexpr (IsContiguousContainer) { using ContiguousType = typename T::value_type; static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); @@ -235,8 +235,8 @@ public: /** * Helper function which deduces the value type of a contiguous STL container used in WriteSpan. - * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls - * WriteObject and T must be a trivially copyable object. + * If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this + * calls WriteObject and T must be a trivially copyable object. * * See WriteSpan for more details if T is a contiguous container. * See WriteObject for more details if T is a trivially copyable object. @@ -249,7 +249,7 @@ public: */ template [[nodiscard]] size_t Write(const T& data) const { - if constexpr (IsSTLContainer) { + if constexpr (IsContiguousContainer) { using ContiguousType = typename T::value_type; static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp index eb4ac1d..813a713 100644 --- a/src/common/fs/fs_util.cpp +++ b/src/common/fs/fs_util.cpp @@ -4,6 +4,7 @@ #include #include "common/fs/fs_util.h" +#include "common/polyfill_ranges.h" namespace Common::FS { diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 1074f24..defa3e9 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include "common/fs/fs.h" diff --git a/src/common/hash.h b/src/common/hash.h index b6f3e6d..e8fe78b 100644 --- a/src/common/hash.h +++ b/src/common/hash.h @@ -18,4 +18,11 @@ struct PairHash { } }; +template +struct IdentityHash { + [[nodiscard]] size_t operator()(T value) const noexcept { + return static_cast(value); + } +}; + } // namespace Common diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 7f96596..909f6cf 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -359,6 +359,12 @@ public: } }); + long page_size = sysconf(_SC_PAGESIZE); + if (page_size != 0x1000) { + LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size); + throw std::bad_alloc{}; + } + // Backing memory initialization #if defined(__FreeBSD__) && __FreeBSD__ < 13 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 diff --git a/src/common/input.h b/src/common/input.h index 825b0d6..fc14fd7 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "common/logging/log.h" #include "common/param_package.h" #include "common/uuid.h" @@ -76,6 +77,19 @@ enum class PollingError { Unknown, }; +// Nfc reply from the controller +enum class NfcState { + Success, + NewAmiibo, + WaitingForAmiibo, + AmiiboRemoved, + NotAnAmiibo, + NotSupported, + WrongDeviceState, + WriteFailed, + Unknown, +}; + // Ir camera reply from the controller enum class CameraError { None, @@ -87,7 +101,6 @@ enum class CameraError { enum class VibrationAmplificationType { Linear, Exponential, - Test, }; // Analog properties for calibration @@ -202,6 +215,11 @@ struct CameraStatus { std::vector data{}; }; +struct NfcStatus { + NfcState state{}; + std::vector data{}; +}; + // List of buttons to be passed to Qt that can be translated enum class ButtonNames { Undefined, @@ -259,7 +277,9 @@ struct CallbackStatus { BodyColorStatus color_status{}; BatteryStatus battery_status{}; VibrationStatus vibration_status{}; - CameraStatus camera_status{}; + CameraFormat camera_status{CameraFormat::None}; + NfcState nfc_status{NfcState::Unknown}; + std::vector raw_data{}; }; // Triggered once every input change @@ -305,6 +325,10 @@ public: return VibrationError::NotSupported; } + virtual bool IsVibrationEnabled() { + return false; + } + virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { return PollingError::NotSupported; } @@ -312,6 +336,14 @@ public: virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { return CameraError::NotSupported; } + + virtual NfcState SupportsNfc() const { + return NfcState::NotSupported; + } + + virtual NfcState WriteNfcData([[maybe_unused]] const std::vector& data) { + return NfcState::NotSupported; + } }; /// An abstract class template for a factory that can create input devices. @@ -352,6 +384,16 @@ void RegisterFactory(const std::string& name, std::shared_ptr> factory) { + RegisterFactory(name, std::move(factory)); +} + +inline void RegisterOutputFactory(const std::string& name, + std::shared_ptr> factory) { + RegisterFactory(name, std::move(factory)); +} + /** * Unregisters an input device factory. * @tparam InputDeviceType the type of input devices the factory can create @@ -364,6 +406,14 @@ void UnregisterFactory(const std::string& name) { } } +inline void UnregisterInputFactory(const std::string& name) { + UnregisterFactory(name); +} + +inline void UnregisterOutputFactory(const std::string& name) { + UnregisterFactory(name); +} + /** * Create an input device from given paramters. * @tparam InputDeviceType the type of input devices to create @@ -385,13 +435,21 @@ std::unique_ptr CreateDeviceFromString(const std::string& param return pair->second->Create(package); } +inline std::unique_ptr CreateInputDeviceFromString(const std::string& params) { + return CreateDeviceFromString(params); +} + +inline std::unique_ptr CreateOutputDeviceFromString(const std::string& params) { + return CreateDeviceFromString(params); +} + /** - * Create an input device from given paramters. + * Create an input device from given parameters. * @tparam InputDeviceType the type of input devices to create - * @param A ParamPackage that contains all parameters for creating the device + * @param package A ParamPackage that contains all parameters for creating the device */ template -std::unique_ptr CreateDevice(const Common::ParamPackage package) { +std::unique_ptr CreateDevice(const ParamPackage& package) { const std::string engine = package.Get("engine", "null"); const auto& factory_list = Impl::FactoryList::list; const auto pair = factory_list.find(engine); @@ -404,4 +462,12 @@ std::unique_ptr CreateDevice(const Common::ParamPackage package return pair->second->Create(package); } +inline std::unique_ptr CreateInputDevice(const ParamPackage& package) { + return CreateDevice(package); +} + +inline std::unique_ptr CreateOutputDevice(const ParamPackage& package) { + return CreateDevice(package); +} + } // namespace Common::Input diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 8ce1c2f..2a3bded 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -18,6 +17,7 @@ #include "common/fs/fs_paths.h" #include "common/fs/path_util.h" #include "common/literals.h" +#include "common/polyfill_thread.h" #include "common/thread.h" #include "common/logging/backend.h" @@ -219,7 +219,7 @@ private: void StartBackendThread() { backend_thread = std::jthread([this](std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:Log"); + Common::SetCurrentThreadName("Logger"); Entry entry; const auto write_logs = [this, &entry]() { ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); diff --git a/src/common/multi_level_page_table.cpp b/src/common/multi_level_page_table.cpp new file mode 100644 index 0000000..46e362f --- /dev/null +++ b/src/common/multi_level_page_table.cpp @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/multi_level_page_table.inc" + +namespace Common { +template class Common::MultiLevelPageTable; +template class Common::MultiLevelPageTable; +} // namespace Common diff --git a/src/common/multi_level_page_table.h b/src/common/multi_level_page_table.h new file mode 100644 index 0000000..31f6676 --- /dev/null +++ b/src/common/multi_level_page_table.h @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" + +namespace Common { + +template +class MultiLevelPageTable final { +public: + constexpr MultiLevelPageTable() = default; + explicit MultiLevelPageTable(std::size_t address_space_bits, std::size_t first_level_bits, + std::size_t page_bits); + + ~MultiLevelPageTable() noexcept; + + MultiLevelPageTable(const MultiLevelPageTable&) = delete; + MultiLevelPageTable& operator=(const MultiLevelPageTable&) = delete; + + MultiLevelPageTable(MultiLevelPageTable&& other) noexcept + : address_space_bits{std::exchange(other.address_space_bits, 0)}, + first_level_bits{std::exchange(other.first_level_bits, 0)}, page_bits{std::exchange( + other.page_bits, 0)}, + first_level_shift{std::exchange(other.first_level_shift, 0)}, + first_level_chunk_size{std::exchange(other.first_level_chunk_size, 0)}, + first_level_map{std::move(other.first_level_map)}, base_ptr{std::exchange(other.base_ptr, + nullptr)} {} + + MultiLevelPageTable& operator=(MultiLevelPageTable&& other) noexcept { + address_space_bits = std::exchange(other.address_space_bits, 0); + first_level_bits = std::exchange(other.first_level_bits, 0); + page_bits = std::exchange(other.page_bits, 0); + first_level_shift = std::exchange(other.first_level_shift, 0); + first_level_chunk_size = std::exchange(other.first_level_chunk_size, 0); + alloc_size = std::exchange(other.alloc_size, 0); + first_level_map = std::move(other.first_level_map); + base_ptr = std::exchange(other.base_ptr, nullptr); + return *this; + } + + void ReserveRange(u64 start, std::size_t size); + + [[nodiscard]] const BaseAddr& operator[](std::size_t index) const { + return base_ptr[index]; + } + + [[nodiscard]] BaseAddr& operator[](std::size_t index) { + return base_ptr[index]; + } + + [[nodiscard]] BaseAddr* data() { + return base_ptr; + } + + [[nodiscard]] const BaseAddr* data() const { + return base_ptr; + } + +private: + void AllocateLevel(u64 level); + + std::size_t address_space_bits{}; + std::size_t first_level_bits{}; + std::size_t page_bits{}; + std::size_t first_level_shift{}; + std::size_t first_level_chunk_size{}; + std::size_t alloc_size{}; + std::vector first_level_map{}; + BaseAddr* base_ptr{}; +}; + +} // namespace Common diff --git a/src/common/multi_level_page_table.inc b/src/common/multi_level_page_table.inc new file mode 100644 index 0000000..8ac506f --- /dev/null +++ b/src/common/multi_level_page_table.inc @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "common/assert.h" +#include "common/multi_level_page_table.h" + +namespace Common { + +template +MultiLevelPageTable::MultiLevelPageTable(std::size_t address_space_bits_, + std::size_t first_level_bits_, + std::size_t page_bits_) + : address_space_bits{address_space_bits_}, + first_level_bits{first_level_bits_}, page_bits{page_bits_} { + if (page_bits == 0) { + return; + } + first_level_shift = address_space_bits - first_level_bits; + first_level_chunk_size = (1ULL << (first_level_shift - page_bits)) * sizeof(BaseAddr); + alloc_size = (1ULL << (address_space_bits - page_bits)) * sizeof(BaseAddr); + std::size_t first_level_size = 1ULL << first_level_bits; + first_level_map.resize(first_level_size, nullptr); +#ifdef _WIN32 + void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)}; +#else + void* base{mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)}; + + if (base == MAP_FAILED) { + base = nullptr; + } +#endif + + ASSERT(base); + base_ptr = reinterpret_cast(base); +} + +template +MultiLevelPageTable::~MultiLevelPageTable() noexcept { + if (!base_ptr) { + return; + } +#ifdef _WIN32 + ASSERT(VirtualFree(base_ptr, 0, MEM_RELEASE)); +#else + ASSERT(munmap(base_ptr, alloc_size) == 0); +#endif +} + +template +void MultiLevelPageTable::ReserveRange(u64 start, std::size_t size) { + const u64 new_start = start >> first_level_shift; + const u64 new_end = (start + size) >> first_level_shift; + for (u64 i = new_start; i <= new_end; i++) { + if (!first_level_map[i]) { + AllocateLevel(i); + } + } +} + +template +void MultiLevelPageTable::AllocateLevel(u64 level) { + void* ptr = reinterpret_cast(base_ptr) + level * first_level_chunk_size; +#ifdef _WIN32 + void* base{VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE)}; +#else + void* base{mmap(ptr, first_level_chunk_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)}; + + if (base == MAP_FAILED) { + base = nullptr; + } +#endif + ASSERT(base); + + first_level_map[level] = base; +} + +} // namespace Common diff --git a/src/common/polyfill_ranges.h b/src/common/polyfill_ranges.h new file mode 100644 index 0000000..ca44bfa --- /dev/null +++ b/src/common/polyfill_ranges.h @@ -0,0 +1,530 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// +// TODO: remove this file when ranges are supported by all compilation targets +// + +#pragma once + +#include +#include +#include + +#ifndef __cpp_lib_ranges + +namespace std { +namespace ranges { + +template +concept range = requires(T& t) { + begin(t); + end(t); +}; + +template +concept input_range = range; + +template +concept output_range = range; + +template +using range_difference_t = ptrdiff_t; + +// +// find, find_if, find_if_not +// + +struct find_fn { + template + constexpr Iterator operator()(Iterator first, Iterator last, const T& value, + Proj proj = {}) const { + for (; first != last; ++first) { + if (std::invoke(proj, *first) == value) { + return first; + } + } + return first; + } + + template + constexpr ranges::iterator_t operator()(R&& r, const T& value, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj)); + } +}; + +struct find_if_fn { + template + constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + for (; first != last; ++first) { + if (std::invoke(pred, std::invoke(proj, *first))) { + return first; + } + } + return first; + } + + template + constexpr ranges::iterator_t operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +struct find_if_not_fn { + template + constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + for (; first != last; ++first) { + if (!std::invoke(pred, std::invoke(proj, *first))) { + return first; + } + } + return first; + } + + template + constexpr ranges::iterator_t operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +inline constexpr find_fn find; +inline constexpr find_if_fn find_if; +inline constexpr find_if_not_fn find_if_not; + +// +// any_of, all_of, none_of +// + +struct all_of_fn { + template + constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + return ranges::find_if_not(first, last, std::ref(pred), std::ref(proj)) == last; + } + + template + constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +struct any_of_fn { + template + constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) != last; + } + + template + constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +struct none_of_fn { + template + constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) == last; + } + + template + constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +inline constexpr any_of_fn any_of; +inline constexpr all_of_fn all_of; +inline constexpr none_of_fn none_of; + +// +// count, count_if +// + +struct count_fn { + template + constexpr ptrdiff_t operator()(Iterator first, Iterator last, const T& value, + Proj proj = {}) const { + ptrdiff_t counter = 0; + for (; first != last; ++first) + if (std::invoke(proj, *first) == value) + ++counter; + return counter; + } + + template + constexpr ptrdiff_t operator()(R&& r, const T& value, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj)); + } +}; + +struct count_if_fn { + template + constexpr ptrdiff_t operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + ptrdiff_t counter = 0; + for (; first != last; ++first) + if (std::invoke(pred, std::invoke(proj, *first))) + ++counter; + return counter; + } + + template + constexpr ptrdiff_t operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +inline constexpr count_fn count; +inline constexpr count_if_fn count_if; + +// +// transform +// + +struct transform_fn { + template + constexpr void operator()(InputIterator first1, InputIterator last1, OutputIterator result, + F op, Proj proj = {}) const { + for (; first1 != last1; ++first1, (void)++result) { + *result = std::invoke(op, std::invoke(proj, *first1)); + } + } + + template + constexpr void operator()(R&& r, OutputIterator result, F op, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), result, std::ref(op), std::ref(proj)); + } +}; + +inline constexpr transform_fn transform; + +// +// sort +// + +struct sort_fn { + template + constexpr void operator()(Iterator first, Iterator last, Comp comp = {}, Proj proj = {}) const { + if (first == last) + return; + + Iterator last_iter = ranges::next(first, last); + std::sort(first, last_iter, + [&](auto& lhs, auto& rhs) { return comp(proj(lhs), proj(rhs)); }); + } + + template + constexpr void operator()(R&& r, Comp comp = {}, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(comp), std::move(proj)); + } +}; + +inline constexpr sort_fn sort; + +// +// fill +// + +struct fill_fn { + template + constexpr OutputIterator operator()(OutputIterator first, OutputIterator last, + const T& value) const { + while (first != last) { + *first++ = value; + } + + return first; + } + + template + constexpr ranges::iterator_t operator()(R&& r, const T& value) const { + return operator()(ranges::begin(r), ranges::end(r), value); + } +}; + +inline constexpr fill_fn fill; + +// +// for_each +// + +struct for_each_fn { + template + constexpr void operator()(Iterator first, Iterator last, Fun f, Proj proj = {}) const { + for (; first != last; ++first) { + std::invoke(f, std::invoke(proj, *first)); + } + } + + template + constexpr void operator()(R&& r, Fun f, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(f), std::ref(proj)); + } +}; + +inline constexpr for_each_fn for_each; + +// +// min_element, max_element +// + +struct min_element_fn { + template + constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {}, + Proj proj = {}) const { + if (first == last) { + return last; + } + + auto smallest = first; + ++first; + for (; first != last; ++first) { + if (!std::invoke(comp, std::invoke(proj, *smallest), std::invoke(proj, *first))) { + smallest = first; + } + } + return smallest; + } + + template + constexpr ranges::iterator_t operator()(R&& r, Comp comp = {}, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj)); + } +}; + +struct max_element_fn { + template + constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {}, + Proj proj = {}) const { + if (first == last) { + return last; + } + + auto largest = first; + ++first; + for (; first != last; ++first) { + if (std::invoke(comp, std::invoke(proj, *largest), std::invoke(proj, *first))) { + largest = first; + } + } + return largest; + } + + template + constexpr ranges::iterator_t operator()(R&& r, Comp comp = {}, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj)); + } +}; + +inline constexpr min_element_fn min_element; +inline constexpr max_element_fn max_element; + +// +// replace, replace_if +// + +struct replace_fn { + template + constexpr Iterator operator()(Iterator first, Iterator last, const T1& old_value, + const T2& new_value, Proj proj = {}) const { + for (; first != last; ++first) { + if (old_value == std::invoke(proj, *first)) { + *first = new_value; + } + } + return first; + } + + template + constexpr ranges::iterator_t operator()(R&& r, const T1& old_value, const T2& new_value, + Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), old_value, new_value, std::move(proj)); + } +}; + +struct replace_if_fn { + template + constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, const T& new_value, + Proj proj = {}) const { + for (; first != last; ++first) { + if (!!std::invoke(pred, std::invoke(proj, *first))) { + *first = new_value; + } + } + return std::move(first); + } + + template + constexpr ranges::iterator_t operator()(R&& r, Pred pred, const T& new_value, + Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(pred), new_value, + std::move(proj)); + } +}; + +inline constexpr replace_fn replace; +inline constexpr replace_if_fn replace_if; + +// +// copy, copy_if +// + +struct copy_fn { + template + constexpr void operator()(InputIterator first, InputIterator last, + OutputIterator result) const { + for (; first != last; ++first, (void)++result) { + *result = *first; + } + } + + template + constexpr void operator()(R&& r, OutputIterator result) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(result)); + } +}; + +struct copy_if_fn { + template + constexpr void operator()(InputIterator first, InputIterator last, OutputIterator result, + Pred pred, Proj proj = {}) const { + for (; first != last; ++first) { + if (std::invoke(pred, std::invoke(proj, *first))) { + *result = *first; + ++result; + } + } + } + + template + constexpr void operator()(R&& r, OutputIterator result, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(result), std::ref(pred), + std::ref(proj)); + } +}; + +inline constexpr copy_fn copy; +inline constexpr copy_if_fn copy_if; + +// +// generate +// + +struct generate_fn { + template + constexpr Iterator operator()(Iterator first, Iterator last, F gen) const { + for (; first != last; *first = std::invoke(gen), ++first) + ; + return first; + } + + template + requires std::invocable && ranges::output_range + constexpr ranges::iterator_t operator()(R&& r, F gen) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(gen)); + } +}; + +inline constexpr generate_fn generate; + +// +// lower_bound, upper_bound +// + +struct lower_bound_fn { + template + constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {}, + Proj proj = {}) const { + Iterator it; + std::ptrdiff_t _count, _step; + _count = std::distance(first, last); + + while (_count > 0) { + it = first; + _step = _count / 2; + ranges::advance(it, _step, last); + if (comp(std::invoke(proj, *it), value)) { + first = ++it; + _count -= _step + 1; + } else { + _count = _step; + } + } + return first; + } + + template + constexpr ranges::iterator_t operator()(R&& r, const T& value, Comp comp = {}, + Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj)); + } +}; + +struct upper_bound_fn { + template + constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {}, + Proj proj = {}) const { + Iterator it; + std::ptrdiff_t _count, _step; + _count = std::distance(first, last); + + while (_count > 0) { + it = first; + _step = _count / 2; + ranges::advance(it, _step, last); + if (!comp(value, std::invoke(proj, *it))) { + first = ++it; + _count -= _step + 1; + } else { + _count = _step; + } + } + return first; + } + + template + constexpr ranges::iterator_t operator()(R&& r, const T& value, Comp comp = {}, + Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj)); + } +}; + +inline constexpr lower_bound_fn lower_bound; +inline constexpr upper_bound_fn upper_bound; + +// +// adjacent_find +// + +struct adjacent_find_fn { + template + constexpr Iterator operator()(Iterator first, Iterator last, Pred pred = {}, + Proj proj = {}) const { + if (first == last) + return first; + auto _next = ranges::next(first); + for (; _next != last; ++_next, ++first) + if (std::invoke(pred, std::invoke(proj, *first), std::invoke(proj, *_next))) + return first; + return _next; + } + + template + constexpr ranges::iterator_t operator()(R&& r, Pred pred = {}, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +inline constexpr adjacent_find_fn adjacent_find; + +} // namespace ranges +} // namespace std + +#endif diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h new file mode 100644 index 0000000..5a8d1ce --- /dev/null +++ b/src/common/polyfill_thread.h @@ -0,0 +1,323 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// +// TODO: remove this file when jthread is supported by all compilation targets +// + +#pragma once + +#include + +#ifdef __cpp_lib_jthread + +#include +#include + +namespace Common { + +template +void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) { + cv.wait(lock, token, std::move(pred)); +} + +} // namespace Common + +#else + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace std { +namespace polyfill { + +using stop_state_callbacks = list>; + +class stop_state { +public: + stop_state() = default; + ~stop_state() = default; + + bool request_stop() { + stop_state_callbacks callbacks; + + { + scoped_lock lk{m_lock}; + + if (m_stop_requested.load()) { + // Already set, nothing to do + return false; + } + + // Set as requested + m_stop_requested = true; + + // Copy callback list + callbacks = m_callbacks; + } + + for (auto callback : callbacks) { + callback(); + } + + return true; + } + + bool stop_requested() const { + return m_stop_requested.load(); + } + + stop_state_callbacks::const_iterator insert_callback(function f) { + stop_state_callbacks::const_iterator ret{}; + bool should_run{}; + + { + scoped_lock lk{m_lock}; + should_run = m_stop_requested.load(); + m_callbacks.push_front(f); + ret = m_callbacks.begin(); + } + + if (should_run) { + f(); + } + + return ret; + } + + void remove_callback(stop_state_callbacks::const_iterator it) { + scoped_lock lk{m_lock}; + m_callbacks.erase(it); + } + +private: + mutex m_lock; + atomic m_stop_requested; + stop_state_callbacks m_callbacks; +}; + +} // namespace polyfill + +class stop_token; +class stop_source; +struct nostopstate_t { + explicit nostopstate_t() = default; +}; +inline constexpr nostopstate_t nostopstate{}; + +template +class stop_callback; + +class stop_token { +public: + stop_token() noexcept = default; + + stop_token(const stop_token&) noexcept = default; + stop_token(stop_token&&) noexcept = default; + stop_token& operator=(const stop_token&) noexcept = default; + stop_token& operator=(stop_token&&) noexcept = default; + ~stop_token() = default; + + void swap(stop_token& other) noexcept { + m_stop_state.swap(other.m_stop_state); + } + + [[nodiscard]] bool stop_requested() const noexcept { + return m_stop_state && m_stop_state->stop_requested(); + } + [[nodiscard]] bool stop_possible() const noexcept { + return m_stop_state != nullptr; + } + +private: + friend class stop_source; + template + friend class stop_callback; + stop_token(shared_ptr stop_state) : m_stop_state(move(stop_state)) {} + +private: + shared_ptr m_stop_state; +}; + +class stop_source { +public: + stop_source() : m_stop_state(make_shared()) {} + explicit stop_source(nostopstate_t) noexcept {} + + stop_source(const stop_source&) noexcept = default; + stop_source(stop_source&&) noexcept = default; + stop_source& operator=(const stop_source&) noexcept = default; + stop_source& operator=(stop_source&&) noexcept = default; + ~stop_source() = default; + void swap(stop_source& other) noexcept { + m_stop_state.swap(other.m_stop_state); + } + + [[nodiscard]] stop_token get_token() const noexcept { + return stop_token(m_stop_state); + } + [[nodiscard]] bool stop_possible() const noexcept { + return m_stop_state != nullptr; + } + [[nodiscard]] bool stop_requested() const noexcept { + return m_stop_state && m_stop_state->stop_requested(); + } + bool request_stop() noexcept { + return m_stop_state && m_stop_state->request_stop(); + } + +private: + friend class jthread; + explicit stop_source(shared_ptr stop_state) + : m_stop_state(move(stop_state)) {} + +private: + shared_ptr m_stop_state; +}; + +template +class stop_callback { + static_assert(is_nothrow_destructible_v); + static_assert(is_invocable_v); + +public: + using callback_type = Callback; + + template + requires constructible_from + explicit stop_callback(const stop_token& st, + C&& cb) noexcept(is_nothrow_constructible_v) + : m_stop_state(st.m_stop_state) { + if (m_stop_state) { + m_callback = m_stop_state->insert_callback(move(cb)); + } + } + template + requires constructible_from + explicit stop_callback(stop_token&& st, + C&& cb) noexcept(is_nothrow_constructible_v) + : m_stop_state(move(st.m_stop_state)) { + if (m_stop_state) { + m_callback = m_stop_state->insert_callback(move(cb)); + } + } + ~stop_callback() { + if (m_stop_state && m_callback) { + m_stop_state->remove_callback(*m_callback); + } + } + + stop_callback(const stop_callback&) = delete; + stop_callback(stop_callback&&) = delete; + stop_callback& operator=(const stop_callback&) = delete; + stop_callback& operator=(stop_callback&&) = delete; + +private: + shared_ptr m_stop_state; + optional m_callback; +}; + +template +stop_callback(stop_token, Callback) -> stop_callback; + +class jthread { +public: + using id = thread::id; + using native_handle_type = thread::native_handle_type; + + jthread() noexcept = default; + + template , jthread>>> + explicit jthread(F&& f, Args&&... args) + : m_stop_state(make_shared()), + m_thread(make_thread(move(f), move(args)...)) {} + + ~jthread() { + if (joinable()) { + request_stop(); + join(); + } + } + + jthread(const jthread&) = delete; + jthread(jthread&&) noexcept = default; + jthread& operator=(const jthread&) = delete; + + jthread& operator=(jthread&& other) noexcept { + m_thread.swap(other.m_thread); + m_stop_state.swap(other.m_stop_state); + return *this; + } + + void swap(jthread& other) noexcept { + m_thread.swap(other.m_thread); + m_stop_state.swap(other.m_stop_state); + } + [[nodiscard]] bool joinable() const noexcept { + return m_thread.joinable(); + } + void join() { + m_thread.join(); + } + void detach() { + m_thread.detach(); + m_stop_state.reset(); + } + + [[nodiscard]] id get_id() const noexcept { + return m_thread.get_id(); + } + [[nodiscard]] native_handle_type native_handle() { + return m_thread.native_handle(); + } + [[nodiscard]] stop_source get_stop_source() noexcept { + return stop_source(m_stop_state); + } + [[nodiscard]] stop_token get_stop_token() const noexcept { + return stop_source(m_stop_state).get_token(); + } + bool request_stop() noexcept { + return get_stop_source().request_stop(); + } + [[nodiscard]] static unsigned int hardware_concurrency() noexcept { + return thread::hardware_concurrency(); + } + +private: + template + thread make_thread(F&& f, Args&&... args) { + if constexpr (is_invocable_v, stop_token, decay_t...>) { + return thread(move(f), get_stop_token(), move(args)...); + } else { + return thread(move(f), move(args)...); + } + } + + shared_ptr m_stop_state; + thread m_thread; +}; + +} // namespace std + +namespace Common { + +template +void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) { + if (token.stop_requested()) { + return; + } + + std::stop_callback callback(token, [&] { cv.notify_all(); }); + cv.wait(lock, [&] { return pred() || token.stop_requested(); }); +} + +} // namespace Common + +#endif diff --git a/src/common/precompiled_headers.h b/src/common/precompiled_headers.h new file mode 100644 index 0000000..aabae73 --- /dev/null +++ b/src/common/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 0a560eb..149e621 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -40,6 +40,7 @@ void LogSettings() { LOG_INFO(Config, "yuzu Configuration:"); log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); + log_setting("System_DeviceName", values.device_name.GetValue()); log_setting("System_CurrentUser", values.current_user.GetValue()); log_setting("System_LanguageIndex", values.language_index.GetValue()); log_setting("System_RegionIndex", values.region_index.GetValue()); @@ -48,6 +49,7 @@ void LogSettings() { log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue()); log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue()); + log_setting("Renderer_FSRSlider", values.fsr_sharpening_slider.GetValue()); log_setting("Renderer_AntiAliasing", values.anti_aliasing.GetValue()); log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue()); log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue()); @@ -151,6 +153,7 @@ void UpdateRescalingInfo() { ASSERT(false); info.up_scale = 1; info.down_shift = 0; + break; } info.up_factor = static_cast(info.up_scale) / (1U << info.down_shift); info.down_factor = static_cast(1U << info.down_shift) / info.up_scale; @@ -180,6 +183,7 @@ void RestoreGlobalState(bool is_powered_on) { values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true); // Renderer + values.fsr_sharpening_slider.SetGlobal(true); values.renderer_backend.SetGlobal(true); values.vulkan_device.SetGlobal(true); values.aspect_ratio.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index 851812f..6b199af 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -19,6 +19,7 @@ namespace Settings { enum class RendererBackend : u32 { OpenGL = 0, Vulkan = 1, + Null = 2, }; enum class ShaderBackend : u32 { @@ -75,7 +76,8 @@ enum class ScalingFilter : u32 { enum class AntiAliasing : u32 { None = 0, Fxaa = 1, - LastAA = Fxaa, + Smaa = 2, + LastAA = Smaa, }; struct ResolutionScalingInfo { @@ -399,6 +401,7 @@ struct Values { Setting cpuopt_fastmem{true, "cpuopt_fastmem"}; Setting cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; Setting cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; + Setting cpuopt_ignore_memory_aborts{true, "cpuopt_ignore_memory_aborts"}; SwitchableSetting cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; SwitchableSetting cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; @@ -411,7 +414,7 @@ struct Values { // Renderer SwitchableSetting renderer_backend{ - RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; + RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"}; Setting renderer_debug{false, "debug"}; Setting renderer_shader_feedback{false, "shader_feedback"}; Setting enable_nsight_aftermath{false, "nsight_aftermath"}; @@ -421,6 +424,7 @@ struct Values { ResolutionScalingInfo resolution_info{}; SwitchableSetting resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; SwitchableSetting scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; + SwitchableSetting fsr_sharpening_slider{25, 0, 200, "fsr_sharpening_slider"}; SwitchableSetting anti_aliasing{AntiAliasing::None, "anti_aliasing"}; // *nix platforms may have issues with the borderless windowed fullscreen mode. // Default to exclusive fullscreen on these platforms for now. @@ -431,7 +435,7 @@ struct Values { FullscreenMode::Exclusive, #endif FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; - SwitchableSetting aspect_ratio{0, 0, 3, "aspect_ratio"}; + SwitchableSetting aspect_ratio{0, 0, 4, "aspect_ratio"}; SwitchableSetting max_anisotropy{0, 0, 5, "max_anisotropy"}; SwitchableSetting use_speed_limit{true, "use_speed_limit"}; SwitchableSetting speed_limit{100, 0, 9999, "speed_limit"}; @@ -442,7 +446,7 @@ struct Values { SwitchableSetting nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; SwitchableSetting accelerate_astc{true, "accelerate_astc"}; SwitchableSetting use_vsync{true, "use_vsync"}; - SwitchableSetting shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, + SwitchableSetting shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL, ShaderBackend::SPIRV, "shader_backend"}; SwitchableSetting use_asynchronous_shaders{false, "use_asynchronous_shaders"}; SwitchableSetting use_fast_gpu_time{true, "use_fast_gpu_time"}; @@ -454,6 +458,7 @@ struct Values { // System SwitchableSetting> rng_seed{std::optional(), "rng_seed"}; + Setting device_name{"Yuzu", "device_name"}; // Measured in seconds since epoch std::optional custom_rtc; // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` @@ -531,6 +536,7 @@ struct Values { Setting use_auto_stub{false, "use_auto_stub"}; Setting enable_all_controllers{false, "enable_all_controllers"}; Setting create_crash_dumps{false, "create_crash_dumps"}; + Setting perform_vulkan_check{true, "perform_vulkan_check"}; // Miscellaneous Setting log_filter{"*:Info", "log_filter"}; diff --git a/src/common/settings_input.h b/src/common/settings_input.h index 485e4ad..46f38c7 100644 --- a/src/common/settings_input.h +++ b/src/common/settings_input.h @@ -391,6 +391,7 @@ struct PlayerInput { u32 body_color_right; u32 button_color_left; u32 button_color_right; + std::string profile_name; }; struct TouchscreenInput { diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 7a495bc..b26db47 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -141,7 +141,7 @@ static std::wstring CPToUTF16(u32 code_page, const std::string& input) { MultiByteToWideChar(code_page, 0, input.data(), static_cast(input.size()), nullptr, 0); if (size == 0) { - return L""; + return {}; } std::wstring output(size, L'\0'); @@ -158,7 +158,7 @@ std::string UTF16ToUTF8(const std::wstring& input) { const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast(input.size()), nullptr, 0, nullptr, nullptr); if (size == 0) { - return ""; + return {}; } std::string output(size, '\0'); diff --git a/src/common/thread.h b/src/common/thread.h index e17a785..8ae169b 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -11,6 +11,7 @@ #include #include #include "common/common_types.h" +#include "common/polyfill_thread.h" namespace Common { @@ -69,7 +70,7 @@ public: explicit Barrier(std::size_t count_) : count(count_) {} /// Blocks until all "count" threads have called Sync() - void Sync() { + bool Sync(std::stop_token token = {}) { std::unique_lock lk{mutex}; const std::size_t current_generation = generation; @@ -77,14 +78,16 @@ public: generation++; waiting = 0; condvar.notify_all(); + return true; } else { - condvar.wait(lk, - [this, current_generation] { return current_generation != generation; }); + CondvarWait(condvar, lk, token, + [this, current_generation] { return current_generation != generation; }); + return !token.stop_requested(); } } private: - std::condition_variable condvar; + std::condition_variable_any condvar; std::mutex mutex; std::size_t count; std::size_t waiting = 0; diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h index 62c60f7..260ad44 100644 --- a/src/common/thread_worker.h +++ b/src/common/thread_worker.h @@ -7,13 +7,13 @@ #include #include #include -#include #include #include #include #include #include +#include "common/polyfill_thread.h" #include "common/thread.h" #include "common/unique_function.h" @@ -47,7 +47,8 @@ public: if (requests.empty()) { wait_condition.notify_all(); } - condition.wait(lock, stop_token, [this] { return !requests.empty(); }); + Common::CondvarWait(condition, lock, stop_token, + [this] { return !requests.empty(); }); if (stop_token.stop_requested()) { break; } diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index 053798e..2ef1da0 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -12,6 +12,8 @@ #include #include +#include "common/polyfill_thread.h" + namespace Common { template class SPSCQueue { @@ -97,7 +99,7 @@ public: T PopWait(std::stop_token stop_token) { if (Empty()) { std::unique_lock lock{cv_mutex}; - cv.wait(lock, stop_token, [this] { return !Empty(); }); + Common::CondvarWait(cv, lock, stop_token, [this] { return !Empty(); }); } if (stop_token.stop_requested()) { return T{}; diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index 1a27532..e54383a 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -4,14 +4,27 @@ #include #include +#include #include +#include #include +#include +#include #include "common/bit_util.h" #include "common/common_types.h" +#include "common/logging/log.h" #include "common/x64/cpu_detect.h" +#ifdef _WIN32 +#include +#endif + #ifdef _MSC_VER #include + +static inline u64 xgetbv(u32 index) { + return _xgetbv(index); +} #else #if defined(__DragonFly__) || defined(__FreeBSD__) @@ -39,12 +52,11 @@ static inline void __cpuid(int info[4], u32 function_id) { } #define _XCR_XFEATURE_ENABLED_MASK 0 -static inline u64 _xgetbv(u32 index) { +static inline u64 xgetbv(u32 index) { u32 eax, edx; __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index)); return ((u64)edx << 32) | eax; } - #endif // _MSC_VER namespace Common { @@ -107,7 +119,7 @@ static CPUCaps Detect() { // - Is the XSAVE bit set in CPUID? // - XGETBV result has the XCR bit set. if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) { - if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { + if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { caps.avx = true; if (Common::Bit<12>(cpu_id[2])) caps.fma = true; @@ -192,4 +204,45 @@ const CPUCaps& GetCPUCaps() { return caps; } +std::optional GetProcessorCount() { +#if defined(_WIN32) + // Get the buffer length. + DWORD length = 0; + GetLogicalProcessorInformation(nullptr, &length); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + LOG_ERROR(Frontend, "Failed to query core count."); + return std::nullopt; + } + std::vector buffer( + length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)); + // Now query the core count. + if (!GetLogicalProcessorInformation(buffer.data(), &length)) { + LOG_ERROR(Frontend, "Failed to query core count."); + return std::nullopt; + } + return static_cast( + std::count_if(buffer.cbegin(), buffer.cend(), [](const auto& proc_info) { + return proc_info.Relationship == RelationProcessorCore; + })); +#elif defined(__unix__) + const int thread_count = std::thread::hardware_concurrency(); + std::ifstream smt("/sys/devices/system/cpu/smt/active"); + char state = '0'; + if (smt) { + smt.read(&state, sizeof(state)); + } + switch (state) { + case '0': + return thread_count; + case '1': + return thread_count / 2; + default: + return std::nullopt; + } +#else + // Shame on you + return std::nullopt; +#endif +} + } // namespace Common diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 6830f37..ca8db19 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "common/common_types.h" @@ -74,4 +75,7 @@ struct CPUCaps { */ const CPUCaps& GetCPUCaps(); +/// Detects CPU core count +std::optional GetProcessorCount(); + } // namespace Common diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e70797a..c6b5ac1 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -120,6 +120,8 @@ add_library(core STATIC file_sys/vfs_vector.h file_sys/xts_archive.cpp file_sys/xts_archive.h + frontend/applets/cabinet.cpp + frontend/applets/cabinet.h frontend/applets/controller.cpp frontend/applets/controller.h frontend/applets/error.cpp @@ -138,8 +140,6 @@ add_library(core STATIC frontend/emu_window.h frontend/framebuffer_layout.cpp frontend/framebuffer_layout.h - hardware_interrupt_manager.cpp - hardware_interrupt_manager.h hid/emulated_console.cpp hid/emulated_console.h hid/emulated_controller.cpp @@ -192,8 +192,13 @@ add_library(core STATIC hle/kernel/k_code_memory.h hle/kernel/k_condition_variable.cpp hle/kernel/k_condition_variable.h + hle/kernel/k_debug.h + hle/kernel/k_dynamic_page_manager.h + hle/kernel/k_dynamic_resource_manager.h + hle/kernel/k_dynamic_slab_heap.h hle/kernel/k_event.cpp hle/kernel/k_event.h + hle/kernel/k_event_info.h hle/kernel/k_handle_table.cpp hle/kernel/k_handle_table.h hle/kernel/k_interrupt_manager.cpp @@ -221,6 +226,8 @@ add_library(core STATIC hle/kernel/k_page_group.h hle/kernel/k_page_table.cpp hle/kernel/k_page_table.h + hle/kernel/k_page_table_manager.h + hle/kernel/k_page_table_slab_heap.h hle/kernel/k_port.cpp hle/kernel/k_port.h hle/kernel/k_priority_queue.h @@ -242,6 +249,8 @@ add_library(core STATIC hle/kernel/k_server_session.h hle/kernel/k_session.cpp hle/kernel/k_session.h + hle/kernel/k_session_request.cpp + hle/kernel/k_session_request.h hle/kernel/k_shared_memory.cpp hle/kernel/k_shared_memory.h hle/kernel/k_shared_memory_info.h @@ -251,6 +260,8 @@ add_library(core STATIC hle/kernel/k_synchronization_object.cpp hle/kernel/k_synchronization_object.h hle/kernel/k_system_control.h + hle/kernel/k_system_resource.cpp + hle/kernel/k_system_resource.h hle/kernel/k_thread.cpp hle/kernel/k_thread.h hle/kernel/k_thread_local_page.cpp @@ -263,8 +274,6 @@ add_library(core STATIC hle/kernel/k_worker_task.h hle/kernel/k_worker_task_manager.cpp hle/kernel/k_worker_task_manager.h - hle/kernel/k_writable_event.cpp - hle/kernel/k_writable_event.h hle/kernel/kernel.cpp hle/kernel/kernel.h hle/kernel/memory_types.h @@ -305,6 +314,8 @@ add_library(core STATIC hle/service/am/applet_ae.h hle/service/am/applet_oe.cpp hle/service/am/applet_oe.h + hle/service/am/applets/applet_cabinet.cpp + hle/service/am/applets/applet_cabinet.h hle/service/am/applets/applet_controller.cpp hle/service/am/applets/applet_controller.h hle/service/am/applets/applet_error.cpp @@ -460,6 +471,8 @@ add_library(core STATIC hle/service/hid/controllers/mouse.h hle/service/hid/controllers/npad.cpp hle/service/hid/controllers/npad.h + hle/service/hid/controllers/palma.cpp + hle/service/hid/controllers/palma.h hle/service/hid/controllers/stubbed.cpp hle/service/hid/controllers/stubbed.h hle/service/hid/controllers/touchscreen.cpp @@ -488,10 +501,6 @@ add_library(core STATIC hle/service/hid/irsensor/processor_base.h hle/service/hid/irsensor/tera_plugin_processor.cpp hle/service/hid/irsensor/tera_plugin_processor.h - hle/service/jit/jit_context.cpp - hle/service/jit/jit_context.h - hle/service/jit/jit.cpp - hle/service/jit/jit.h hle/service/lbl/lbl.cpp hle/service/lbl/lbl.h hle/service/ldn/lan_discovery.cpp @@ -519,13 +528,23 @@ add_library(core STATIC hle/service/mnpp/mnpp_app.h hle/service/ncm/ncm.cpp hle/service/ncm/ncm.h + hle/service/nfc/mifare_user.cpp + hle/service/nfc/mifare_user.h hle/service/nfc/nfc.cpp hle/service/nfc/nfc.h + hle/service/nfc/nfc_device.cpp + hle/service/nfc/nfc_device.h + hle/service/nfc/nfc_result.h + hle/service/nfc/nfc_user.cpp + hle/service/nfc/nfc_user.h hle/service/nfp/amiibo_crypto.cpp hle/service/nfp/amiibo_crypto.h - hle/service/nfp/amiibo_types.h hle/service/nfp/nfp.cpp hle/service/nfp/nfp.h + hle/service/nfp/nfp_device.cpp + hle/service/nfp/nfp_device.h + hle/service/nfp/nfp_result.h + hle/service/nfp/nfp_types.h hle/service/nfp/nfp_user.cpp hle/service/nfp/nfp_user.h hle/service/ngct/ngct.cpp @@ -545,6 +564,12 @@ add_library(core STATIC hle/service/ns/ns.h hle/service/ns/pdm_qry.cpp hle/service/ns/pdm_qry.h + hle/service/nvdrv/core/container.cpp + hle/service/nvdrv/core/container.h + hle/service/nvdrv/core/nvmap.cpp + hle/service/nvdrv/core/nvmap.h + hle/service/nvdrv/core/syncpoint_manager.cpp + hle/service/nvdrv/core/syncpoint_manager.h hle/service/nvdrv/devices/nvdevice.h hle/service/nvdrv/devices/nvdisp_disp0.cpp hle/service/nvdrv/devices/nvdisp_disp0.h @@ -573,8 +598,6 @@ add_library(core STATIC hle/service/nvdrv/nvdrv_interface.h hle/service/nvdrv/nvmemp.cpp hle/service/nvdrv/nvmemp.h - hle/service/nvdrv/syncpoint_manager.cpp - hle/service/nvdrv/syncpoint_manager.h hle/service/nvflinger/binder.h hle/service/nvflinger/buffer_item.h hle/service/nvflinger/buffer_item_consumer.cpp @@ -750,6 +773,7 @@ add_library(core STATIC memory.h perf_stats.cpp perf_stats.h + precompiled_headers.h reporter.cpp reporter.h telemetry_session.cpp @@ -764,19 +788,15 @@ if (MSVC) /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) else() target_compile_options(core PRIVATE -Werror=conversion - -Werror=ignored-qualifiers - - $<$:-Werror=class-memaccess> - $<$:-Werror=unused-but-set-parameter> - $<$:-Werror=unused-but-set-variable> - - $<$:-fsized-deallocation> -Wno-sign-conversion + + $<$:-fsized-deallocation> ) endif() @@ -793,14 +813,22 @@ if (ENABLE_WEB_SERVICE) target_link_libraries(core PRIVATE web_service) endif() -if (ARCHITECTURE_x86_64) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) target_sources(core PRIVATE - arm/dynarmic/arm_dynarmic_32.cpp - arm/dynarmic/arm_dynarmic_32.h arm/dynarmic/arm_dynarmic_64.cpp arm/dynarmic/arm_dynarmic_64.h + arm/dynarmic/arm_dynarmic_32.cpp + arm/dynarmic/arm_dynarmic_32.h arm/dynarmic/arm_dynarmic_cp15.cpp arm/dynarmic/arm_dynarmic_cp15.h + hle/service/jit/jit_context.cpp + hle/service/jit/jit_context.h + hle/service/jit/jit.cpp + hle/service/jit/jit.h ) - target_link_libraries(core PRIVATE dynarmic) + target_link_libraries(core PRIVATE dynarmic::dynarmic) +endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(core PRIVATE precompiled_headers.h) endif() diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 953d964..2df7b0e 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -134,14 +134,26 @@ void ARM_Interface::Run() { } system.ExitDynarmicProfile(); + // If the thread is scheduled for termination, exit the thread. + if (current_thread->HasDpc()) { + if (current_thread->IsTerminationRequested()) { + current_thread->Exit(); + UNREACHABLE(); + } + } + // Notify the debugger and go to sleep if a breakpoint was hit, // or if the thread is unable to continue for any reason. if (Has(hr, breakpoint) || Has(hr, no_execute)) { - RewindBreakpointInstruction(); + if (!Has(hr, no_execute)) { + RewindBreakpointInstruction(); + } if (system.DebuggerEnabled()) { system.GetDebugger().NotifyThreadStopped(current_thread); + } else { + LogBacktrace(); } - current_thread->RequestSuspend(Kernel::SuspendType::Debug); + current_thread->RequestSuspend(SuspendType::Debug); break; } diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index d1e70f1..947747d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -29,7 +29,9 @@ class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { public: explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) : parent{parent_}, - memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {} + memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()}, + check_memory_access{debugger_enabled || + !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} u8 MemoryRead8(u32 vaddr) override { CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); @@ -154,6 +156,17 @@ public: } bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) { + if (!check_memory_access) { + return true; + } + + if (!memory.IsValidVirtualAddressRange(addr, size)) { + LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}", + addr); + parent.jit.load()->HaltExecution(ARM_Interface::no_execute); + return false; + } + if (!debugger_enabled) { return true; } @@ -181,7 +194,8 @@ public: ARM_Dynarmic_32& parent; Core::Memory::Memory& memory; std::size_t num_interpreted_instructions{}; - bool debugger_enabled{}; + const bool debugger_enabled{}; + const bool check_memory_access{}; static constexpr u64 minimum_run_cycles = 10000U; }; @@ -264,6 +278,9 @@ std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable* if (!Settings::values.cpuopt_recompile_exclusives) { config.recompile_on_exclusive_fastmem_failure = false; } + if (!Settings::values.cpuopt_ignore_memory_aborts) { + config.check_halt_on_memory_access = true; + } } else { // Unsafe optimizations if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { @@ -301,6 +318,11 @@ std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable* } } +#ifdef ARCHITECTURE_arm64 + // TODO: remove when fixed in dynarmic + config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking; +#endif + return std::make_unique(config); } @@ -450,7 +472,7 @@ std::vector ARM_Dynarmic_32::GetBacktrace(Core::S // Frame records are two words long: // fp+0 : pointer to previous frame record // fp+4 : value of lr for frame - while (true) { + for (size_t i = 0; i < 256; i++) { out.push_back({"", 0, lr, 0, ""}); if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { break; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 1d46f6d..3df943d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -29,7 +29,9 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { public: explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) : parent{parent_}, - memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {} + memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()}, + check_memory_access{debugger_enabled || + !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} u8 MemoryRead8(u64 vaddr) override { CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); @@ -111,6 +113,7 @@ public: LOG_ERROR(Core_ARM, "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, num_instructions, memory.Read32(pc)); + ReturnException(pc, ARM_Interface::no_execute); } void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, @@ -197,6 +200,17 @@ public: } bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) { + if (!check_memory_access) { + return true; + } + + if (!memory.IsValidVirtualAddressRange(addr, size)) { + LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}", + addr); + parent.jit.load()->HaltExecution(ARM_Interface::no_execute); + return false; + } + if (!debugger_enabled) { return true; } @@ -225,7 +239,8 @@ public: Core::Memory::Memory& memory; u64 tpidrro_el0 = 0; u64 tpidr_el0 = 0; - bool debugger_enabled{}; + const bool debugger_enabled{}; + const bool check_memory_access{}; static constexpr u64 minimum_run_cycles = 10000U; }; @@ -322,6 +337,9 @@ std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable* if (!Settings::values.cpuopt_recompile_exclusives) { config.recompile_on_exclusive_fastmem_failure = false; } + if (!Settings::values.cpuopt_ignore_memory_aborts) { + config.check_halt_on_memory_access = true; + } } else { // Unsafe optimizations if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { @@ -347,7 +365,6 @@ std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable* if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { config.unsafe_optimizations = true; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; config.fastmem_address_space_bits = 64; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; } @@ -516,7 +533,7 @@ std::vector ARM_Dynarmic_64::GetBacktrace(Core::S // Frame records are two words long: // fp+0 : pointer to previous frame record // fp+8 : value of lr for frame - while (true) { + for (size_t i = 0; i < 256; i++) { out.push_back({"", 0, lr, 0, ""}); if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { break; diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index 200efe4..5a4eba3 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp @@ -52,12 +52,16 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1 case 4: // CP15_DATA_SYNC_BARRIER return Callback{ - [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { -#ifdef _MSC_VER + [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t { +#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64) _mm_mfence(); _mm_lfence(); -#else +#elif defined(ARCHITECTURE_x86_64) asm volatile("mfence\n\tlfence\n\t" : : : "memory"); +#elif defined(ARCHITECTURE_arm64) + asm volatile("dsb sy\n\t" : : : "memory"); +#else +#error Unsupported architecture #endif return 0; }, @@ -66,11 +70,15 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1 case 5: // CP15_DATA_MEMORY_BARRIER return Callback{ - [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { -#ifdef _MSC_VER + [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t { +#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64) _mm_mfence(); -#else +#elif defined(ARCHITECTURE_x86_64) asm volatile("mfence\n\t" : : : "memory"); +#elif defined(ARCHITECTURE_arm64) + asm volatile("dmb sy\n\t" : : : "memory"); +#else +#error Unsupported architecture #endif return 0; }, @@ -115,7 +123,7 @@ CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1, CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) { if (!two && opc == 0 && CRm == CoprocReg::C14) { // CNTPCT - const auto callback = [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 { + const auto callback = [](void* arg, u32, u32) -> u64 { const auto& parent_arg = *static_cast(arg); return parent_arg.system.CoreTiming().GetClockTicks(); }; diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index 2db0b03..20550fa 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) #include "core/arm/dynarmic/arm_exclusive_monitor.h" #endif #include "core/arm/exclusive_monitor.h" @@ -13,7 +13,7 @@ ExclusiveMonitor::~ExclusiveMonitor() = default; std::unique_ptr MakeExclusiveMonitor(Memory::Memory& memory, std::size_t num_cores) { -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) return std::make_unique(memory, num_cores); #else // TODO(merry): Passthrough exclusive monitor diff --git a/src/core/core.cpp b/src/core/core.cpp index 1210928..a738f22 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -27,7 +27,6 @@ #include "core/file_sys/savedata_factory.h" #include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_real.h" -#include "core/hardware_interrupt_manager.h" #include "core/hid/hid_core.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_process.h" @@ -51,6 +50,7 @@ #include "core/telemetry_session.h" #include "core/tools/freezer.h" #include "network/network.h" +#include "video_core/host1x/host1x.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -133,13 +133,63 @@ struct System::Impl { : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} + void Initialize(System& system) { + device_memory = std::make_unique(); + + is_multicore = Settings::values.use_multi_core.GetValue(); + extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); + + core_timing.SetMulticore(is_multicore); + core_timing.Initialize([&system]() { system.RegisterHostThread(); }); + + const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); + const auto current_time = + std::chrono::duration_cast(posix_time).count(); + Settings::values.custom_rtc_differential = + Settings::values.custom_rtc.value_or(current_time) - current_time; + + // Create a default fs if one doesn't already exist. + if (virtual_filesystem == nullptr) { + virtual_filesystem = std::make_shared(); + } + if (content_provider == nullptr) { + content_provider = std::make_unique(); + } + + // Create default implementations of applets if one is not provided. + applet_manager.SetDefaultAppletsIfMissing(); + + is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); + + kernel.SetMulticore(is_multicore); + cpu_manager.SetMulticore(is_multicore); + cpu_manager.SetAsyncGpu(is_async_gpu); + } + + void ReinitializeIfNecessary(System& system) { + const bool must_reinitialize = + is_multicore != Settings::values.use_multi_core.GetValue() || + extended_memory_layout != Settings::values.use_extended_memory_layout.GetValue(); + + if (!must_reinitialize) { + return; + } + + LOG_DEBUG(Kernel, "Re-initializing"); + + is_multicore = Settings::values.use_multi_core.GetValue(); + extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); + + Initialize(system); + } + SystemResultStatus Run() { std::unique_lock lk(suspend_guard); status = SystemResultStatus::Success; kernel.Suspend(false); core_timing.SyncPause(false); - is_paused = false; + is_paused.store(false, std::memory_order_relaxed); return status; } @@ -150,14 +200,13 @@ struct System::Impl { core_timing.SyncPause(true); kernel.Suspend(true); - is_paused = true; + is_paused.store(true, std::memory_order_relaxed); return status; } bool IsPaused() const { - std::unique_lock lk(suspend_guard); - return is_paused; + return is_paused.load(std::memory_order_relaxed); } std::unique_lock StallProcesses() { @@ -168,7 +217,7 @@ struct System::Impl { } void UnstallProcesses() { - if (!is_paused) { + if (!IsPaused()) { core_timing.SyncPause(false); kernel.Suspend(false); } @@ -178,43 +227,21 @@ struct System::Impl { debugger = std::make_unique(system, port); } - SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { + SystemResultStatus SetupForMainProcess(System& system, Frontend::EmuWindow& emu_window) { LOG_DEBUG(Core, "initialized OK"); - device_memory = std::make_unique(); - - is_multicore = Settings::values.use_multi_core.GetValue(); - is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); - - kernel.SetMulticore(is_multicore); - cpu_manager.SetMulticore(is_multicore); - cpu_manager.SetAsyncGpu(is_async_gpu); - core_timing.SetMulticore(is_multicore); + // Setting changes may require a full system reinitialization (e.g., disabling multicore). + ReinitializeIfNecessary(system); kernel.Initialize(); cpu_manager.Initialize(); - core_timing.Initialize([&system]() { system.RegisterHostThread(); }); - - const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); - const auto current_time = - std::chrono::duration_cast(posix_time).count(); - Settings::values.custom_rtc_differential = - Settings::values.custom_rtc.value_or(current_time) - current_time; - - // Create a default fs if one doesn't already exist. - if (virtual_filesystem == nullptr) - virtual_filesystem = std::make_shared(); - if (content_provider == nullptr) - content_provider = std::make_unique(); - - /// Create default implementations of applets if one is not provided. - applet_manager.SetDefaultAppletsIfMissing(); /// Reset all glue registrations arp_manager.ResetAll(); telemetry_session = std::make_unique(); + host1x_core = std::make_unique(system); gpu_core = VideoCore::CreateGPU(emu_window, system); if (!gpu_core) { return SystemResultStatus::ErrorVideoCore; @@ -224,7 +251,6 @@ struct System::Impl { service_manager = std::make_shared(kernel); services = std::make_unique(service_manager, system); - interrupt_manager = std::make_unique(system); // Initialize time manager, which must happen after kernel is created time_manager.Initialize(); @@ -253,11 +279,11 @@ struct System::Impl { return SystemResultStatus::ErrorGetLoader; } - SystemResultStatus init_result{Init(system, emu_window)}; + SystemResultStatus init_result{SetupForMainProcess(system, emu_window)}; if (init_result != SystemResultStatus::Success) { LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", static_cast(init_result)); - Shutdown(); + ShutdownMainProcess(); return init_result; } @@ -276,7 +302,7 @@ struct System::Impl { const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); if (load_result != Loader::ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result); - Shutdown(); + ShutdownMainProcess(); return static_cast( static_cast(SystemResultStatus::ErrorLoader) + static_cast(load_result)); @@ -335,7 +361,7 @@ struct System::Impl { return status; } - void Shutdown() { + void ShutdownMainProcess() { SetShuttingDown(true); // Log last frame performance stats if game was loded @@ -363,20 +389,23 @@ struct System::Impl { kernel.ShutdownCores(); cpu_manager.Shutdown(); debugger.reset(); + if (services) { + services->KillNVNFlinger(); + } kernel.CloseServices(); services.reset(); service_manager.reset(); cheat_engine.reset(); telemetry_session.reset(); time_manager.Shutdown(); - core_timing.Shutdown(); + core_timing.ClearPendingEvents(); app_loader.reset(); audio_core.reset(); gpu_core.reset(); + host1x_core.reset(); perf_stats.reset(); kernel.Shutdown(); memory.Reset(); - applet_manager.ClearAll(); if (auto room_member = room_network.GetRoomMember().lock()) { Network::GameInfo game_info{}; @@ -437,7 +466,7 @@ struct System::Impl { } mutable std::mutex suspend_guard; - bool is_paused{}; + std::atomic_bool is_paused{}; std::atomic is_shutting_down{}; Timing::CoreTiming core_timing; @@ -450,7 +479,7 @@ struct System::Impl { /// AppLoader used to load the current executing application std::unique_ptr app_loader; std::unique_ptr gpu_core; - std::unique_ptr interrupt_manager; + std::unique_ptr host1x_core; std::unique_ptr device_memory; std::unique_ptr audio_core; Core::Memory::Memory memory; @@ -499,6 +528,7 @@ struct System::Impl { bool is_multicore{}; bool is_async_gpu{}; + bool extended_memory_layout{}; ExecuteProgramCallback execute_program_callback; ExitCallback exit_callback; @@ -519,6 +549,10 @@ const CpuManager& System::GetCpuManager() const { return impl->cpu_manager; } +void System::Initialize() { + impl->Initialize(*this); +} + SystemResultStatus System::Run() { return impl->Run(); } @@ -539,8 +573,8 @@ void System::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) { impl->kernel.InvalidateCpuInstructionCacheRange(addr, size); } -void System::Shutdown() { - impl->Shutdown(); +void System::ShutdownMainProcess() { + impl->ShutdownMainProcess(); } bool System::IsShuttingDown() const { @@ -668,12 +702,12 @@ const Tegra::GPU& System::GPU() const { return *impl->gpu_core; } -Core::Hardware::InterruptManager& System::InterruptManager() { - return *impl->interrupt_manager; +Tegra::Host1x::Host1x& System::Host1x() { + return *impl->host1x_core; } -const Core::Hardware::InterruptManager& System::InterruptManager() const { - return *impl->interrupt_manager; +const Tegra::Host1x::Host1x& System::Host1x() const { + return *impl->host1x_core; } VideoCore::RendererBase& System::Renderer() { diff --git a/src/core/core.h b/src/core/core.h index 0ce3b1d..4ebedff 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -74,6 +74,9 @@ class TimeManager; namespace Tegra { class DebugContext; class GPU; +namespace Host1x { +class Host1x; +} // namespace Host1x } // namespace Tegra namespace VideoCore { @@ -88,10 +91,6 @@ namespace Core::Timing { class CoreTiming; } -namespace Core::Hardware { -class InterruptManager; -} - namespace Core::HID { class HIDCore; } @@ -143,6 +142,12 @@ public: System(System&&) = delete; System& operator=(System&&) = delete; + /** + * Initializes the system + * This function will initialize core functionaility used for system emulation + */ + void Initialize(); + /** * Run the OS and Application * This function will start emulation and run the relevant devices @@ -167,8 +172,8 @@ public: void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size); - /// Shutdown the emulated system. - void Shutdown(); + /// Shutdown the main emulated process. + void ShutdownMainProcess(); /// Check if the core is shutting down. [[nodiscard]] bool IsShuttingDown() const; @@ -260,6 +265,12 @@ public: /// Gets an immutable reference to the GPU interface. [[nodiscard]] const Tegra::GPU& GPU() const; + /// Gets a mutable reference to the Host1x interface + [[nodiscard]] Tegra::Host1x::Host1x& Host1x(); + + /// Gets an immutable reference to the Host1x interface. + [[nodiscard]] const Tegra::Host1x::Host1x& Host1x() const; + /// Gets a mutable reference to the renderer. [[nodiscard]] VideoCore::RendererBase& Renderer(); @@ -296,12 +307,6 @@ public: /// Provides a constant reference to the core timing instance. [[nodiscard]] const Timing::CoreTiming& CoreTiming() const; - /// Provides a reference to the interrupt manager instance. - [[nodiscard]] Core::Hardware::InterruptManager& InterruptManager(); - - /// Provides a constant reference to the interrupt manager instance. - [[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const; - /// Provides a reference to the kernel instance. [[nodiscard]] Kernel::KernelCore& Kernel(); diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index f6c4567..0e7b5f9 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -40,10 +40,12 @@ struct CoreTiming::Event { CoreTiming::CoreTiming() : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} -CoreTiming::~CoreTiming() = default; +CoreTiming::~CoreTiming() { + Reset(); +} void CoreTiming::ThreadEntry(CoreTiming& instance) { - constexpr char name[] = "yuzu:HostTiming"; + constexpr char name[] = "HostTiming"; MicroProfileOnThreadCreate(name); Common::SetCurrentThreadName(name); Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); @@ -53,6 +55,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { } void CoreTiming::Initialize(std::function&& on_thread_init_) { + Reset(); on_thread_init = std::move(on_thread_init_); event_fifo_id = 0; shutting_down = false; @@ -65,17 +68,8 @@ void CoreTiming::Initialize(std::function&& on_thread_init_) { } } -void CoreTiming::Shutdown() { - paused = true; - shutting_down = true; - pause_event.Set(); - event.Set(); - if (timer_thread) { - timer_thread->join(); - } - ClearPendingEvents(); - timer_thread.reset(); - has_started = false; +void CoreTiming::ClearPendingEvents() { + event_queue.clear(); } void CoreTiming::Pause(bool is_paused) { @@ -196,10 +190,6 @@ u64 CoreTiming::GetClockTicks() const { return CpuCyclesToClockCycles(ticks); } -void CoreTiming::ClearPendingEvents() { - event_queue.clear(); -} - void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { std::scoped_lock lock{basic_lock}; @@ -270,6 +260,7 @@ void CoreTiming::ThreadLoop() { // There are more events left in the queue, wait until the next event. const auto wait_time = *next_time - GetGlobalTimeNs().count(); if (wait_time > 0) { +#ifdef _WIN32 // Assume a timer resolution of 1ms. static constexpr s64 TimerResolutionNS = 1000000; @@ -287,6 +278,9 @@ void CoreTiming::ThreadLoop() { if (event.IsSet()) { event.Reset(); } +#else + event.WaitFor(std::chrono::nanoseconds(wait_time)); +#endif } } else { // Queue is empty, wait until another event is scheduled and signals us to continue. @@ -303,6 +297,18 @@ void CoreTiming::ThreadLoop() { } } +void CoreTiming::Reset() { + paused = true; + shutting_down = true; + pause_event.Set(); + event.Set(); + if (timer_thread) { + timer_thread->join(); + } + timer_thread.reset(); + has_started = false; +} + std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { if (is_multicore) { return clock->GetTimeNS(); diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 3259397..b592519 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -61,19 +61,14 @@ public: /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. void Initialize(std::function&& on_thread_init_); - /// Tears down all timing related functionality. - void Shutdown(); + /// Clear all pending events. This should ONLY be done on exit. + void ClearPendingEvents(); /// Sets if emulation is multicore or single core, must be set before Initialize void SetMulticore(bool is_multicore_) { is_multicore = is_multicore_; } - /// Check if it's using host timing. - bool IsHostTiming() const { - return is_multicore; - } - /// Pauses/Unpauses the execution of the timer thread. void Pause(bool is_paused); @@ -136,12 +131,11 @@ public: private: struct Event; - /// Clear all pending events. This should ONLY be done on exit. - void ClearPendingEvents(); - static void ThreadEntry(CoreTiming& instance); void ThreadLoop(); + void Reset(); + std::unique_ptr clock; s64 global_timer = 0; diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 9b1565a..04a11f4 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -20,23 +20,20 @@ namespace Core { CpuManager::CpuManager(System& system_) : system{system_} {} CpuManager::~CpuManager() = default; -void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, - std::size_t core) { - cpu_manager.RunThread(core); -} - void CpuManager::Initialize() { num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1; gpu_barrier = std::make_unique(num_cores + 1); for (std::size_t core = 0; core < num_cores; core++) { - core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); + core_data[core].host_thread = + std::jthread([this, core](std::stop_token token) { RunThread(token, core); }); } } void CpuManager::Shutdown() { for (std::size_t core = 0; core < num_cores; core++) { if (core_data[core].host_thread.joinable()) { + core_data[core].host_thread.request_stop(); core_data[core].host_thread.join(); } } @@ -184,14 +181,14 @@ void CpuManager::ShutdownThread() { UNREACHABLE(); } -void CpuManager::RunThread(std::size_t core) { +void CpuManager::RunThread(std::stop_token token, std::size_t core) { /// Initialization system.RegisterCoreThread(core); std::string name; if (is_multicore) { - name = "yuzu:CPUCore_" + std::to_string(core); + name = "CPUCore_" + std::to_string(core); } else { - name = "yuzu:CPUThread"; + name = "CPUThread"; } MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); @@ -206,7 +203,9 @@ void CpuManager::RunThread(std::size_t core) { }); // Running - gpu_barrier->Sync(); + if (!gpu_barrier->Sync(token)) { + return; + } if (!is_async_gpu && !is_multicore) { system.GPU().ObtainContext(); diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 95ea3ef..0deea9c 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -10,6 +10,7 @@ #include #include "common/fiber.h" +#include "common/polyfill_thread.h" #include "common/thread.h" #include "core/hardware_properties.h" @@ -80,12 +81,10 @@ private: void SingleCoreRunGuestThread(); void SingleCoreRunIdleThread(); - static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core); - void GuestActivate(); void HandleInterrupt(); void ShutdownThread(); - void RunThread(std::size_t core); + void RunThread(std::stop_token stop_token, std::size_t core); struct CoreData { std::shared_ptr host_context; diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 4433233..65a9fe8 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -578,18 +578,18 @@ KeyManager::KeyManager() { if (Settings::values.use_dev_keys) { dev_mode = true; - LoadFromFile(yuzu_keys_dir / "dev.keys", false); LoadFromFile(yuzu_keys_dir / "dev.keys_autogenerated", false); + LoadFromFile(yuzu_keys_dir / "dev.keys", false); } else { dev_mode = false; - LoadFromFile(yuzu_keys_dir / "prod.keys", false); LoadFromFile(yuzu_keys_dir / "prod.keys_autogenerated", false); + LoadFromFile(yuzu_keys_dir / "prod.keys", false); } - LoadFromFile(yuzu_keys_dir / "title.keys", true); LoadFromFile(yuzu_keys_dir / "title.keys_autogenerated", true); - LoadFromFile(yuzu_keys_dir / "console.keys", false); + LoadFromFile(yuzu_keys_dir / "title.keys", true); LoadFromFile(yuzu_keys_dir / "console.keys_autogenerated", false); + LoadFromFile(yuzu_keys_dir / "console.keys", false); } static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) { diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index e42bdd1..a9675df 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -9,6 +9,7 @@ #include #include "common/logging/log.h" +#include "common/polyfill_thread.h" #include "common/thread.h" #include "core/core.h" #include "core/debugger/debugger.h" @@ -27,12 +28,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { const u8* buffer_start = reinterpret_cast(&buffer); std::span received_data{buffer_start, buffer_start + bytes_read}; c(received_data); + AsyncReceiveInto(r, buffer, c); } - - AsyncReceiveInto(r, buffer, c); }); } +template +static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) { + acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) { + if (!error.failed()) { + c(peer_socket); + AsyncAccept(acceptor, c); + } + }); +} + template static std::span ReceiveInto(Readable& r, Buffer& buffer) { static_assert(std::is_trivial_v); @@ -59,9 +69,7 @@ namespace Core { class DebuggerImpl : public DebuggerBackend { public: - explicit DebuggerImpl(Core::System& system_, u16 port) - : system{system_}, signal_pipe{io_context}, client_socket{io_context} { - frontend = std::make_unique(*this, system); + explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} { InitializeServer(port); } @@ -70,39 +78,42 @@ public: } bool SignalDebugger(SignalInfo signal_info) { - { - std::scoped_lock lk{connection_lock}; + std::scoped_lock lk{connection_lock}; - if (stopped) { - // Do not notify the debugger about another event. - // It should be ignored. - return false; - } - - // Set up the state. - stopped = true; - info = signal_info; + if (stopped || !state) { + // Do not notify the debugger about another event. + // It should be ignored. + return false; } + // Set up the state. + stopped = true; + state->info = signal_info; + // Write a single byte into the pipe to wake up the debug interface. - boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); + boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); + return true; } + // These functions are callbacks from the frontend, and the lock will be held. + // There is no need to relock it. + std::span ReadFromClient() override { - return ReceiveInto(client_socket, client_data); + return ReceiveInto(state->client_socket, state->client_data); } void WriteToClient(std::span data) override { - boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes())); + boost::asio::write(state->client_socket, + boost::asio::buffer(data.data(), data.size_bytes())); } void SetActiveThread(Kernel::KThread* thread) override { - active_thread = thread; + state->active_thread = thread; } Kernel::KThread* GetActiveThread() override { - return active_thread; + return state->active_thread; } private: @@ -113,65 +124,78 @@ private: // Run the connection thread. connection_thread = std::jthread([&, port](std::stop_token stop_token) { + Common::SetCurrentThreadName("Debugger"); + try { // Initialize the listening socket and accept a new client. tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port}; tcp::acceptor acceptor{io_context, endpoint}; - acceptor.async_accept(client_socket, [](const auto&) {}); - io_context.run_one(); - io_context.restart(); + AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); }); - if (stop_token.stop_requested()) { - return; + while (!stop_token.stop_requested() && io_context.run()) { } - - ThreadLoop(stop_token); } catch (const std::exception& ex) { LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what()); } }); } + void AcceptConnection(boost::asio::ip::tcp::socket&& peer) { + LOG_INFO(Debug_GDBStub, "Accepting new peer connection"); + + std::scoped_lock lk{connection_lock}; + + // Ensure everything is stopped. + PauseEmulation(); + + // Set up the new frontend. + frontend = std::make_unique(*this, system); + + // Set the new state. This will tear down any existing state. + state = ConnectionState{ + .client_socket{std::move(peer)}, + .signal_pipe{io_context}, + .info{}, + .active_thread{}, + .client_data{}, + .pipe_data{}, + }; + + // Set up the client signals for new data. + AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); }); + AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); }); + + // Set the active thread. + UpdateActiveThread(); + + // Set up the frontend. + frontend->Connected(); + } + void ShutdownServer() { connection_thread.request_stop(); io_context.stop(); connection_thread.join(); } - void ThreadLoop(std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:Debugger"); - - // Set up the client signals for new data. - AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); - AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); - - // Set the active thread. - UpdateActiveThread(); - - // Set up the frontend. - frontend->Connected(); - - // Main event loop. - while (!stop_token.stop_requested() && io_context.run()) { - } - } - void PipeData(std::span data) { - switch (info.type) { + std::scoped_lock lk{connection_lock}; + + switch (state->info.type) { case SignalType::Stopped: case SignalType::Watchpoint: // Stop emulation. PauseEmulation(); // Notify the client. - active_thread = info.thread; + state->active_thread = state->info.thread; UpdateActiveThread(); - if (info.type == SignalType::Watchpoint) { - frontend->Watchpoint(active_thread, *info.watchpoint); + if (state->info.type == SignalType::Watchpoint) { + frontend->Watchpoint(state->active_thread, *state->info.watchpoint); } else { - frontend->Stopped(active_thread); + frontend->Stopped(state->active_thread); } break; @@ -179,8 +203,8 @@ private: frontend->ShuttingDown(); // Wait for emulation to shut down gracefully now. - signal_pipe.close(); - client_socket.shutdown(boost::asio::socket_base::shutdown_both); + state->signal_pipe.close(); + state->client_socket.shutdown(boost::asio::socket_base::shutdown_both); LOG_INFO(Debug_GDBStub, "Shut down server"); break; @@ -188,17 +212,16 @@ private: } void ClientData(std::span data) { + std::scoped_lock lk{connection_lock}; + const auto actions{frontend->ClientData(data)}; for (const auto action : actions) { switch (action) { case DebuggerAction::Interrupt: { - { - std::scoped_lock lk{connection_lock}; - stopped = true; - } + stopped = true; PauseEmulation(); UpdateActiveThread(); - frontend->Stopped(active_thread); + frontend->Stopped(state->active_thread); break; } case DebuggerAction::Continue: @@ -206,15 +229,15 @@ private: break; case DebuggerAction::StepThreadUnlocked: MarkResumed([&] { - active_thread->SetStepState(Kernel::StepState::StepPending); - active_thread->Resume(Kernel::SuspendType::Debug); - ResumeEmulation(active_thread); + state->active_thread->SetStepState(Kernel::StepState::StepPending); + state->active_thread->Resume(Kernel::SuspendType::Debug); + ResumeEmulation(state->active_thread); }); break; case DebuggerAction::StepThreadLocked: { MarkResumed([&] { - active_thread->SetStepState(Kernel::StepState::StepPending); - active_thread->Resume(Kernel::SuspendType::Debug); + state->active_thread->SetStepState(Kernel::StepState::StepPending); + state->active_thread->Resume(Kernel::SuspendType::Debug); }); break; } @@ -254,15 +277,14 @@ private: template void MarkResumed(Callback&& cb) { Kernel::KScopedSchedulerLock sl{system.Kernel()}; - std::scoped_lock cl{connection_lock}; stopped = false; cb(); } void UpdateActiveThread() { const auto& threads{ThreadList()}; - if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) { - active_thread = threads[0]; + if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) { + state->active_thread = threads[0]; } } @@ -274,18 +296,22 @@ private: System& system; std::unique_ptr frontend; + boost::asio::io_context io_context; std::jthread connection_thread; std::mutex connection_lock; - boost::asio::io_context io_context; - boost::process::async_pipe signal_pipe; - boost::asio::ip::tcp::socket client_socket; - SignalInfo info; - Kernel::KThread* active_thread; - bool pipe_data; - bool stopped; + struct ConnectionState { + boost::asio::ip::tcp::socket client_socket; + boost::process::async_pipe signal_pipe; - std::array client_data; + SignalInfo info; + Kernel::KThread* active_thread; + std::array client_data; + bool pipe_data; + }; + + std::optional state{}; + bool stopped{}; }; Debugger::Debugger(Core::System& system, u16 port) { diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 884229c..a64a9ac 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -606,6 +606,8 @@ void GDBStub::HandleQuery(std::string_view command) { } else if (command.starts_with("StartNoAckMode")) { no_ack = true; SendReply(GDB_STUB_REPLY_OK); + } else if (command.starts_with("Rcmd,")) { + HandleRcmd(Common::HexStringToVector(command.substr(5), false)); } else { SendReply(GDB_STUB_REPLY_EMPTY); } @@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector& } } +constexpr std::array, 22> MemoryStateNames{{ + {"----- Free -----", Kernel::Svc::MemoryState::Free}, + {"Io ", Kernel::Svc::MemoryState::Io}, + {"Static ", Kernel::Svc::MemoryState::Static}, + {"Code ", Kernel::Svc::MemoryState::Code}, + {"CodeData ", Kernel::Svc::MemoryState::CodeData}, + {"Normal ", Kernel::Svc::MemoryState::Normal}, + {"Shared ", Kernel::Svc::MemoryState::Shared}, + {"AliasCode ", Kernel::Svc::MemoryState::AliasCode}, + {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData}, + {"Ipc ", Kernel::Svc::MemoryState::Ipc}, + {"Stack ", Kernel::Svc::MemoryState::Stack}, + {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal}, + {"Transfered ", Kernel::Svc::MemoryState::Transfered}, + {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered}, + {"SharedCode ", Kernel::Svc::MemoryState::SharedCode}, + {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible}, + {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc}, + {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc}, + {"Kernel ", Kernel::Svc::MemoryState::Kernel}, + {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode}, + {"CodeOut ", Kernel::Svc::MemoryState::CodeOut}, + {"Coverage ", Kernel::Svc::MemoryState::Coverage}, +}}; + +static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) { + for (size_t i = 0; i < MemoryStateNames.size(); i++) { + if (std::get<1>(MemoryStateNames[i]) == state) { + return std::get<0>(MemoryStateNames[i]); + } + } + return "Unknown "; +} + +static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) { + if (info.state == Kernel::Svc::MemoryState::Free) { + return " "; + } + + switch (info.permission) { + case Kernel::Svc::MemoryPermission::ReadExecute: + return "r-x"; + case Kernel::Svc::MemoryPermission::Read: + return "r--"; + case Kernel::Svc::MemoryPermission::ReadWrite: + return "rw-"; + default: + return "---"; + } +} + +static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) { + Kernel::Svc::MemoryInfo mem_info; + VAddr cur_addr{base}; + + // Expect: r-x Code (.text) + mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); + cur_addr = mem_info.base_address + mem_info.size; + if (mem_info.state != Kernel::Svc::MemoryState::Code || + mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { + return cur_addr - 1; + } + + // Expect: r-- Code (.rodata) + mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); + cur_addr = mem_info.base_address + mem_info.size; + if (mem_info.state != Kernel::Svc::MemoryState::Code || + mem_info.permission != Kernel::Svc::MemoryPermission::Read) { + return cur_addr - 1; + } + + // Expect: rw- CodeData (.data) + mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); + cur_addr = mem_info.base_address + mem_info.size; + return cur_addr - 1; +} + +void GDBStub::HandleRcmd(const std::vector& command) { + std::string_view command_str{reinterpret_cast(&command[0]), command.size()}; + std::string reply; + + auto* process = system.CurrentProcess(); + auto& page_table = process->PageTable(); + + if (command_str == "get info") { + Loader::AppLoader::Modules modules; + system.GetAppLoader().ReadNSOModules(modules); + + reply = fmt::format("Process: {:#x} ({})\n" + "Program Id: {:#018x}\n", + process->GetProcessID(), process->GetName(), process->GetProgramID()); + reply += + fmt::format("Layout:\n" + " Alias: {:#012x} - {:#012x}\n" + " Heap: {:#012x} - {:#012x}\n" + " Aslr: {:#012x} - {:#012x}\n" + " Stack: {:#012x} - {:#012x}\n" + "Modules:\n", + page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(), + page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(), + page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(), + page_table.GetStackRegionStart(), page_table.GetStackRegionEnd()); + + for (const auto& [vaddr, name] : modules) { + reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, + GetModuleEnd(page_table, vaddr), name); + } + } else if (command_str == "get mappings") { + reply = "Mappings:\n"; + VAddr cur_addr = 0; + + while (true) { + using MemoryAttribute = Kernel::Svc::MemoryAttribute; + + auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); + + if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible || + mem_info.base_address + mem_info.size - 1 != std::numeric_limits::max()) { + const char* state = GetMemoryStateName(mem_info.state); + const char* perm = GetMemoryPermissionString(mem_info); + + const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; + const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; + const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; + const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; + + reply += + fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n", + mem_info.base_address, mem_info.base_address + mem_info.size - 1, + perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count); + } + + const uintptr_t next_address = mem_info.base_address + mem_info.size; + if (next_address <= cur_addr) { + break; + } + + cur_addr = next_address; + } + } else if (command_str == "help") { + reply = "Commands:\n get info\n get mappings\n"; + } else { + reply = "Unknown command.\nCommands:\n get info\n get mappings\n"; + } + + std::span reply_span{reinterpret_cast(&reply.front()), reply.size()}; + SendReply(Common::HexToString(reply_span, false)); +} + Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; for (auto* thread : threads) { diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h index 0b0f56e..3681979 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h @@ -32,6 +32,7 @@ private: void ExecuteCommand(std::string_view packet, std::vector& actions); void HandleVCont(std::string_view command, std::vector& actions); void HandleQuery(std::string_view command); + void HandleRcmd(const std::vector& command); void HandleBreakpointInsert(std::string_view command); void HandleBreakpointRemove(std::string_view command); std::vector::const_iterator CommandEnd() const; diff --git a/src/core/device_memory.h b/src/core/device_memory.h index df61b0c..9051073 100644 --- a/src/core/device_memory.h +++ b/src/core/device_memory.h @@ -31,12 +31,14 @@ public: DramMemoryMap::Base; } - u8* GetPointer(PAddr addr) { - return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base); + template + T* GetPointer(PAddr addr) { + return reinterpret_cast(buffer.BackingBasePointer() + (addr - DramMemoryMap::Base)); } - const u8* GetPointer(PAddr addr) const { - return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base); + template + const T* GetPointer(PAddr addr) const { + return reinterpret_cast(buffer.BackingBasePointer() + (addr - DramMemoryMap::Base)); } Common::HostMemory buffer; diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index f23d937..5d02865 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -232,8 +232,8 @@ const std::vector>& XCI::GetNCAs() const { std::shared_ptr XCI::GetNCAByType(NCAContentType type) const { const auto program_id = secure_partition->GetProgramTitleID(); - const auto iter = std::find_if( - ncas.begin(), ncas.end(), [this, type, program_id](const std::shared_ptr& nca) { + const auto iter = + std::find_if(ncas.begin(), ncas.end(), [type, program_id](const std::shared_ptr& nca) { return nca->GetType() == type && nca->GetTitleId() == program_id; }); return iter == ncas.end() ? nullptr : *iter; diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 78e56bb..50303fe 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -7,6 +7,7 @@ #include #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "core/crypto/aes_util.h" #include "core/crypto/ctr_encryption_layer.h" #include "core/crypto/key_manager.h" diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index be25da2..50f44f5 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/settings.h" #include "common/string_util.h" #include "common/swap.h" #include "core/file_sys/control_metadata.h" @@ -37,6 +38,27 @@ std::string LanguageEntry::GetDeveloperName() const { developer_name.size()); } +constexpr std::array language_to_codes = {{ + Language::Japanese, + Language::AmericanEnglish, + Language::French, + Language::German, + Language::Italian, + Language::Spanish, + Language::Chinese, + Language::Korean, + Language::Dutch, + Language::Portuguese, + Language::Russian, + Language::Taiwanese, + Language::BritishEnglish, + Language::CanadianFrench, + Language::LatinAmericanSpanish, + Language::Chinese, + Language::Taiwanese, + Language::BrazilianPortuguese, +}}; + NACP::NACP() = default; NACP::NACP(VirtualFile file) { @@ -45,9 +67,13 @@ NACP::NACP(VirtualFile file) { NACP::~NACP() = default; -const LanguageEntry& NACP::GetLanguageEntry(Language language) const { - if (language != Language::Default) { - return raw.language_entries.at(static_cast(language)); +const LanguageEntry& NACP::GetLanguageEntry() const { + Language language = language_to_codes[Settings::values.language_index.GetValue()]; + + { + const auto& language_entry = raw.language_entries.at(static_cast(language)); + if (!language_entry.GetApplicationName().empty()) + return language_entry; } for (const auto& language_entry : raw.language_entries) { @@ -55,16 +81,15 @@ const LanguageEntry& NACP::GetLanguageEntry(Language language) const { return language_entry; } - // Fallback to English - return GetLanguageEntry(Language::AmericanEnglish); + return raw.language_entries.at(static_cast(Language::AmericanEnglish)); } -std::string NACP::GetApplicationName(Language language) const { - return GetLanguageEntry(language).GetApplicationName(); +std::string NACP::GetApplicationName() const { + return GetLanguageEntry().GetApplicationName(); } -std::string NACP::GetDeveloperName(Language language) const { - return GetLanguageEntry(language).GetDeveloperName(); +std::string NACP::GetDeveloperName() const { + return GetLanguageEntry().GetDeveloperName(); } u64 NACP::GetTitleId() const { diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 7529551..6a81873 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -101,9 +101,9 @@ public: explicit NACP(VirtualFile file); ~NACP(); - const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; - std::string GetApplicationName(Language language = Language::Default) const; - std::string GetDeveloperName(Language language = Language::Default) const; + const LanguageEntry& GetLanguageEntry() const; + std::string GetApplicationName() const; + std::string GetDeveloperName() const; u64 GetTitleId() const; u64 GetDLCBaseTitleId() const; std::string GetVersionString() const; diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index e0cdf35..f00479b 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -33,11 +33,55 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { return Loader::ResultStatus::ErrorBadACIHeader; } - if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) { + // Load acid_file_access per-component instead of the entire struct, since this struct does not + // reflect the layout of the real data. + std::size_t current_offset = acid_header.fac_offset; + if (sizeof(FileAccessControl::version) != file->ReadBytes(&acid_file_access.version, + sizeof(FileAccessControl::version), + current_offset)) { + return Loader::ResultStatus::ErrorBadFileAccessControl; + } + if (sizeof(FileAccessControl::permissions) != + file->ReadBytes(&acid_file_access.permissions, sizeof(FileAccessControl::permissions), + current_offset += sizeof(FileAccessControl::version) + 3)) { + return Loader::ResultStatus::ErrorBadFileAccessControl; + } + if (sizeof(FileAccessControl::unknown) != + file->ReadBytes(&acid_file_access.unknown, sizeof(FileAccessControl::unknown), + current_offset + sizeof(FileAccessControl::permissions))) { return Loader::ResultStatus::ErrorBadFileAccessControl; } - if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) { + // Load aci_file_access per-component instead of the entire struct, same as acid_file_access + current_offset = aci_header.fah_offset; + if (sizeof(FileAccessHeader::version) != file->ReadBytes(&aci_file_access.version, + sizeof(FileAccessHeader::version), + current_offset)) { + return Loader::ResultStatus::ErrorBadFileAccessHeader; + } + if (sizeof(FileAccessHeader::permissions) != + file->ReadBytes(&aci_file_access.permissions, sizeof(FileAccessHeader::permissions), + current_offset += sizeof(FileAccessHeader::version) + 3)) { + return Loader::ResultStatus::ErrorBadFileAccessHeader; + } + if (sizeof(FileAccessHeader::unk_offset) != + file->ReadBytes(&aci_file_access.unk_offset, sizeof(FileAccessHeader::unk_offset), + current_offset += sizeof(FileAccessHeader::permissions))) { + return Loader::ResultStatus::ErrorBadFileAccessHeader; + } + if (sizeof(FileAccessHeader::unk_size) != + file->ReadBytes(&aci_file_access.unk_size, sizeof(FileAccessHeader::unk_size), + current_offset += sizeof(FileAccessHeader::unk_offset))) { + return Loader::ResultStatus::ErrorBadFileAccessHeader; + } + if (sizeof(FileAccessHeader::unk_offset_2) != + file->ReadBytes(&aci_file_access.unk_offset_2, sizeof(FileAccessHeader::unk_offset_2), + current_offset += sizeof(FileAccessHeader::unk_size))) { + return Loader::ResultStatus::ErrorBadFileAccessHeader; + } + if (sizeof(FileAccessHeader::unk_size_2) != + file->ReadBytes(&aci_file_access.unk_size_2, sizeof(FileAccessHeader::unk_size_2), + current_offset + sizeof(FileAccessHeader::unk_offset_2))) { return Loader::ResultStatus::ErrorBadFileAccessHeader; } @@ -83,7 +127,7 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address } bool ProgramMetadata::Is64BitProgram() const { - return npdm_header.has_64_bit_instructions; + return npdm_header.has_64_bit_instructions.As(); } ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const { @@ -152,9 +196,7 @@ void ProgramMetadata::Print() const { LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO"); LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min); LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max); - u64_le permissions_l; // local copy to fix alignment error - std::memcpy(&permissions_l, &acid_file_access.permissions, sizeof(permissions_l)); - LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", permissions_l); + LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions); // Begin ACI0 printing (actual perms, unsigned) LOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data()); diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index e8fb4e2..2e8960b 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -144,20 +144,18 @@ private: static_assert(sizeof(AciHeader) == 0x40, "ACI0 header structure size is wrong"); -#pragma pack(push, 1) - + // FileAccessControl and FileAccessHeader need loaded per-component: this layout does not + // reflect the real layout to avoid reference binding to misaligned addresses struct FileAccessControl { u8 version; - INSERT_PADDING_BYTES(3); + // 3 padding bytes u64_le permissions; std::array unknown; }; - static_assert(sizeof(FileAccessControl) == 0x2C, "FS access control structure size is wrong"); - struct FileAccessHeader { u8 version; - INSERT_PADDING_BYTES(3); + // 3 padding bytes u64_le permissions; u32_le unk_offset; u32_le unk_size; @@ -165,10 +163,6 @@ private: u32_le unk_size_2; }; - static_assert(sizeof(FileAccessHeader) == 0x1C, "FS access header structure size is wrong"); - -#pragma pack(pop) - Header npdm_header; AciHeader aci_header; AcidHeader acid_header; diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 8c1b252..1567da2 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -5,6 +5,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/uuid.h" #include "core/core.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/vfs.h" @@ -59,6 +60,36 @@ bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataA attr.title_id == 0 && attr.save_id == 0); } +std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u64 title_id, + u128 user_id) { + // Only detect nand user saves. + const auto space_id_path = [space_id]() -> std::string_view { + switch (space_id) { + case SaveDataSpaceId::NandUser: + return "/user/save"; + default: + return ""; + } + }(); + + if (space_id_path.empty()) { + return ""; + } + + Common::UUID uuid; + std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID)); + + // Only detect account/device saves from the future location. + switch (type) { + case SaveDataType::SaveData: + return fmt::format("{}/account/{}/{:016X}/1", space_id_path, uuid.RawString(), title_id); + case SaveDataType::DeviceSaveData: + return fmt::format("{}/device/{:016X}/1", space_id_path, title_id); + default: + return ""; + } +} + } // Anonymous namespace std::string SaveDataAttribute::DebugInfo() const { @@ -82,7 +113,7 @@ ResultVal SaveDataFactory::Create(SaveDataSpaceId space, PrintSaveDataAttributeWarnings(meta); const auto save_directory = - GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id); + GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); auto out = dir->CreateDirectoryRelative(save_directory); @@ -99,7 +130,7 @@ ResultVal SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const { const auto save_directory = - GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id); + GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); auto out = dir->GetDirectoryRelative(save_directory); @@ -134,9 +165,9 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { } } -std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId space, - SaveDataType type, u64 title_id, u128 user_id, - u64 save_id) { +std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir, + SaveDataSpaceId space, SaveDataType type, u64 title_id, + u128 user_id, u64 save_id) { // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should // be interpreted as the title id of the current process. if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) { @@ -145,6 +176,17 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s } } + // For compat with a future impl. + if (std::string future_path = + GetFutureSaveDataPath(space, type, title_id & ~(0xFFULL), user_id); + !future_path.empty()) { + // Check if this location exists, and prefer it over the old. + if (const auto future_dir = dir->GetDirectoryRelative(future_path); future_dir != nullptr) { + LOG_INFO(Service_FS, "Using save at new location: {}", future_path); + return future_path; + } + } + std::string out = GetSaveDataSpaceIdPath(space); switch (type) { @@ -167,7 +209,8 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const { - const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + const auto path = + GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto size_file = relative_dir->GetFile(SAVE_DATA_SIZE_FILENAME); @@ -185,7 +228,8 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value) const { - const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + const auto path = + GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto size_file = relative_dir->CreateFile(SAVE_DATA_SIZE_FILENAME); diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index a763b94..d3633ef 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -95,8 +95,8 @@ public: VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); - static std::string GetFullPath(Core::System& system, SaveDataSpaceId space, SaveDataType type, - u64 title_id, u128 user_id, u64 save_id); + static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space, + SaveDataType type, u64 title_id, u128 user_id, u64 save_id); SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, diff --git a/src/core/frontend/applets/cabinet.cpp b/src/core/frontend/applets/cabinet.cpp new file mode 100644 index 0000000..26c7fef --- /dev/null +++ b/src/core/frontend/applets/cabinet.cpp @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/frontend/applets/cabinet.h" + +#include + +namespace Core::Frontend { + +CabinetApplet::~CabinetApplet() = default; + +void DefaultCabinetApplet::ShowCabinetApplet( + const CabinetCallback& callback, const CabinetParameters& parameters, + std::shared_ptr nfp_device) const { + LOG_WARNING(Service_AM, "(STUBBED) called"); + callback(false, {}); +} + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/cabinet.h b/src/core/frontend/applets/cabinet.h new file mode 100644 index 0000000..c28a235 --- /dev/null +++ b/src/core/frontend/applets/cabinet.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "core/hle/service/nfp/nfp_types.h" + +namespace Service::NFP { +class NfpDevice; +} // namespace Service::NFP + +namespace Core::Frontend { + +struct CabinetParameters { + Service::NFP::TagInfo tag_info; + Service::NFP::RegisterInfo register_info; + Service::NFP::CabinetMode mode; +}; + +using CabinetCallback = std::function; + +class CabinetApplet { +public: + virtual ~CabinetApplet(); + virtual void ShowCabinetApplet(const CabinetCallback& callback, + const CabinetParameters& parameters, + std::shared_ptr nfp_device) const = 0; +}; + +class DefaultCabinetApplet final : public CabinetApplet { +public: + void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters, + std::shared_ptr nfp_device) const override; +}; + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index 6c230f6..5291948 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp @@ -16,7 +16,7 @@ DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_ DefaultControllerApplet::~DefaultControllerApplet() = default; -void DefaultControllerApplet::ReconfigureControllers(std::function callback, +void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callback, const ControllerParameters& parameters) const { LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h index 1d2850a..adb2fee 100644 --- a/src/core/frontend/applets/controller.h +++ b/src/core/frontend/applets/controller.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include "common/common_types.h" @@ -35,9 +36,11 @@ struct ControllerParameters { class ControllerApplet { public: + using ReconfigureCallback = std::function; + virtual ~ControllerApplet(); - virtual void ReconfigureControllers(std::function callback, + virtual void ReconfigureControllers(ReconfigureCallback callback, const ControllerParameters& parameters) const = 0; }; @@ -46,7 +49,7 @@ public: explicit DefaultControllerApplet(HID::HIDCore& hid_core_); ~DefaultControllerApplet() override; - void ReconfigureControllers(std::function callback, + void ReconfigureControllers(ReconfigureCallback callback, const ControllerParameters& parameters) const override; private: diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp index f8b9610..69c2b2b 100644 --- a/src/core/frontend/applets/error.cpp +++ b/src/core/frontend/applets/error.cpp @@ -8,13 +8,13 @@ namespace Core::Frontend { ErrorApplet::~ErrorApplet() = default; -void DefaultErrorApplet::ShowError(Result error, std::function finished) const { +void DefaultErrorApplet::ShowError(Result error, FinishedCallback finished) const { LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})", error.module.Value(), error.description.Value(), error.raw); } void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time, - std::function finished) const { + FinishedCallback finished) const { LOG_CRITICAL( Service_Fatal, "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}", @@ -23,7 +23,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::secon void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text, std::string detail_text, - std::function finished) const { + FinishedCallback finished) const { LOG_CRITICAL(Service_Fatal, "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})", error.module.Value(), error.description.Value(), error.raw); diff --git a/src/core/frontend/applets/error.h b/src/core/frontend/applets/error.h index f378f88..884f2f6 100644 --- a/src/core/frontend/applets/error.h +++ b/src/core/frontend/applets/error.h @@ -12,25 +12,27 @@ namespace Core::Frontend { class ErrorApplet { public: + using FinishedCallback = std::function; + virtual ~ErrorApplet(); - virtual void ShowError(Result error, std::function finished) const = 0; + virtual void ShowError(Result error, FinishedCallback finished) const = 0; virtual void ShowErrorWithTimestamp(Result error, std::chrono::seconds time, - std::function finished) const = 0; + FinishedCallback finished) const = 0; virtual void ShowCustomErrorText(Result error, std::string dialog_text, std::string fullscreen_text, - std::function finished) const = 0; + FinishedCallback finished) const = 0; }; class DefaultErrorApplet final : public ErrorApplet { public: - void ShowError(Result error, std::function finished) const override; + void ShowError(Result error, FinishedCallback finished) const override; void ShowErrorWithTimestamp(Result error, std::chrono::seconds time, - std::function finished) const override; + FinishedCallback finished) const override; void ShowCustomErrorText(Result error, std::string main_text, std::string detail_text, - std::function finished) const override; + FinishedCallback finished) const override; }; } // namespace Core::Frontend diff --git a/src/core/frontend/applets/mii_edit.cpp b/src/core/frontend/applets/mii_edit.cpp index d37b536..bc8c570 100644 --- a/src/core/frontend/applets/mii_edit.cpp +++ b/src/core/frontend/applets/mii_edit.cpp @@ -8,7 +8,7 @@ namespace Core::Frontend { MiiEditApplet::~MiiEditApplet() = default; -void DefaultMiiEditApplet::ShowMiiEdit(const std::function& callback) const { +void DefaultMiiEditApplet::ShowMiiEdit(const MiiEditCallback& callback) const { LOG_WARNING(Service_AM, "(STUBBED) called"); callback(); diff --git a/src/core/frontend/applets/mii_edit.h b/src/core/frontend/applets/mii_edit.h index 58fa203..d828f06 100644 --- a/src/core/frontend/applets/mii_edit.h +++ b/src/core/frontend/applets/mii_edit.h @@ -9,14 +9,16 @@ namespace Core::Frontend { class MiiEditApplet { public: + using MiiEditCallback = std::function; + virtual ~MiiEditApplet(); - virtual void ShowMiiEdit(const std::function& callback) const = 0; + virtual void ShowMiiEdit(const MiiEditCallback& callback) const = 0; }; class DefaultMiiEditApplet final : public MiiEditApplet { public: - void ShowMiiEdit(const std::function& callback) const override; + void ShowMiiEdit(const MiiEditCallback& callback) const override; }; } // namespace Core::Frontend diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp index d11fbce..da4cfbf 100644 --- a/src/core/frontend/applets/profile_select.cpp +++ b/src/core/frontend/applets/profile_select.cpp @@ -9,8 +9,7 @@ namespace Core::Frontend { ProfileSelectApplet::~ProfileSelectApplet() = default; -void DefaultProfileSelectApplet::SelectProfile( - std::function)> callback) const { +void DefaultProfileSelectApplet::SelectProfile(SelectProfileCallback callback) const { Service::Account::ProfileManager manager; callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{})); LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h index 8d6ee52..1384295 100644 --- a/src/core/frontend/applets/profile_select.h +++ b/src/core/frontend/applets/profile_select.h @@ -11,14 +11,16 @@ namespace Core::Frontend { class ProfileSelectApplet { public: + using SelectProfileCallback = std::function)>; + virtual ~ProfileSelectApplet(); - virtual void SelectProfile(std::function)> callback) const = 0; + virtual void SelectProfile(SelectProfileCallback callback) const = 0; }; class DefaultProfileSelectApplet final : public ProfileSelectApplet { public: - void SelectProfile(std::function)> callback) const override; + void SelectProfile(SelectProfileCallback callback) const override; }; } // namespace Core::Frontend diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index 020c7fa..a3720f4 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -15,10 +15,7 @@ DefaultSoftwareKeyboardApplet::~DefaultSoftwareKeyboardApplet() = default; void DefaultSoftwareKeyboardApplet::InitializeKeyboard( bool is_inline, KeyboardInitializeParameters initialize_parameters, - std::function - submit_normal_callback_, - std::function - submit_inline_callback_) { + SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) { if (is_inline) { LOG_WARNING( Service_AM, diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index 094d1e7..8aef103 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -54,14 +54,17 @@ struct InlineTextParameters { class SoftwareKeyboardApplet { public: + using SubmitInlineCallback = + std::function; + using SubmitNormalCallback = + std::function; + virtual ~SoftwareKeyboardApplet(); - virtual void InitializeKeyboard( - bool is_inline, KeyboardInitializeParameters initialize_parameters, - std::function - submit_normal_callback_, - std::function - submit_inline_callback_) = 0; + virtual void InitializeKeyboard(bool is_inline, + KeyboardInitializeParameters initialize_parameters, + SubmitNormalCallback submit_normal_callback_, + SubmitInlineCallback submit_inline_callback_) = 0; virtual void ShowNormalKeyboard() const = 0; @@ -81,12 +84,9 @@ class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { public: ~DefaultSoftwareKeyboardApplet() override; - void InitializeKeyboard( - bool is_inline, KeyboardInitializeParameters initialize_parameters, - std::function - submit_normal_callback_, - std::function - submit_inline_callback_) override; + void InitializeKeyboard(bool is_inline, KeyboardInitializeParameters initialize_parameters, + SubmitNormalCallback submit_normal_callback_, + SubmitInlineCallback submit_inline_callback_) override; void ShowNormalKeyboard() const override; @@ -105,12 +105,10 @@ private: void SubmitNormalText(std::u16string text) const; void SubmitInlineText(std::u16string_view text) const; - KeyboardInitializeParameters parameters; + KeyboardInitializeParameters parameters{}; - mutable std::function - submit_normal_callback; - mutable std::function - submit_inline_callback; + mutable SubmitNormalCallback submit_normal_callback; + mutable SubmitInlineCallback submit_inline_callback; }; } // namespace Core::Frontend diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp index 27c7086..b09cb71 100644 --- a/src/core/frontend/applets/web_browser.cpp +++ b/src/core/frontend/applets/web_browser.cpp @@ -10,18 +10,17 @@ WebBrowserApplet::~WebBrowserApplet() = default; DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default; -void DefaultWebBrowserApplet::OpenLocalWebPage( - const std::string& local_url, std::function extract_romfs_callback, - std::function callback) const { +void DefaultWebBrowserApplet::OpenLocalWebPage(const std::string& local_url, + ExtractROMFSCallback extract_romfs_callback, + OpenWebPageCallback callback) const { LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}", local_url); callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/"); } -void DefaultWebBrowserApplet::OpenExternalWebPage( - const std::string& external_url, - std::function callback) const { +void DefaultWebBrowserApplet::OpenExternalWebPage(const std::string& external_url, + OpenWebPageCallback callback) const { LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}", external_url); diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h index 1411274..4f72284 100644 --- a/src/core/frontend/applets/web_browser.h +++ b/src/core/frontend/applets/web_browser.h @@ -11,29 +11,29 @@ namespace Core::Frontend { class WebBrowserApplet { public: + using ExtractROMFSCallback = std::function; + using OpenWebPageCallback = + std::function; + virtual ~WebBrowserApplet(); - virtual void OpenLocalWebPage( - const std::string& local_url, std::function extract_romfs_callback, - std::function callback) const = 0; + virtual void OpenLocalWebPage(const std::string& local_url, + ExtractROMFSCallback extract_romfs_callback, + OpenWebPageCallback callback) const = 0; - virtual void OpenExternalWebPage( - const std::string& external_url, - std::function callback) const = 0; + virtual void OpenExternalWebPage(const std::string& external_url, + OpenWebPageCallback callback) const = 0; }; class DefaultWebBrowserApplet final : public WebBrowserApplet { public: ~DefaultWebBrowserApplet() override; - void OpenLocalWebPage(const std::string& local_url, - std::function extract_romfs_callback, - std::function - callback) const override; + void OpenLocalWebPage(const std::string& local_url, ExtractROMFSCallback extract_romfs_callback, + OpenWebPageCallback callback) const override; void OpenExternalWebPage(const std::string& external_url, - std::function - callback) const override; + OpenWebPageCallback callback) const override; }; } // namespace Core::Frontend diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index ac1906d..cf85ba2 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -17,6 +17,8 @@ enum class WindowSystemType { Windows, X11, Wayland, + Cocoa, + Android, }; /** @@ -129,6 +131,10 @@ public: return active_config; } + bool StrictContextRequired() const { + return strict_context_required; + } + /** * Requests the internal configuration to be replaced by the specified argument at some point in * the future. @@ -205,6 +211,8 @@ protected: WindowSystemInfo window_info; + bool strict_context_required = false; + private: /** * Handler called when the minimal client area was requested to be changed via SetConfig. diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 90dd68f..b4081fc 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -67,6 +67,8 @@ float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio) { return 3.0f / 4.0f; case AspectRatio::R21_9: return 9.0f / 21.0f; + case AspectRatio::R16_10: + return 10.0f / 16.0f; case AspectRatio::StretchToWindow: return window_aspect_ratio; default: diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 1561d99..94683b3 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -27,6 +27,7 @@ enum class AspectRatio { Default, R4_3, R21_9, + R16_10, StretchToWindow, }; diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp deleted file mode 100644 index d08cc33..0000000 --- a/src/core/hardware_interrupt_manager.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_interrupt_manager.h" -#include "core/hle/service/nvdrv/nvdrv_interface.h" -#include "core/hle/service/sm/sm.h" - -namespace Core::Hardware { - -InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { - gpu_interrupt_event = Core::Timing::CreateEvent( - "GPUInterrupt", - [this](std::uintptr_t message, u64 time, - std::chrono::nanoseconds) -> std::optional { - auto nvdrv = system.ServiceManager().GetService("nvdrv"); - const u32 syncpt = static_cast(message >> 32); - const u32 value = static_cast(message); - nvdrv->SignalGPUInterruptSyncpt(syncpt, value); - return std::nullopt; - }); -} - -InterruptManager::~InterruptManager() = default; - -void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { - const u64 msg = (static_cast(syncpoint_id) << 32ULL) | value; - system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg); -} - -} // namespace Core::Hardware diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h deleted file mode 100644 index 5665c59..0000000 --- a/src/core/hardware_interrupt_manager.h +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "common/common_types.h" - -namespace Core { -class System; -} - -namespace Core::Timing { -struct EventType; -} - -namespace Core::Hardware { - -class InterruptManager { -public: - explicit InterruptManager(Core::System& system); - ~InterruptManager(); - - void GPUInterruptSyncpt(u32 syncpoint_id, u32 value); - -private: - Core::System& system; - std::shared_ptr gpu_interrupt_event; -}; - -} // namespace Core::Hardware diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp index aac4590..30c2e9d 100644 --- a/src/core/hid/emulated_console.cpp +++ b/src/core/hid/emulated_console.cpp @@ -19,27 +19,26 @@ void EmulatedConsole::ReloadFromSettings() { } void EmulatedConsole::SetTouchParams() { - // TODO(german77): Support any number of fingers std::size_t index = 0; - // Hardcode mouse, touchscreen and cemuhook parameters + // We can't use mouse as touch if native mouse is enabled if (!Settings::values.mouse_enabled) { - // We can't use mouse as touch if native mouse is enabled touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"}; } touch_params[index++] = - Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"}; + Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"}; touch_params[index++] = - Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"}; - touch_params[index++] = - Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"}; - touch_params[index++] = - Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"}; - touch_params[index++] = - Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"}; - touch_params[index++] = - Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"}; + Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"}; + + for (int i = 0; i < static_cast(MaxActiveTouchInputs); i++) { + Common::ParamPackage touchscreen_param{}; + touchscreen_param.Set("engine", "touch"); + touchscreen_param.Set("axis_x", i * 2); + touchscreen_param.Set("axis_y", (i * 2) + 1); + touchscreen_param.Set("button", i); + touch_params[index++] = std::move(touchscreen_param); + } const auto button_index = static_cast(Settings::values.touch_from_button_map_index.GetValue()); @@ -47,7 +46,7 @@ void EmulatedConsole::SetTouchParams() { // Map the rest of the fingers from touch from button configuration for (const auto& config_entry : touch_buttons) { - if (index >= touch_params.size()) { + if (index >= MaxTouchDevices) { continue; } Common::ParamPackage params{config_entry}; @@ -60,8 +59,7 @@ void EmulatedConsole::SetTouchParams() { touch_button_params.Set("button", params.Serialize()); touch_button_params.Set("x", x); touch_button_params.Set("y", y); - touch_button_params.Set("touch_id", static_cast(index)); - touch_params[index] = touch_button_params; + touch_params[index] = std::move(touch_button_params); index++; } } @@ -70,7 +68,7 @@ void EmulatedConsole::ReloadInput() { // If you load any device here add the equivalent to the UnloadInput() function SetTouchParams(); - motion_devices = Common::Input::CreateDevice(motion_params); + motion_devices = Common::Input::CreateInputDevice(motion_params); if (motion_devices) { motion_devices->SetCallback({ .on_change = @@ -81,7 +79,7 @@ void EmulatedConsole::ReloadInput() { // Unique index for identifying touch device source std::size_t index = 0; for (auto& touch_device : touch_devices) { - touch_device = Common::Input::CreateDevice(touch_params[index]); + touch_device = Common::Input::CreateInputDevice(touch_params[index]); if (!touch_device) { continue; } @@ -133,7 +131,7 @@ Common::ParamPackage EmulatedConsole::GetMotionParam() const { } void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { - motion_params = param; + motion_params = std::move(param); ReloadInput(); } @@ -178,12 +176,38 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { } void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) { - if (index >= console.touch_values.size()) { + if (index >= MaxTouchDevices) { return; } std::unique_lock lock{mutex}; - console.touch_values[index] = TransformToTouch(callback); + const auto touch_input = TransformToTouch(callback); + auto touch_index = GetIndexFromFingerId(index); + bool is_new_input = false; + + if (!touch_index.has_value() && touch_input.pressed.value) { + touch_index = GetNextFreeIndex(); + is_new_input = true; + } + + // No free entries or invalid state. Ignore input + if (!touch_index.has_value()) { + return; + } + + auto& touch_value = console.touch_values[touch_index.value()]; + + if (is_new_input) { + touch_value.pressed.value = true; + touch_value.id = static_cast(index); + } + + touch_value.x = touch_input.x; + touch_value.y = touch_input.y; + + if (!touch_input.pressed.value) { + touch_value.pressed.value = false; + } if (is_configuring) { lock.unlock(); @@ -191,11 +215,15 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st return; } - // TODO(german77): Remap touch id in sequential order - console.touch_state[index] = { - .position = {console.touch_values[index].x.value, console.touch_values[index].y.value}, - .id = static_cast(console.touch_values[index].id), - .pressed = console.touch_values[index].pressed.value, + // Touch outside allowed range. Ignore input + if (touch_index.value() >= MaxActiveTouchInputs) { + return; + } + + console.touch_state[touch_index.value()] = { + .position = {touch_value.x.value, touch_value.y.value}, + .id = static_cast(touch_index.value()), + .pressed = touch_input.pressed.value, }; lock.unlock(); @@ -222,6 +250,28 @@ TouchFingerState EmulatedConsole::GetTouch() const { return console.touch_state; } +std::optional EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const { + for (std::size_t index = 0; index < MaxTouchDevices; ++index) { + const auto& finger = console.touch_values[index]; + if (!finger.pressed.value) { + continue; + } + if (finger.id == static_cast(finger_id)) { + return index; + } + } + return std::nullopt; +} + +std::optional EmulatedConsole::GetNextFreeIndex() const { + for (std::size_t index = 0; index < MaxTouchDevices; ++index) { + if (!console.touch_values[index].pressed.value) { + return index; + } + } + return std::nullopt; +} + void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { std::scoped_lock lock{callback_mutex}; for (const auto& poller_pair : callback_list) { @@ -234,7 +284,7 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { std::scoped_lock lock{callback_mutex}; - callback_list.insert_or_assign(last_callback_key, update_callback); + callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); return last_callback_key++; } diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h index 1c510cd..697ecd2 100644 --- a/src/core/hid/emulated_console.h +++ b/src/core/hid/emulated_console.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "common/common_funcs.h" @@ -20,6 +21,8 @@ #include "core/hid/motion_input.h" namespace Core::HID { +static constexpr std::size_t MaxTouchDevices = 32; +static constexpr std::size_t MaxActiveTouchInputs = 16; struct ConsoleMotionInfo { Common::Input::MotionStatus raw_status{}; @@ -27,13 +30,13 @@ struct ConsoleMotionInfo { }; using ConsoleMotionDevices = std::unique_ptr; -using TouchDevices = std::array, 16>; +using TouchDevices = std::array, MaxTouchDevices>; using ConsoleMotionParams = Common::ParamPackage; -using TouchParams = std::array; +using TouchParams = std::array; using ConsoleMotionValues = ConsoleMotionInfo; -using TouchValues = std::array; +using TouchValues = std::array; struct TouchFinger { u64 last_touch{}; @@ -55,7 +58,7 @@ struct ConsoleMotion { bool is_at_rest{}; }; -using TouchFingerState = std::array; +using TouchFingerState = std::array; struct ConsoleStatus { // Data from input_common @@ -166,6 +169,10 @@ private: */ void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index); + std::optional GetIndexFromFingerId(std::size_t finger_id) const; + + std::optional GetNextFreeIndex() const; + /** * Triggers a callback that something has changed on the console status * @param type Input type of the event to trigger diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 01c43be..f238d6c 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + +#include "common/polyfill_ranges.h" #include "common/thread.h" #include "core/hid/emulated_controller.h" #include "core/hid/input_converter.h" @@ -93,7 +96,7 @@ void EmulatedController::ReloadFromSettings() { .body = GetNpadColor(player.body_color_left), .button = GetNpadColor(player.button_color_left), }; - controller.colors_state.left = { + controller.colors_state.right = { .body = GetNpadColor(player.body_color_right), .button = GetNpadColor(player.button_color_right), }; @@ -107,10 +110,9 @@ void EmulatedController::ReloadFromSettings() { original_npad_type = npad_type; } + Disconnect(); if (player.connected) { Connect(); - } else { - Disconnect(); } ReloadInput(); @@ -131,38 +133,43 @@ void EmulatedController::LoadDevices() { battery_params[RightIndex].Set("battery", true); camera_params = Common::ParamPackage{"engine:camera,camera:1"}; + nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; output_params[LeftIndex] = left_joycon; output_params[RightIndex] = right_joycon; output_params[2] = camera_params; + output_params[3] = nfc_params; output_params[LeftIndex].Set("output", true); output_params[RightIndex].Set("output", true); output_params[2].Set("output", true); + output_params[3].Set("output", true); LoadTASParams(); + LoadVirtualGamepadParams(); - std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, - button_params.begin() + Settings::NativeButton::BUTTON_NS_END, - button_devices.begin(), Common::Input::CreateDevice); - std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, - stick_params.begin() + Settings::NativeAnalog::STICK_HID_END, - stick_devices.begin(), Common::Input::CreateDevice); - std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, - motion_params.begin() + Settings::NativeMotion::MOTION_HID_END, - motion_devices.begin(), Common::Input::CreateDevice); - std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(), - Common::Input::CreateDevice); - std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(), - Common::Input::CreateDevice); - camera_devices = Common::Input::CreateDevice(camera_params); - std::transform(output_params.begin(), output_params.end(), output_devices.begin(), - Common::Input::CreateDevice); + std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice); + std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice); + std::ranges::transform(motion_params, motion_devices.begin(), Common::Input::CreateInputDevice); + std::ranges::transform(trigger_params, trigger_devices.begin(), + Common::Input::CreateInputDevice); + std::ranges::transform(battery_params, battery_devices.begin(), + Common::Input::CreateInputDevice); + camera_devices = Common::Input::CreateInputDevice(camera_params); + nfc_devices = Common::Input::CreateInputDevice(nfc_params); + std::ranges::transform(output_params, output_devices.begin(), + Common::Input::CreateOutputDevice); // Initialize TAS devices - std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(), - Common::Input::CreateDevice); - std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(), - Common::Input::CreateDevice); + std::ranges::transform(tas_button_params, tas_button_devices.begin(), + Common::Input::CreateInputDevice); + std::ranges::transform(tas_stick_params, tas_stick_devices.begin(), + Common::Input::CreateInputDevice); + + // Initialize virtual gamepad devices + std::ranges::transform(virtual_button_params, virtual_button_devices.begin(), + Common::Input::CreateInputDevice); + std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(), + Common::Input::CreateInputDevice); } void EmulatedController::LoadTASParams() { @@ -205,6 +212,46 @@ void EmulatedController::LoadTASParams() { tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); } +void EmulatedController::LoadVirtualGamepadParams() { + const auto player_index = NpadIdTypeToIndex(npad_id_type); + Common::ParamPackage common_params{}; + common_params.Set("engine", "virtual_gamepad"); + common_params.Set("port", static_cast(player_index)); + for (auto& param : virtual_button_params) { + param = common_params; + } + for (auto& param : virtual_stick_params) { + param = common_params; + } + + // TODO(german77): Replace this with an input profile or something better + virtual_button_params[Settings::NativeButton::A].Set("button", 0); + virtual_button_params[Settings::NativeButton::B].Set("button", 1); + virtual_button_params[Settings::NativeButton::X].Set("button", 2); + virtual_button_params[Settings::NativeButton::Y].Set("button", 3); + virtual_button_params[Settings::NativeButton::LStick].Set("button", 4); + virtual_button_params[Settings::NativeButton::RStick].Set("button", 5); + virtual_button_params[Settings::NativeButton::L].Set("button", 6); + virtual_button_params[Settings::NativeButton::R].Set("button", 7); + virtual_button_params[Settings::NativeButton::ZL].Set("button", 8); + virtual_button_params[Settings::NativeButton::ZR].Set("button", 9); + virtual_button_params[Settings::NativeButton::Plus].Set("button", 10); + virtual_button_params[Settings::NativeButton::Minus].Set("button", 11); + virtual_button_params[Settings::NativeButton::DLeft].Set("button", 12); + virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); + virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); + virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); + virtual_button_params[Settings::NativeButton::SL].Set("button", 16); + virtual_button_params[Settings::NativeButton::SR].Set("button", 17); + virtual_button_params[Settings::NativeButton::Home].Set("button", 18); + virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); + + virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); + virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); + virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); + virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); +} + void EmulatedController::ReloadInput() { // If you load any device here add the equivalent to the UnloadInput() function LoadDevices(); @@ -284,6 +331,16 @@ void EmulatedController::ReloadInput() { camera_devices->ForceUpdate(); } + if (nfc_devices) { + if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) { + nfc_devices->SetCallback({ + .on_change = + [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); }, + }); + nfc_devices->ForceUpdate(); + } + } + // Use a common UUID for TAS static constexpr Common::UUID TAS_UUID = Common::UUID{ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; @@ -312,6 +369,35 @@ void EmulatedController::ReloadInput() { }, }); } + + // Use a common UUID for Virtual Gamepad + static constexpr Common::UUID VIRTUAL_UUID = Common::UUID{ + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; + + // Register virtual devices. No need to force update + for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) { + if (!virtual_button_devices[index]) { + continue; + } + virtual_button_devices[index]->SetCallback({ + .on_change = + [this, index](const Common::Input::CallbackStatus& callback) { + SetButton(callback, index, VIRTUAL_UUID); + }, + }); + } + + for (std::size_t index = 0; index < virtual_stick_devices.size(); ++index) { + if (!virtual_stick_devices[index]) { + continue; + } + virtual_stick_devices[index]->SetCallback({ + .on_change = + [this, index](const Common::Input::CallbackStatus& callback) { + SetStick(callback, index, VIRTUAL_UUID); + }, + }); + } } void EmulatedController::UnloadInput() { @@ -339,6 +425,14 @@ void EmulatedController::UnloadInput() { for (auto& stick : tas_stick_devices) { stick.reset(); } + for (auto& button : virtual_button_devices) { + button.reset(); + } + for (auto& stick : virtual_stick_devices) { + stick.reset(); + } + camera_devices.reset(); + nfc_devices.reset(); } void EmulatedController::EnableConfiguration() { @@ -412,15 +506,14 @@ void EmulatedController::RestoreConfig() { ReloadFromSettings(); } -std::vector EmulatedController::GetMappedDevices( - EmulatedDeviceIndex device_index) const { +std::vector EmulatedController::GetMappedDevices() const { std::vector devices; for (const auto& param : button_params) { if (!param.Has("engine")) { continue; } const auto devices_it = std::find_if( - devices.begin(), devices.end(), [param](const Common::ParamPackage param_) { + devices.begin(), devices.end(), [¶m](const Common::ParamPackage& param_) { return param.Get("engine", "") == param_.Get("engine", "") && param.Get("guid", "") == param_.Get("guid", "") && param.Get("port", 0) == param_.Get("port", 0) && @@ -429,12 +522,12 @@ std::vector EmulatedController::GetMappedDevices( if (devices_it != devices.end()) { continue; } - Common::ParamPackage device{}; + + auto& device = devices.emplace_back(); device.Set("engine", param.Get("engine", "")); device.Set("guid", param.Get("guid", "")); device.Set("port", param.Get("port", 0)); device.Set("pad", param.Get("pad", 0)); - devices.push_back(device); } for (const auto& param : stick_params) { @@ -445,7 +538,7 @@ std::vector EmulatedController::GetMappedDevices( continue; } const auto devices_it = std::find_if( - devices.begin(), devices.end(), [param](const Common::ParamPackage param_) { + devices.begin(), devices.end(), [¶m](const Common::ParamPackage& param_) { return param.Get("engine", "") == param_.Get("engine", "") && param.Get("guid", "") == param_.Get("guid", "") && param.Get("port", 0) == param_.Get("port", 0) && @@ -454,12 +547,12 @@ std::vector EmulatedController::GetMappedDevices( if (devices_it != devices.end()) { continue; } - Common::ParamPackage device{}; + + auto& device = devices.emplace_back(); device.Set("engine", param.Get("engine", "")); device.Set("guid", param.Get("guid", "")); device.Set("port", param.Get("port", 0)); device.Set("pad", param.Get("pad", 0)); - devices.push_back(device); } return devices; } @@ -903,6 +996,25 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback TriggerOnChange(ControllerTriggerType::IrSensor, true); } +void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { + std::unique_lock lock{mutex}; + controller.nfc_values = TransformToNfc(callback); + + if (is_configuring) { + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Nfc, false); + return; + } + + controller.nfc_state = { + controller.nfc_values.state, + controller.nfc_values.data, + }; + + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Nfc, true); +} + bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { if (device_index >= output_devices.size()) { return false; @@ -935,14 +1047,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v Common::Input::VibrationError::None; } -bool EmulatedController::TestVibration(std::size_t device_index) { - if (device_index >= output_devices.size()) { - return false; - } - if (!output_devices[device_index]) { - return false; - } - +bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { const auto player_index = NpadIdTypeToIndex(npad_id_type); const auto& player = Settings::values.players.GetValue()[player_index]; @@ -950,37 +1055,27 @@ bool EmulatedController::TestVibration(std::size_t device_index) { return false; } - const Common::Input::VibrationStatus test_vibration = { - .low_amplitude = 0.001f, - .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency, - .high_amplitude = 0.001f, - .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency, - .type = Common::Input::VibrationAmplificationType::Test, - }; + if (device_index >= output_devices.size()) { + return false; + } - const Common::Input::VibrationStatus zero_vibration = { - .low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude, - .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency, - .high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude, - .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency, - .type = Common::Input::VibrationAmplificationType::Test, - }; + if (!output_devices[device_index]) { + return false; + } - // Send a slight vibration to test for rumble support - output_devices[device_index]->SetVibration(test_vibration); - - // Wait for about 15ms to ensure the controller is ready for the stop command - std::this_thread::sleep_for(std::chrono::milliseconds(15)); - - // Stop any vibration and return the result - return output_devices[device_index]->SetVibration(zero_vibration) == - Common::Input::VibrationError::None; + return output_devices[device_index]->IsVibrationEnabled(); } bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); auto& output_device = output_devices[static_cast(DeviceIndex::Right)]; - return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; + auto& nfc_output_device = output_devices[3]; + + const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); + const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode); + + return virtual_nfc_result == Common::Input::PollingError::None || + mapped_nfc_result == Common::Input::PollingError::None; } bool EmulatedController::SetCameraFormat( @@ -1000,6 +1095,33 @@ bool EmulatedController::SetCameraFormat( camera_format)) == Common::Input::CameraError::None; } +bool EmulatedController::HasNfc() const { + const auto& nfc_output_device = output_devices[3]; + + switch (npad_type) { + case NpadStyleIndex::JoyconRight: + case NpadStyleIndex::JoyconDual: + case NpadStyleIndex::ProController: + case NpadStyleIndex::Handheld: + break; + default: + return false; + } + + const bool has_virtual_nfc = + npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld; + const bool is_virtual_nfc_supported = + nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported; + + return is_connected && (has_virtual_nfc && is_virtual_nfc_supported); +} + +bool EmulatedController::WriteNfc(const std::vector& data) { + auto& nfc_output_device = output_devices[3]; + + return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; +} + void EmulatedController::SetLedPattern() { for (auto& device : output_devices) { if (!device) { @@ -1091,27 +1213,27 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; switch (type) { case NpadStyleIndex::ProController: - return supported_style_tag.fullkey; + return supported_style_tag.fullkey.As(); case NpadStyleIndex::Handheld: - return supported_style_tag.handheld; + return supported_style_tag.handheld.As(); case NpadStyleIndex::JoyconDual: - return supported_style_tag.joycon_dual; + return supported_style_tag.joycon_dual.As(); case NpadStyleIndex::JoyconLeft: - return supported_style_tag.joycon_left; + return supported_style_tag.joycon_left.As(); case NpadStyleIndex::JoyconRight: - return supported_style_tag.joycon_right; + return supported_style_tag.joycon_right.As(); case NpadStyleIndex::GameCube: - return supported_style_tag.gamecube; + return supported_style_tag.gamecube.As(); case NpadStyleIndex::Pokeball: - return supported_style_tag.palma; + return supported_style_tag.palma.As(); case NpadStyleIndex::NES: - return supported_style_tag.lark; + return supported_style_tag.lark.As(); case NpadStyleIndex::SNES: - return supported_style_tag.lucia; + return supported_style_tag.lucia.As(); case NpadStyleIndex::N64: - return supported_style_tag.lagoon; + return supported_style_tag.lagoon.As(); case NpadStyleIndex::SegaGenesis: - return supported_style_tag.lager; + return supported_style_tag.lager.As(); default: return false; } @@ -1167,12 +1289,6 @@ bool EmulatedController::IsConnected(bool get_temporary_value) const { return is_connected; } -bool EmulatedController::IsVibrationEnabled() const { - const auto player_index = NpadIdTypeToIndex(npad_id_type); - const auto& player = Settings::values.players.GetValue()[player_index]; - return player.vibration_enabled; -} - NpadIdType EmulatedController::GetNpadIdType() const { std::scoped_lock lock{mutex}; return npad_id_type; @@ -1363,6 +1479,11 @@ const CameraState& EmulatedController::GetCamera() const { return controller.camera_state; } +const NfcState& EmulatedController::GetNfc() const { + std::scoped_lock lock{mutex}; + return controller.nfc_state; +} + NpadColor EmulatedController::GetNpadColor(u32 color) { return { .r = static_cast((color >> 16) & 0xFF), diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index c3aa8f9..a398543 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "common/common_types.h" #include "common/input.h" @@ -20,7 +21,7 @@ namespace Core::HID { const std::size_t max_emulated_controllers = 2; -const std::size_t output_devices = 3; +const std::size_t output_devices_size = 4; struct ControllerMotionInfo { Common::Input::MotionStatus raw_status{}; MotionInput emulated{}; @@ -37,7 +38,8 @@ using TriggerDevices = using BatteryDevices = std::array, max_emulated_controllers>; using CameraDevices = std::unique_ptr; -using OutputDevices = std::array, output_devices>; +using NfcDevices = std::unique_ptr; +using OutputDevices = std::array, output_devices_size>; using ButtonParams = std::array; using StickParams = std::array; @@ -45,7 +47,8 @@ using ControllerMotionParams = std::array; using BatteryParams = std::array; using CameraParams = Common::ParamPackage; -using OutputParams = std::array; +using NfcParams = Common::ParamPackage; +using OutputParams = std::array; using ButtonValues = std::array; using SticksValues = std::array; @@ -55,6 +58,7 @@ using ControllerMotionValues = std::array; using BatteryValues = std::array; using CameraValues = Common::Input::CameraStatus; +using NfcValues = Common::Input::NfcStatus; using VibrationValues = std::array; struct AnalogSticks { @@ -80,6 +84,11 @@ struct CameraState { std::size_t sample{}; }; +struct NfcState { + Common::Input::NfcState state{}; + std::vector data{}; +}; + struct ControllerMotion { Common::Vec3f accel{}; Common::Vec3f gyro{}; @@ -107,6 +116,7 @@ struct ControllerStatus { BatteryValues battery_values{}; VibrationValues vibration_values{}; CameraValues camera_values{}; + NfcValues nfc_values{}; // Data for HID serices HomeButtonState home_button_state{}; @@ -119,6 +129,7 @@ struct ControllerStatus { ControllerColors colors_state{}; BatteryLevelState battery_state{}; CameraState camera_state{}; + NfcState nfc_state{}; }; enum class ControllerTriggerType { @@ -130,6 +141,7 @@ enum class ControllerTriggerType { Battery, Vibration, IrSensor, + Nfc, Connected, Disconnected, Type, @@ -195,9 +207,6 @@ public: */ bool IsConnected(bool get_temporary_value = false) const; - /// Returns true if vibration is enabled - bool IsVibrationEnabled() const; - /// Removes all callbacks created from input devices void UnloadInput(); @@ -235,7 +244,7 @@ public: void RestoreConfig(); /// Returns a vector of mapped devices from the mapped button and stick parameters - std::vector GetMappedDevices(EmulatedDeviceIndex device_index) const; + std::vector GetMappedDevices() const; // Returns the current mapped button device Common::ParamPackage GetButtonParam(std::size_t index) const; @@ -315,6 +324,9 @@ public: /// Returns the latest camera status from the controller const CameraState& GetCamera() const; + /// Returns the latest ntag status from the controller + const NfcState& GetNfc() const; + /** * Sends a specific vibration to the output device * @return true if vibration had no errors @@ -325,7 +337,7 @@ public: * Sends a small vibration to the output device * @return true if SetVibration was successfull */ - bool TestVibration(std::size_t device_index); + bool IsVibrationEnabled(std::size_t device_index); /** * Sets the desired data to be polled from a controller @@ -341,6 +353,12 @@ public: */ bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); + /// Returns true if the device has nfc support + bool HasNfc() const; + + /// Returns true if the nfc tag was written + bool WriteNfc(const std::vector& data); + /// Returns the led pattern corresponding to this emulated controller LedPattern GetLedPattern() const; @@ -367,6 +385,9 @@ private: /// Set the params for TAS devices void LoadTASParams(); + /// Set the params for virtual pad devices + void LoadVirtualGamepadParams(); + /** * @param use_temporary_value If true tmp_npad_type will be used * @return true if the controller style is fullkey @@ -424,6 +445,12 @@ private: */ void SetCamera(const Common::Input::CallbackStatus& callback); + /** + * Updates the nfc status of the controller + * @param callback A CallbackStatus containing the nfc status + */ + void SetNfc(const Common::Input::CallbackStatus& callback); + /** * Converts a color format from bgra to rgba * @param color in bgra format @@ -458,6 +485,7 @@ private: TriggerParams trigger_params; BatteryParams battery_params; CameraParams camera_params; + NfcParams nfc_params; OutputParams output_params; ButtonDevices button_devices; @@ -466,6 +494,7 @@ private: TriggerDevices trigger_devices; BatteryDevices battery_devices; CameraDevices camera_devices; + NfcDevices nfc_devices; OutputDevices output_devices; // TAS related variables @@ -474,6 +503,12 @@ private: ButtonDevices tas_button_devices; StickDevices tas_stick_devices; + // Virtual gamepad related variables + ButtonParams virtual_button_params; + StickParams virtual_stick_params; + ButtonDevices virtual_button_devices; + StickDevices virtual_stick_devices; + mutable std::mutex mutex; mutable std::mutex callback_mutex; std::unordered_map callback_list; diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp index 8d367b5..e421828 100644 --- a/src/core/hid/emulated_devices.cpp +++ b/src/core/hid/emulated_devices.cpp @@ -25,12 +25,12 @@ void EmulatedDevices::ReloadInput() { Common::ParamPackage mouse_params; mouse_params.Set("engine", "mouse"); mouse_params.Set("button", static_cast(key_index)); - mouse_device = Common::Input::CreateDevice(mouse_params); + mouse_device = Common::Input::CreateInputDevice(mouse_params); key_index++; } - mouse_stick_device = Common::Input::CreateDeviceFromString( - "engine:mouse,axis_x:0,axis_y:1"); + mouse_stick_device = + Common::Input::CreateInputDeviceFromString("engine:mouse,axis_x:0,axis_y:1"); // First two axis are reserved for mouse position key_index = 2; @@ -38,7 +38,7 @@ void EmulatedDevices::ReloadInput() { Common::ParamPackage mouse_params; mouse_params.Set("engine", "mouse"); mouse_params.Set("axis", static_cast(key_index)); - mouse_device = Common::Input::CreateDevice(mouse_params); + mouse_device = Common::Input::CreateInputDevice(mouse_params); key_index++; } @@ -50,7 +50,7 @@ void EmulatedDevices::ReloadInput() { keyboard_params.Set("button", static_cast(key_index)); keyboard_params.Set("port", 1); keyboard_params.Set("pad", 0); - keyboard_device = Common::Input::CreateDevice(keyboard_params); + keyboard_device = Common::Input::CreateInputDevice(keyboard_params); key_index++; } @@ -62,11 +62,11 @@ void EmulatedDevices::ReloadInput() { keyboard_params.Set("button", static_cast(key_index)); keyboard_params.Set("port", 1); keyboard_params.Set("pad", 1); - keyboard_device = Common::Input::CreateDevice(keyboard_params); + keyboard_device = Common::Input::CreateInputDevice(keyboard_params); key_index++; } - ring_analog_device = Common::Input::CreateDevice(ring_params); + ring_analog_device = Common::Input::CreateInputDevice(ring_params); for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { if (!mouse_button_devices[index]) { @@ -145,6 +145,7 @@ void EmulatedDevices::UnloadInput() { for (auto& button : keyboard_modifier_devices) { button.reset(); } + ring_analog_device.reset(); } void EmulatedDevices::EnableConfiguration() { diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h index 4149eec..4cdbf9d 100644 --- a/src/core/hid/emulated_devices.h +++ b/src/core/hid/emulated_devices.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "common/common_types.h" #include "common/input.h" diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index 52fb69e..5026928 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp @@ -200,9 +200,6 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& x = std::clamp(x, 0.0f, 1.0f); y = std::clamp(y, 0.0f, 1.0f); - // Limit id to maximum number of fingers - status.id = std::clamp(status.id, 0, 16); - if (status.pressed.inverted) { status.pressed.value = !status.pressed.value; } @@ -277,7 +274,10 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu Common::Input::CameraStatus camera{}; switch (callback.type) { case Common::Input::InputType::IrSensor: - camera = callback.camera_status; + camera = { + .format = callback.camera_status, + .data = callback.raw_data, + }; break; default: LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type); @@ -287,6 +287,23 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu return camera; } +Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) { + Common::Input::NfcStatus nfc{}; + switch (callback.type) { + case Common::Input::InputType::Nfc: + nfc = { + .state = callback.nfc_status, + .data = callback.raw_data, + }; + break; + default: + LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); + break; + } + + return nfc; +} + void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { const auto& properties = analog.properties; float& raw_value = analog.raw_value; diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h index 143c50c..b7eb6e6 100644 --- a/src/core/hid/input_converter.h +++ b/src/core/hid/input_converter.h @@ -84,6 +84,14 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu */ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); +/** + * Converts raw input data into a valid nfc status. + * + * @param callback Supported callbacks: Nfc. + * @return A valid CameraObject object. + */ +Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); + /** * Converts raw analog data into a valid analog value * @param analog An analog object containing raw data and properties diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h index 88c5b01..0d1bfe5 100644 --- a/src/core/hid/irs_types.h +++ b/src/core/hid/irs_types.h @@ -14,7 +14,7 @@ enum class CameraAmbientNoiseLevel : u32 { Low, Medium, High, - Unkown3, // This level can't be reached + Unknown3, // This level can't be reached }; // This is nn::irsensor::CameraLightTarget @@ -75,9 +75,9 @@ enum class IrCameraStatus : u32 { enum class IrCameraInternalStatus : u32 { Stopped, FirmwareUpdateNeeded, - Unkown2, - Unkown3, - Unkown4, + Unknown2, + Unknown3, + Unknown4, FirmwareVersionRequested, FirmwareVersionIsInvalid, Ready, @@ -121,20 +121,20 @@ enum class IrSensorFunctionLevel : u8 { // This is nn::irsensor::MomentProcessorPreprocess enum class MomentProcessorPreprocess : u32 { - Unkown0, - Unkown1, + Unknown0, + Unknown1, }; // This is nn::irsensor::PackedMomentProcessorPreprocess enum class PackedMomentProcessorPreprocess : u8 { - Unkown0, - Unkown1, + Unknown0, + Unknown1, }; // This is nn::irsensor::PointingStatus enum class PointingStatus : u32 { - Unkown0, - Unkown1, + Unknown0, + Unknown1, }; struct IrsRect { diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index d631c03..a86bec2 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -86,13 +86,13 @@ public: u32 num_domain_objects{}; const bool always_move_handles{ (static_cast(flags) & static_cast(Flags::AlwaysMoveHandles)) != 0}; - if (!ctx.Session()->IsDomain() || always_move_handles) { + if (!ctx.GetManager()->IsDomain() || always_move_handles) { num_handles_to_move = num_objects_to_move; } else { num_domain_objects = num_objects_to_move; } - if (ctx.Session()->IsDomain()) { + if (ctx.GetManager()->IsDomain()) { raw_data_size += static_cast(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects); ctx.write_size += num_domain_objects; @@ -125,7 +125,7 @@ public: if (!ctx.IsTipc()) { AlignWithPadding(); - if (ctx.Session()->IsDomain() && ctx.HasDomainMessageHeader()) { + if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) { IPC::DomainMessageHeader domain_header{}; domain_header.num_objects = num_domain_objects; PushRaw(domain_header); @@ -145,17 +145,18 @@ public: template void PushIpcInterface(std::shared_ptr iface) { - if (context->Session()->IsDomain()) { + if (context->GetManager()->IsDomain()) { context->AddDomainObject(std::move(iface)); } else { kernel.CurrentProcess()->GetResourceLimit()->Reserve( - Kernel::LimitableResource::Sessions, 1); + Kernel::LimitableResource::SessionCountMax, 1); auto* session = Kernel::KSession::Create(kernel); session->Initialize(nullptr, iface->GetServiceName()); + iface->RegisterSession(&session->GetServerSession(), + std::make_shared(kernel)); context->AddMoveObject(&session->GetClientSession()); - iface->ClientConnected(&session->GetServerSession()); } } @@ -385,7 +386,7 @@ public: template std::weak_ptr PopIpcInterface() { - ASSERT(context->Session()->IsDomain()); + ASSERT(context->GetManager()->IsDomain()); ASSERT(context->GetDomainMessageHeader().input_object_count > 0); return context->GetDomainHandler(Pop() - 1); } @@ -404,7 +405,7 @@ inline s32 RequestParser::Pop() { } // Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects. -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" #endif @@ -415,7 +416,7 @@ void RequestParser::PopRaw(T& value) { std::memcpy(&value, cmdbuf + index, sizeof(T)); index += (sizeof(T) + 3) / 4; // round up to word length } -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) #pragma GCC diagnostic pop #endif diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h index fe37576..4b717d0 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h @@ -8,6 +8,10 @@ namespace Kernel::Board::Nintendo::Nx { class KSystemControl { +public: + // This can be overridden as needed. + static constexpr size_t SecureAppletMemorySize = 4 * 1024 * 1024; // 4_MB + public: class Init { public: diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp index 65576b8..fd911a3 100644 --- a/src/core/hle/kernel/global_scheduler_context.cpp +++ b/src/core/hle/kernel/global_scheduler_context.cpp @@ -49,4 +49,26 @@ bool GlobalSchedulerContext::IsLocked() const { return scheduler_lock.IsLockedByCurrentThread(); } +void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) { + ASSERT(IsLocked()); + + woken_dummy_threads.insert(thread); +} + +void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) { + ASSERT(IsLocked()); + + woken_dummy_threads.erase(thread); +} + +void GlobalSchedulerContext::WakeupWaitingDummyThreads() { + ASSERT(IsLocked()); + + for (auto* thread : woken_dummy_threads) { + thread->DummyThreadEndWait(); + } + + woken_dummy_threads.clear(); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h index 67bb985..220ed61 100644 --- a/src/core/hle/kernel/global_scheduler_context.h +++ b/src/core/hle/kernel/global_scheduler_context.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include "common/common_types.h" @@ -58,6 +59,10 @@ public: /// Returns true if the global scheduler lock is acquired bool IsLocked() const; + void UnregisterDummyThreadForWakeup(KThread* thread); + void RegisterDummyThreadForWakeup(KThread* thread); + void WakeupWaitingDummyThreads(); + [[nodiscard]] LockType& SchedulerLock() { return scheduler_lock; } @@ -76,6 +81,9 @@ private: KSchedulerPriorityQueue priority_queue; LockType scheduler_lock; + /// Lists dummy threads pending wakeup on lock release + std::set woken_dummy_threads; + /// Lists all thread ids that aren't deleted/etc. std::vector thread_list; std::mutex global_list_guard; diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 5b3feec..738b6d0 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -16,27 +16,39 @@ #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_server_port.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/service_thread.h" #include "core/memory.h" namespace Kernel { SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_, ServiceThreadType thread_type) - : kernel{kernel_} { - if (thread_type == ServiceThreadType::CreateNew) { - service_thread = kernel.CreateServiceThread(service_name_); - } else { - service_thread = kernel.GetDefaultServiceThread(); - } -} + : kernel{kernel_}, service_thread{thread_type == ServiceThreadType::CreateNew + ? kernel.CreateServiceThread(service_name_) + : kernel.GetDefaultServiceThread()} {} SessionRequestHandler::~SessionRequestHandler() { kernel.ReleaseServiceThread(service_thread); } +void SessionRequestHandler::AcceptSession(KServerPort* server_port) { + auto* server_session = server_port->AcceptSession(); + ASSERT(server_session != nullptr); + + RegisterSession(server_session, std::make_shared(kernel)); +} + +void SessionRequestHandler::RegisterSession(KServerSession* server_session, + std::shared_ptr manager) { + manager->SetSessionHandler(shared_from_this()); + service_thread.RegisterServerSession(server_session, manager); + server_session->Close(); +} + SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {} SessionRequestManager::~SessionRequestManager() = default; @@ -56,15 +68,77 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co } } -void SessionRequestHandler::ClientConnected(KServerSession* session) { - session->ClientConnected(shared_from_this()); +Result SessionRequestManager::CompleteSyncRequest(KServerSession* server_session, + HLERequestContext& context) { + Result result = ResultSuccess; - // Ensure our server session is tracked globally. - kernel.RegisterServerObject(session); + // If the session has been converted to a domain, handle the domain request + if (this->HasSessionRequestHandler(context)) { + if (IsDomain() && context.HasDomainMessageHeader()) { + result = HandleDomainSyncRequest(server_session, context); + // If there is no domain header, the regular session handler is used + } else if (this->HasSessionHandler()) { + // If this manager has an associated HLE handler, forward the request to it. + result = this->SessionHandler().HandleSyncRequest(*server_session, context); + } + } else { + ASSERT_MSG(false, "Session handler is invalid, stubbing response!"); + IPC::ResponseBuilder rb(context, 2); + rb.Push(ResultSuccess); + } + + if (convert_to_domain) { + ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance."); + this->ConvertToDomain(); + convert_to_domain = false; + } + + return result; } -void SessionRequestHandler::ClientDisconnected(KServerSession* session) { - session->ClientDisconnected(); +Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_session, + HLERequestContext& context) { + if (!context.HasDomainMessageHeader()) { + return ResultSuccess; + } + + // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs + ASSERT(context.GetManager().get() == this); + + // If there is a DomainMessageHeader, then this is CommandType "Request" + const auto& domain_message_header = context.GetDomainMessageHeader(); + const u32 object_id{domain_message_header.object_id}; + switch (domain_message_header.command) { + case IPC::DomainMessageHeader::CommandType::SendMessage: + if (object_id > this->DomainHandlerCount()) { + LOG_CRITICAL(IPC, + "object_id {} is too big! This probably means a recent service call " + "needed to return a new interface!", + object_id); + ASSERT(false); + return ResultSuccess; // Ignore error if asserts are off + } + if (auto strong_ptr = this->DomainHandler(object_id - 1).lock()) { + return strong_ptr->HandleSyncRequest(*server_session, context); + } else { + ASSERT(false); + return ResultSuccess; + } + + case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { + LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); + + this->CloseDomainHandler(object_id - 1); + + IPC::ResponseBuilder rb{context, 2}; + rb.Push(ResultSuccess); + return ResultSuccess; + } + } + + LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value()); + ASSERT(false); + return ResultSuccess; } HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, @@ -93,6 +167,9 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 } if (incoming) { // Populate the object lists with the data in the IPC request. + incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy); + incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move); + for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { incoming_copy_handles.push_back(rp.Pop()); } @@ -107,6 +184,11 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 } } + buffer_x_desciptors.reserve(command_header->num_buf_x_descriptors); + buffer_a_desciptors.reserve(command_header->num_buf_a_descriptors); + buffer_b_desciptors.reserve(command_header->num_buf_b_descriptors); + buffer_w_desciptors.reserve(command_header->num_buf_w_descriptors); + for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) { buffer_x_desciptors.push_back(rp.PopRaw()); } @@ -126,7 +208,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 // Padding to align to 16 bytes rp.AlignWithPadding(); - if (Session()->IsDomain() && + if (GetManager()->IsDomain() && ((command_header->type == IPC::CommandType::Request || command_header->type == IPC::CommandType::RequestWithContext) || !incoming)) { @@ -135,7 +217,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 if (incoming || domain_message_header) { domain_message_header = rp.PopRaw(); } else { - if (Session()->IsDomain()) { + if (GetManager()->IsDomain()) { LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); } } @@ -228,12 +310,11 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa // Write the domain objects to the command buffer, these go after the raw untranslated data. // TODO(Subv): This completely ignores C buffers. - if (Session()->IsDomain()) { + if (GetManager()->IsDomain()) { current_offset = domain_offset - static_cast(outgoing_domain_objects.size()); - for (const auto& object : outgoing_domain_objects) { - server_session->AppendDomainHandler(object); - cmd_buf[current_offset++] = - static_cast(server_session->NumDomainRequestHandlers()); + for (auto& object : outgoing_domain_objects) { + GetManager()->AppendDomainHandler(std::move(object)); + cmd_buf[current_offset++] = static_cast(GetManager()->DomainHandlerCount()); } } @@ -245,25 +326,23 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa } std::vector HLERequestContext::ReadBuffer(std::size_t buffer_index) const { - std::vector buffer{}; const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && BufferDescriptorA()[buffer_index].Size()}; - if (is_buffer_a) { ASSERT_OR_EXECUTE_MSG( - BufferDescriptorA().size() > buffer_index, { return buffer; }, + BufferDescriptorA().size() > buffer_index, { return {}; }, "BufferDescriptorA invalid buffer_index {}", buffer_index); - buffer.resize(BufferDescriptorA()[buffer_index].Size()); + std::vector buffer(BufferDescriptorA()[buffer_index].Size()); memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); + return buffer; } else { ASSERT_OR_EXECUTE_MSG( - BufferDescriptorX().size() > buffer_index, { return buffer; }, + BufferDescriptorX().size() > buffer_index, { return {}; }, "BufferDescriptorX invalid buffer_index {}", buffer_index); - buffer.resize(BufferDescriptorX()[buffer_index].Size()); + std::vector buffer(BufferDescriptorX()[buffer_index].Size()); memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); + return buffer; } - - return buffer; } std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 99265ce..e252b5f 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -43,13 +43,15 @@ class Domain; class HLERequestContext; class KAutoObject; class KernelCore; +class KEvent; class KHandleTable; +class KServerPort; class KProcess; class KServerSession; class KThread; class KReadableEvent; class KSession; -class KWritableEvent; +class SessionRequestManager; class ServiceThread; enum class ThreadWakeupReason; @@ -76,27 +78,17 @@ public: virtual Result HandleSyncRequest(Kernel::KServerSession& session, Kernel::HLERequestContext& context) = 0; - /** - * Signals that a client has just connected to this HLE handler and keeps the - * associated ServerSession alive for the duration of the connection. - * @param server_session Owning pointer to the ServerSession associated with the connection. - */ - void ClientConnected(KServerSession* session); + void AcceptSession(KServerPort* server_port); + void RegisterSession(KServerSession* server_session, + std::shared_ptr manager); - /** - * Signals that a client has just disconnected from this HLE handler and releases the - * associated ServerSession. - * @param server_session ServerSession associated with the connection. - */ - void ClientDisconnected(KServerSession* session); - - std::weak_ptr GetServiceThread() const { + ServiceThread& GetServiceThread() const { return service_thread; } protected: KernelCore& kernel; - std::weak_ptr service_thread; + ServiceThread& service_thread; }; using SessionRequestHandlerWeakPtr = std::weak_ptr; @@ -121,6 +113,10 @@ public: is_domain = true; } + void ConvertToDomainOnRequestEnd() { + convert_to_domain = true; + } + std::size_t DomainHandlerCount() const { return domain_handlers.size(); } @@ -158,13 +154,17 @@ public: session_handler = std::move(handler); } - std::weak_ptr GetServiceThread() const { + ServiceThread& GetServiceThread() const { return session_handler->GetServiceThread(); } bool HasSessionRequestHandler(const HLERequestContext& context) const; + Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context); + Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context); + private: + bool convert_to_domain{}; bool is_domain{}; SessionRequestHandlerPtr session_handler; std::vector domain_handlers; @@ -199,7 +199,7 @@ public: ~HLERequestContext(); /// Returns a pointer to the IPC command buffer for this request. - u32* CommandBuffer() { + [[nodiscard]] u32* CommandBuffer() { return cmd_buf.data(); } @@ -207,7 +207,7 @@ public: * Returns the session through which this request was made. This can be used as a map key to * access per-client data on services. */ - Kernel::KServerSession* Session() { + [[nodiscard]] Kernel::KServerSession* Session() { return server_session; } @@ -217,61 +217,61 @@ public: /// Writes data from this context back to the requesting process/thread. Result WriteToOutgoingCommandBuffer(KThread& requesting_thread); - u32_le GetHipcCommand() const { + [[nodiscard]] u32_le GetHipcCommand() const { return command; } - u32_le GetTipcCommand() const { + [[nodiscard]] u32_le GetTipcCommand() const { return static_cast(command_header->type.Value()) - static_cast(IPC::CommandType::TIPC_CommandRegion); } - u32_le GetCommand() const { + [[nodiscard]] u32_le GetCommand() const { return command_header->IsTipc() ? GetTipcCommand() : GetHipcCommand(); } - bool IsTipc() const { + [[nodiscard]] bool IsTipc() const { return command_header->IsTipc(); } - IPC::CommandType GetCommandType() const { + [[nodiscard]] IPC::CommandType GetCommandType() const { return command_header->type; } - u64 GetPID() const { + [[nodiscard]] u64 GetPID() const { return pid; } - u32 GetDataPayloadOffset() const { + [[nodiscard]] u32 GetDataPayloadOffset() const { return data_payload_offset; } - const std::vector& BufferDescriptorX() const { + [[nodiscard]] const std::vector& BufferDescriptorX() const { return buffer_x_desciptors; } - const std::vector& BufferDescriptorA() const { + [[nodiscard]] const std::vector& BufferDescriptorA() const { return buffer_a_desciptors; } - const std::vector& BufferDescriptorB() const { + [[nodiscard]] const std::vector& BufferDescriptorB() const { return buffer_b_desciptors; } - const std::vector& BufferDescriptorC() const { + [[nodiscard]] const std::vector& BufferDescriptorC() const { return buffer_c_desciptors; } - const IPC::DomainMessageHeader& GetDomainMessageHeader() const { + [[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const { return domain_message_header.value(); } - bool HasDomainMessageHeader() const { + [[nodiscard]] bool HasDomainMessageHeader() const { return domain_message_header.has_value(); } /// Helper function to read a buffer using the appropriate buffer descriptor - std::vector ReadBuffer(std::size_t buffer_index = 0) const; + [[nodiscard]] std::vector ReadBuffer(std::size_t buffer_index = 0) const; /// Helper function to write a buffer using the appropriate buffer descriptor std::size_t WriteBuffer(const void* buffer, std::size_t size, @@ -295,7 +295,7 @@ public: */ template >> std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { - if constexpr (Common::IsSTLContainer) { + if constexpr (Common::IsContiguousContainer) { using ContiguousType = typename T::value_type; static_assert(std::is_trivially_copyable_v, "Container to WriteBuffer must contain trivially copyable objects"); @@ -308,22 +308,34 @@ public: } /// Helper function to get the size of the input buffer - std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const; + [[nodiscard]] std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const; /// Helper function to get the size of the output buffer - std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const; + [[nodiscard]] std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const; + + /// Helper function to derive the number of elements able to be contained in the read buffer + template + [[nodiscard]] std::size_t GetReadBufferNumElements(std::size_t buffer_index = 0) const { + return GetReadBufferSize(buffer_index) / sizeof(T); + } + + /// Helper function to derive the number of elements able to be contained in the write buffer + template + [[nodiscard]] std::size_t GetWriteBufferNumElements(std::size_t buffer_index = 0) const { + return GetWriteBufferSize(buffer_index) / sizeof(T); + } /// Helper function to test whether the input buffer at buffer_index can be read - bool CanReadBuffer(std::size_t buffer_index = 0) const; + [[nodiscard]] bool CanReadBuffer(std::size_t buffer_index = 0) const; /// Helper function to test whether the output buffer at buffer_index can be written - bool CanWriteBuffer(std::size_t buffer_index = 0) const; + [[nodiscard]] bool CanWriteBuffer(std::size_t buffer_index = 0) const; - Handle GetCopyHandle(std::size_t index) const { + [[nodiscard]] Handle GetCopyHandle(std::size_t index) const { return incoming_copy_handles.at(index); } - Handle GetMoveHandle(std::size_t index) const { + [[nodiscard]] Handle GetMoveHandle(std::size_t index) const { return incoming_move_handles.at(index); } @@ -341,19 +353,23 @@ public: template std::shared_ptr GetDomainHandler(std::size_t index) const { - return std::static_pointer_cast(manager.lock()->DomainHandler(index).lock()); + return std::static_pointer_cast(GetManager()->DomainHandler(index).lock()); } void SetSessionRequestManager(std::weak_ptr manager_) { - manager = std::move(manager_); + manager = manager_; } - std::string Description() const; + [[nodiscard]] std::string Description() const; - KThread& GetThread() { + [[nodiscard]] KThread& GetThread() { return *thread; } + [[nodiscard]] std::shared_ptr GetManager() const { + return manager.lock(); + } + private: friend class IPC::ResponseBuilder; @@ -387,7 +403,7 @@ private: u32 handles_offset{}; u32 domain_offset{}; - std::weak_ptr manager; + std::weak_ptr manager{}; KernelCore& kernel; Core::Memory::Memory& memory; diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index 9b6b284..7b363eb 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -10,7 +10,9 @@ #include "core/hardware_properties.h" #include "core/hle/kernel/init/init_slab_setup.h" #include "core/hle/kernel/k_code_memory.h" +#include "core/hle/kernel/k_debug.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_event_info.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_page_buffer.h" @@ -18,9 +20,11 @@ #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_session.h" +#include "core/hle/kernel/k_session_request.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory_info.h" #include "core/hle/kernel/k_system_control.h" +#include "core/hle/kernel/k_system_resource.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread_local_page.h" #include "core/hle/kernel/k_transfer_memory.h" @@ -34,6 +38,7 @@ namespace Kernel::Init { HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \ HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ + HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ##__VA_ARGS__) \ HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ @@ -42,7 +47,10 @@ namespace Kernel::Init { HANDLER(KThreadLocalPage, \ (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \ ##__VA_ARGS__) \ - HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) + HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) \ + HANDLER(KEventInfo, (SLAB_COUNT(KThread) + SLAB_COUNT(KDebug)), ##__VA_ARGS__) \ + HANDLER(KDebug, (SLAB_COUNT(KDebug)), ##__VA_ARGS__) \ + HANDLER(KSecureSystemResource, (SLAB_COUNT(KProcess)), ##__VA_ARGS__) namespace { @@ -71,8 +79,20 @@ constexpr size_t SlabCountKResourceLimit = 5; constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES; constexpr size_t SlabCountKIoPool = 1; constexpr size_t SlabCountKIoRegion = 6; +constexpr size_t SlabcountKSessionRequestMappings = 40; -constexpr size_t SlabCountExtraKThread = 160; +constexpr size_t SlabCountExtraKThread = (1024 + 256 + 256) - SlabCountKThread; + +namespace test { + +static_assert(KernelPageBufferHeapSize == + 2 * PageSize + (SlabCountKProcess + SlabCountKThread + + (SlabCountKProcess + SlabCountKThread) / 8) * + PageSize); +static_assert(KernelPageBufferAdditionalSize == + (SlabCountExtraKThread + (SlabCountExtraKThread / 8)) * PageSize); + +} // namespace test /// Helper function to translate from the slab virtual address to the reserved location in physical /// memory. @@ -94,8 +114,8 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd // TODO(bunnei): Fix this once we support the kernel virtual memory layout. if (size > 0) { - void* backing_kernel_memory{ - system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))}; + void* backing_kernel_memory{system.DeviceMemory().GetPointer( + TranslateSlabAddrToPhysical(memory_layout, start))}; const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1); ASSERT(region != nullptr); @@ -107,7 +127,7 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd } size_t CalculateSlabHeapGapSize() { - constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB; + constexpr size_t KernelSlabHeapGapSize = 2_MiB - 320_KiB; static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax); return KernelSlabHeapGapSize; } @@ -132,6 +152,7 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() { .num_KDebug = SlabCountKDebug, .num_KIoPool = SlabCountKIoPool, .num_KIoRegion = SlabCountKIoRegion, + .num_KSessionRequestMappings = SlabcountKSessionRequestMappings, }; } @@ -162,29 +183,6 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) { return size; } -void InitializeKPageBufferSlabHeap(Core::System& system) { - auto& kernel = system.Kernel(); - - const auto& counts = kernel.SlabResourceCounts(); - const size_t num_pages = - counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8; - const size_t slab_size = num_pages * PageSize; - - // Reserve memory from the system resource limit. - ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size)); - - // Allocate memory for the slab. - constexpr auto AllocateOption = KMemoryManager::EncodeOption( - KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront); - const PAddr slab_address = - kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption); - ASSERT(slab_address != 0); - - // Initialize the slabheap. - KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address), - slab_size); -} - void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { auto& kernel = system.Kernel(); @@ -245,6 +243,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { // If we somehow get an invalid type, abort. default: ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]); + break; } // If we've hit the end of a gap, free it. @@ -256,3 +255,30 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { } } // namespace Kernel::Init + +namespace Kernel { + +void KPageBufferSlabHeap::Initialize(Core::System& system) { + auto& kernel = system.Kernel(); + const auto& counts = kernel.SlabResourceCounts(); + const size_t num_pages = + counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8; + const size_t slab_size = num_pages * PageSize; + + // Reserve memory from the system resource limit. + ASSERT( + kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size)); + + // Allocate memory for the slab. + constexpr auto AllocateOption = KMemoryManager::EncodeOption( + KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront); + const PAddr slab_address = + kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption); + ASSERT(slab_address != 0); + + // Initialize the slabheap. + KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address), + slab_size); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/init/init_slab_setup.h b/src/core/hle/kernel/init/init_slab_setup.h index 13be63c..5e22821 100644 --- a/src/core/hle/kernel/init/init_slab_setup.h +++ b/src/core/hle/kernel/init/init_slab_setup.h @@ -33,11 +33,11 @@ struct KSlabResourceCounts { size_t num_KDebug; size_t num_KIoPool; size_t num_KIoRegion; + size_t num_KSessionRequestMappings; }; void InitializeSlabResourceCounts(KernelCore& kernel); size_t CalculateTotalSlabHeapSize(const KernelCore& kernel); -void InitializeKPageBufferSlabHeap(Core::System& system); void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout); } // namespace Kernel::Init diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp index cc2a0f7..a850db3 100644 --- a/src/core/hle/kernel/k_class_token.cpp +++ b/src/core/hle/kernel/k_class_token.cpp @@ -16,9 +16,9 @@ #include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/k_system_resource.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/kernel/k_writable_event.h" namespace Kernel { @@ -42,13 +42,12 @@ static_assert(ClassToken == 0b10000101'00000000); static_assert(ClassToken == 0b00011001'00000000); static_assert(ClassToken == 0b00101001'00000000); static_assert(ClassToken == 0b01001001'00000000); -static_assert(ClassToken == 0b10001001'00000000); // static_assert(ClassToken == 0b00110001'00000000); // static_assert(ClassToken == 0b01010001'00000000); -static_assert(ClassToken == 0b10010001'00000000); +static_assert(ClassToken == 0b01010001'00000000); // static_assert(ClassToken == 0b01100001'00000000); // static_assert(ClassToken == 0b10100001'00000000); -static_assert(ClassToken == 0b11000001'00000000); +static_assert(ClassToken == 0b10100001'00000000); // Ensure that the token hierarchy is correct. @@ -73,13 +72,12 @@ static_assert(ClassToken == ((0b10000101 << 8) | ClassToken) static_assert(ClassToken == ((0b00011001 << 8) | ClassToken)); static_assert(ClassToken == ((0b00101001 << 8) | ClassToken)); static_assert(ClassToken == ((0b01001001 << 8) | ClassToken)); -static_assert(ClassToken == ((0b10001001 << 8) | ClassToken)); // static_assert(ClassToken == ((0b00110001 << 8) | ClassToken)); // static_assert(ClassToken == ((0b01010001 << 8) | ClassToken)); -static_assert(ClassToken == ((0b10010001 << 8) | ClassToken)); +static_assert(ClassToken == ((0b01010001 << 8) | ClassToken)); // static_assert(ClassToken == ((0b01100001 << 8) | ClassToken)); // static_assert(ClassToken == ((0b10100001 << 8) | ClassToken)); -static_assert(ClassToken == ((0b11000001 << 8) | ClassToken)); +static_assert(ClassToken == ((0b10100001 << 8) | ClassToken)); // Ensure that the token hierarchy reflects the class hierarchy. @@ -110,7 +108,6 @@ static_assert(std::is_final_v && std::is_base_of_v); static_assert(std::is_final_v && std::is_base_of_v); static_assert(std::is_final_v && std::is_base_of_v); static_assert(std::is_final_v && std::is_base_of_v); -static_assert(std::is_final_v && std::is_base_of_v); // static_assert(std::is_final_v && // std::is_base_of_v); // static_assert(std::is_final_v && @@ -123,4 +120,6 @@ static_assert(std::is_final_v && std::is_base_of_v && // std::is_base_of_v); +static_assert(std::is_base_of_v); + } // namespace Kernel diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h index c9001ae..e75b1c0 100644 --- a/src/core/hle/kernel/k_class_token.h +++ b/src/core/hle/kernel/k_class_token.h @@ -10,6 +10,8 @@ namespace Kernel { class KAutoObject; +class KSystemResource; + class KClassTokenGenerator { public: using TokenBaseType = u16; @@ -58,7 +60,7 @@ private: if constexpr (std::is_same::value) { static_assert(T::ObjectType == ObjectType::KAutoObject); return 0; - } else if constexpr (!std::is_final::value) { + } else if constexpr (!std::is_final::value && !std::same_as) { static_assert(ObjectType::BaseClassesStart <= T::ObjectType && T::ObjectType < ObjectType::BaseClassesEnd); constexpr auto ClassIndex = static_cast(T::ObjectType) - @@ -101,7 +103,6 @@ public: KSession, KSharedMemory, KEvent, - KWritableEvent, KLightClientSession, KLightServerSession, KTransferMemory, @@ -109,6 +110,8 @@ public: KSessionRequest, KCodeMemory, + KSystemResource, + // NOTE: True order for these has not been determined yet. KAlpha, KBeta, diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index 3cb22ff..2ec623a 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp @@ -58,11 +58,10 @@ bool KClientPort::IsSignaled() const { return num_sessions < max_sessions; } -Result KClientPort::CreateSession(KClientSession** out, - std::shared_ptr session_manager) { +Result KClientPort::CreateSession(KClientSession** out) { // Reserve a new session from the resource limit. KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), - LimitableResource::Sessions); + LimitableResource::SessionCountMax); R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); // Update the session counts. @@ -104,7 +103,7 @@ Result KClientPort::CreateSession(KClientSession** out, } // Initialize the session. - session->Initialize(this, parent->GetName(), session_manager); + session->Initialize(this, parent->GetName()); // Commit the session reservation. session_reservation.Commit(); diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h index e17eff2..81046fb 100644 --- a/src/core/hle/kernel/k_client_port.h +++ b/src/core/hle/kernel/k_client_port.h @@ -52,8 +52,7 @@ public: void Destroy() override; bool IsSignaled() const override; - Result CreateSession(KClientSession** out, - std::shared_ptr session_manager = nullptr); + Result CreateSession(KClientSession** out); private: std::atomic num_sessions{}; diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp index b2a887b..b4197a8 100644 --- a/src/core/hle/kernel/k_client_session.cpp +++ b/src/core/hle/kernel/k_client_session.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/scope_exit.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_client_session.h" #include "core/hle/kernel/k_server_session.h" @@ -10,6 +11,8 @@ namespace Kernel { +static constexpr u32 MessageBufferSize = 0x100; + KClientSession::KClientSession(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} KClientSession::~KClientSession() = default; @@ -21,10 +24,17 @@ void KClientSession::Destroy() { void KClientSession::OnServerClosed() {} -Result KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing) { - // Signal the server session that new data is available - return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing); +Result KClientSession::SendSyncRequest() { + // Create a session request. + KSessionRequest* request = KSessionRequest::Create(kernel); + R_UNLESS(request != nullptr, ResultOutOfResource); + SCOPE_EXIT({ request->Close(); }); + + // Initialize the request. + request->Initialize(nullptr, GetCurrentThread(kernel).GetTLSAddress(), MessageBufferSize); + + // Send the request. + return parent->GetServerSession().OnRequest(request); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h index 0c750d7..b4a19c5 100644 --- a/src/core/hle/kernel/k_client_session.h +++ b/src/core/hle/kernel/k_client_session.h @@ -46,8 +46,7 @@ public: return parent; } - Result SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing); + Result SendSyncRequest(); void OnServerClosed(); diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index da57ceb..4b1c134 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp @@ -34,7 +34,7 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si // Clear the memory. for (const auto& block : m_page_group.Nodes()) { - std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); + std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); } // Set remaining tracking members. diff --git a/src/core/hle/kernel/k_debug.h b/src/core/hle/kernel/k_debug.h new file mode 100644 index 0000000..e3a0689 --- /dev/null +++ b/src/core/hle/kernel/k_debug.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/kernel/k_auto_object.h" +#include "core/hle/kernel/slab_helpers.h" + +namespace Kernel { + +class KDebug final : public KAutoObjectWithSlabHeapAndContainer { + KERNEL_AUTOOBJECT_TRAITS(KDebug, KAutoObject); + +public: + explicit KDebug(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} + + static void PostDestroy([[maybe_unused]] uintptr_t arg) {} +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_dynamic_page_manager.h b/src/core/hle/kernel/k_dynamic_page_manager.h new file mode 100644 index 0000000..ac80d60 --- /dev/null +++ b/src/core/hle/kernel/k_dynamic_page_manager.h @@ -0,0 +1,169 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/alignment.h" +#include "common/common_types.h" +#include "core/hle/kernel/k_page_bitmap.h" +#include "core/hle/kernel/k_spin_lock.h" +#include "core/hle/kernel/memory_types.h" +#include "core/hle/kernel/svc_results.h" + +namespace Kernel { + +class KDynamicPageManager { +public: + class PageBuffer { + private: + u8 m_buffer[PageSize]; + }; + static_assert(sizeof(PageBuffer) == PageSize); + +public: + KDynamicPageManager() = default; + + template + T* GetPointer(VAddr addr) { + return reinterpret_cast(m_backing_memory.data() + (addr - m_address)); + } + + template + const T* GetPointer(VAddr addr) const { + return reinterpret_cast(m_backing_memory.data() + (addr - m_address)); + } + + Result Initialize(VAddr memory, size_t size, size_t align) { + // We need to have positive size. + R_UNLESS(size > 0, ResultOutOfMemory); + m_backing_memory.resize(size); + + // Set addresses. + m_address = memory; + m_aligned_address = Common::AlignDown(memory, align); + + // Calculate extents. + const size_t managed_size = m_address + size - m_aligned_address; + const size_t overhead_size = Common::AlignUp( + KPageBitmap::CalculateManagementOverheadSize(managed_size / sizeof(PageBuffer)), + sizeof(PageBuffer)); + R_UNLESS(overhead_size < size, ResultOutOfMemory); + + // Set tracking fields. + m_size = Common::AlignDown(size - overhead_size, sizeof(PageBuffer)); + m_count = m_size / sizeof(PageBuffer); + + // Clear the management region. + u64* management_ptr = GetPointer(m_address + size - overhead_size); + std::memset(management_ptr, 0, overhead_size); + + // Initialize the bitmap. + const size_t allocatable_region_size = + (m_address + size - overhead_size) - m_aligned_address; + ASSERT(allocatable_region_size >= sizeof(PageBuffer)); + + m_page_bitmap.Initialize(management_ptr, allocatable_region_size / sizeof(PageBuffer)); + + // Free the pages to the bitmap. + for (size_t i = 0; i < m_count; i++) { + // Ensure the freed page is all-zero. + std::memset(GetPointer(m_address) + i, 0, PageSize); + + // Set the bit for the free page. + m_page_bitmap.SetBit((m_address + (i * sizeof(PageBuffer)) - m_aligned_address) / + sizeof(PageBuffer)); + } + + R_SUCCEED(); + } + + VAddr GetAddress() const { + return m_address; + } + size_t GetSize() const { + return m_size; + } + size_t GetUsed() const { + return m_used; + } + size_t GetPeak() const { + return m_peak; + } + size_t GetCount() const { + return m_count; + } + + PageBuffer* Allocate() { + // Take the lock. + // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. + KScopedSpinLock lk(m_lock); + + // Find a random free block. + s64 soffset = m_page_bitmap.FindFreeBlock(true); + if (soffset < 0) [[unlikely]] { + return nullptr; + } + + const size_t offset = static_cast(soffset); + + // Update our tracking. + m_page_bitmap.ClearBit(offset); + m_peak = std::max(m_peak, (++m_used)); + + return GetPointer(m_aligned_address) + offset; + } + + PageBuffer* Allocate(size_t count) { + // Take the lock. + // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. + KScopedSpinLock lk(m_lock); + + // Find a random free block. + s64 soffset = m_page_bitmap.FindFreeRange(count); + if (soffset < 0) [[likely]] { + return nullptr; + } + + const size_t offset = static_cast(soffset); + + // Update our tracking. + m_page_bitmap.ClearRange(offset, count); + m_used += count; + m_peak = std::max(m_peak, m_used); + + return GetPointer(m_aligned_address) + offset; + } + + void Free(PageBuffer* pb) { + // Ensure all pages in the heap are zero. + std::memset(pb, 0, PageSize); + + // Take the lock. + // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. + KScopedSpinLock lk(m_lock); + + // Set the bit for the free page. + size_t offset = (reinterpret_cast(pb) - m_aligned_address) / sizeof(PageBuffer); + m_page_bitmap.SetBit(offset); + + // Decrement our used count. + --m_used; + } + +private: + KSpinLock m_lock; + KPageBitmap m_page_bitmap; + size_t m_used{}; + size_t m_peak{}; + size_t m_count{}; + VAddr m_address{}; + VAddr m_aligned_address{}; + size_t m_size{}; + + // TODO(bunnei): Back by host memory until we emulate kernel virtual address space. + std::vector m_backing_memory; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_dynamic_resource_manager.h b/src/core/hle/kernel/k_dynamic_resource_manager.h new file mode 100644 index 0000000..b6a27d6 --- /dev/null +++ b/src/core/hle/kernel/k_dynamic_resource_manager.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "core/hle/kernel/k_dynamic_slab_heap.h" +#include "core/hle/kernel/k_memory_block.h" +#include "core/hle/kernel/k_page_group.h" + +namespace Kernel { + +template +class KDynamicResourceManager { + YUZU_NON_COPYABLE(KDynamicResourceManager); + YUZU_NON_MOVEABLE(KDynamicResourceManager); + +public: + using DynamicSlabType = KDynamicSlabHeap; + +public: + constexpr KDynamicResourceManager() = default; + + constexpr size_t GetSize() const { + return m_slab_heap->GetSize(); + } + constexpr size_t GetUsed() const { + return m_slab_heap->GetUsed(); + } + constexpr size_t GetPeak() const { + return m_slab_heap->GetPeak(); + } + constexpr size_t GetCount() const { + return m_slab_heap->GetCount(); + } + + void Initialize(KDynamicPageManager* page_allocator, DynamicSlabType* slab_heap) { + m_page_allocator = page_allocator; + m_slab_heap = slab_heap; + } + + T* Allocate() const { + return m_slab_heap->Allocate(m_page_allocator); + } + + void Free(T* t) const { + m_slab_heap->Free(t); + } + +private: + KDynamicPageManager* m_page_allocator{}; + DynamicSlabType* m_slab_heap{}; +}; + +class KBlockInfoManager : public KDynamicResourceManager {}; +class KMemoryBlockSlabManager : public KDynamicResourceManager {}; + +using KBlockInfoSlabHeap = typename KBlockInfoManager::DynamicSlabType; +using KMemoryBlockSlabHeap = typename KMemoryBlockSlabManager::DynamicSlabType; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_dynamic_slab_heap.h b/src/core/hle/kernel/k_dynamic_slab_heap.h new file mode 100644 index 0000000..3a0ddd0 --- /dev/null +++ b/src/core/hle/kernel/k_dynamic_slab_heap.h @@ -0,0 +1,122 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_funcs.h" +#include "core/hle/kernel/k_dynamic_page_manager.h" +#include "core/hle/kernel/k_slab_heap.h" + +namespace Kernel { + +template +class KDynamicSlabHeap : protected impl::KSlabHeapImpl { + YUZU_NON_COPYABLE(KDynamicSlabHeap); + YUZU_NON_MOVEABLE(KDynamicSlabHeap); + +public: + constexpr KDynamicSlabHeap() = default; + + constexpr VAddr GetAddress() const { + return m_address; + } + constexpr size_t GetSize() const { + return m_size; + } + constexpr size_t GetUsed() const { + return m_used.load(); + } + constexpr size_t GetPeak() const { + return m_peak.load(); + } + constexpr size_t GetCount() const { + return m_count.load(); + } + + constexpr bool IsInRange(VAddr addr) const { + return this->GetAddress() <= addr && addr <= this->GetAddress() + this->GetSize() - 1; + } + + void Initialize(KDynamicPageManager* page_allocator, size_t num_objects) { + ASSERT(page_allocator != nullptr); + + // Initialize members. + m_address = page_allocator->GetAddress(); + m_size = page_allocator->GetSize(); + + // Initialize the base allocator. + KSlabHeapImpl::Initialize(); + + // Allocate until we have the correct number of objects. + while (m_count.load() < num_objects) { + auto* allocated = reinterpret_cast(page_allocator->Allocate()); + ASSERT(allocated != nullptr); + + for (size_t i = 0; i < sizeof(PageBuffer) / sizeof(T); i++) { + KSlabHeapImpl::Free(allocated + i); + } + + m_count += sizeof(PageBuffer) / sizeof(T); + } + } + + T* Allocate(KDynamicPageManager* page_allocator) { + T* allocated = static_cast(KSlabHeapImpl::Allocate()); + + // If we successfully allocated and we should clear the node, do so. + if constexpr (ClearNode) { + if (allocated != nullptr) [[likely]] { + reinterpret_cast(allocated)->next = nullptr; + } + } + + // If we fail to allocate, try to get a new page from our next allocator. + if (allocated == nullptr) [[unlikely]] { + if (page_allocator != nullptr) { + allocated = reinterpret_cast(page_allocator->Allocate()); + if (allocated != nullptr) { + // If we succeeded in getting a page, free the rest to our slab. + for (size_t i = 1; i < sizeof(PageBuffer) / sizeof(T); i++) { + KSlabHeapImpl::Free(allocated + i); + } + m_count += sizeof(PageBuffer) / sizeof(T); + } + } + } + + if (allocated != nullptr) [[likely]] { + // Construct the object. + std::construct_at(allocated); + + // Update our tracking. + const size_t used = ++m_used; + size_t peak = m_peak.load(); + while (peak < used) { + if (m_peak.compare_exchange_weak(peak, used, std::memory_order_relaxed)) { + break; + } + } + } + + return allocated; + } + + void Free(T* t) { + KSlabHeapImpl::Free(t); + --m_used; + } + +private: + using PageBuffer = KDynamicPageManager::PageBuffer; + +private: + std::atomic m_used{}; + std::atomic m_peak{}; + std::atomic m_count{}; + VAddr m_address{}; + size_t m_size{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp index e52fafb..d973853 100644 --- a/src/core/hle/kernel/k_event.cpp +++ b/src/core/hle/kernel/k_event.cpp @@ -8,44 +8,57 @@ namespace Kernel { KEvent::KEvent(KernelCore& kernel_) - : KAutoObjectWithSlabHeapAndContainer{kernel_}, readable_event{kernel_}, writable_event{ - kernel_} {} + : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_readable_event{kernel_} {} KEvent::~KEvent() = default; -void KEvent::Initialize(std::string&& name_, KProcess* owner_) { - // Increment reference count. - // Because reference count is one on creation, this will result - // in a reference count of two. Thus, when both readable and - // writable events are closed this object will be destroyed. - Open(); +void KEvent::Initialize(KProcess* owner) { + // Create our readable event. + KAutoObject::Create(std::addressof(m_readable_event)); - // Create our sub events. - KAutoObject::Create(std::addressof(readable_event)); - KAutoObject::Create(std::addressof(writable_event)); - - // Initialize our sub sessions. - readable_event.Initialize(this, name_ + ":Readable"); - writable_event.Initialize(this, name_ + ":Writable"); + // Initialize our readable event. + m_readable_event.Initialize(this); // Set our owner process. - owner = owner_; - owner->Open(); + // HACK: this should never be nullptr, but service threads don't have a + // proper parent process yet. + if (owner != nullptr) { + m_owner = owner; + m_owner->Open(); + } // Mark initialized. - name = std::move(name_); - initialized = true; + m_initialized = true; } void KEvent::Finalize() { KAutoObjectWithSlabHeapAndContainer::Finalize(); } +Result KEvent::Signal() { + KScopedSchedulerLock sl{kernel}; + + R_SUCCEED_IF(m_readable_event_destroyed); + + return m_readable_event.Signal(); +} + +Result KEvent::Clear() { + KScopedSchedulerLock sl{kernel}; + + R_SUCCEED_IF(m_readable_event_destroyed); + + return m_readable_event.Clear(); +} + void KEvent::PostDestroy(uintptr_t arg) { // Release the event count resource the owner process holds. KProcess* owner = reinterpret_cast(arg); - owner->GetResourceLimit()->Release(LimitableResource::Events, 1); - owner->Close(); + + if (owner != nullptr) { + owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1); + owner->Close(); + } } } // namespace Kernel diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h index 2ff828f..48ce7d9 100644 --- a/src/core/hle/kernel/k_event.h +++ b/src/core/hle/kernel/k_event.h @@ -4,14 +4,12 @@ #pragma once #include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/slab_helpers.h" namespace Kernel { class KernelCore; class KReadableEvent; -class KWritableEvent; class KProcess; class KEvent final : public KAutoObjectWithSlabHeapAndContainer { @@ -21,37 +19,40 @@ public: explicit KEvent(KernelCore& kernel_); ~KEvent() override; - void Initialize(std::string&& name, KProcess* owner_); + void Initialize(KProcess* owner); void Finalize() override; bool IsInitialized() const override { - return initialized; + return m_initialized; } uintptr_t GetPostDestroyArgument() const override { - return reinterpret_cast(owner); + return reinterpret_cast(m_owner); } KProcess* GetOwner() const override { - return owner; + return m_owner; } KReadableEvent& GetReadableEvent() { - return readable_event; - } - - KWritableEvent& GetWritableEvent() { - return writable_event; + return m_readable_event; } static void PostDestroy(uintptr_t arg); + Result Signal(); + Result Clear(); + + void OnReadableEventDestroyed() { + m_readable_event_destroyed = true; + } + private: - KReadableEvent readable_event; - KWritableEvent writable_event; - KProcess* owner{}; - bool initialized{}; + KReadableEvent m_readable_event; + KProcess* m_owner{}; + bool m_initialized{}; + bool m_readable_event_destroyed{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_event_info.h b/src/core/hle/kernel/k_event_info.h new file mode 100644 index 0000000..25b3ff5 --- /dev/null +++ b/src/core/hle/kernel/k_event_info.h @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include + +#include "core/hle/kernel/slab_helpers.h" +#include "core/hle/kernel/svc_types.h" + +namespace Kernel { + +class KEventInfo : public KSlabAllocated, public boost::intrusive::list_base_hook<> { +public: + struct InfoCreateThread { + u32 thread_id{}; + uintptr_t tls_address{}; + }; + + struct InfoExitProcess { + Svc::ProcessExitReason reason{}; + }; + + struct InfoExitThread { + Svc::ThreadExitReason reason{}; + }; + + struct InfoException { + Svc::DebugException exception_type{}; + s32 exception_data_count{}; + uintptr_t exception_address{}; + std::array exception_data{}; + }; + + struct InfoSystemCall { + s64 tick{}; + s32 id{}; + }; + +public: + KEventInfo() = default; + ~KEventInfo() = default; + +public: + Svc::DebugEvent event{}; + u32 thread_id{}; + u32 flags{}; + bool is_attached{}; + bool continue_flag{}; + bool ignore_continue{}; + bool close_once{}; + union { + InfoCreateThread create_thread; + InfoExitProcess exit_process; + InfoExitThread exit_thread; + InfoException exception; + InfoSystemCall system_call; + } info{}; + KThread* debug_thread{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp index e830ca4..3535ddc 100644 --- a/src/core/hle/kernel/k_handle_table.cpp +++ b/src/core/hle/kernel/k_handle_table.cpp @@ -2,17 +2,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_handle_table.h" +#include "core/hle/kernel/k_process.h" namespace Kernel { -KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {} -KHandleTable::~KHandleTable() = default; - Result KHandleTable::Finalize() { // Get the table and clear our record of it. u16 saved_table_size = 0; { - KScopedDisableDispatch dd(kernel); + KScopedDisableDispatch dd{m_kernel}; KScopedSpinLock lk(m_lock); std::swap(m_table_size, saved_table_size); @@ -25,28 +23,28 @@ Result KHandleTable::Finalize() { } } - return ResultSuccess; + R_SUCCEED(); } bool KHandleTable::Remove(Handle handle) { // Don't allow removal of a pseudo-handle. - if (Svc::IsPseudoHandle(handle)) { + if (Svc::IsPseudoHandle(handle)) [[unlikely]] { return false; } // Handles must not have reserved bits set. const auto handle_pack = HandlePack(handle); - if (handle_pack.reserved != 0) { + if (handle_pack.reserved != 0) [[unlikely]] { return false; } // Find the object and free the entry. KAutoObject* obj = nullptr; { - KScopedDisableDispatch dd(kernel); + KScopedDisableDispatch dd{m_kernel}; KScopedSpinLock lk(m_lock); - if (this->IsValidHandle(handle)) { + if (this->IsValidHandle(handle)) [[likely]] { const auto index = handle_pack.index; obj = m_objects[index]; @@ -57,13 +55,13 @@ bool KHandleTable::Remove(Handle handle) { } // Close the object. - kernel.UnregisterInUseObject(obj); + m_kernel.UnregisterInUseObject(obj); obj->Close(); return true; } Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) { - KScopedDisableDispatch dd(kernel); + KScopedDisableDispatch dd{m_kernel}; KScopedSpinLock lk(m_lock); // Never exceed our capacity. @@ -82,22 +80,38 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) { *out_handle = EncodeHandle(static_cast(index), linear_id); } - return ResultSuccess; + R_SUCCEED(); +} + +KScopedAutoObject KHandleTable::GetObjectForIpc(Handle handle, + KThread* cur_thread) const { + // Handle pseudo-handles. + ASSERT(cur_thread != nullptr); + if (handle == Svc::PseudoHandle::CurrentProcess) { + auto* const cur_process = cur_thread->GetOwnerProcess(); + ASSERT(cur_process != nullptr); + return cur_process; + } + if (handle == Svc::PseudoHandle::CurrentThread) { + return cur_thread; + } + + return GetObjectForIpcWithoutPseudoHandle(handle); } Result KHandleTable::Reserve(Handle* out_handle) { - KScopedDisableDispatch dd(kernel); + KScopedDisableDispatch dd{m_kernel}; KScopedSpinLock lk(m_lock); // Never exceed our capacity. R_UNLESS(m_count < m_table_size, ResultOutOfHandles); *out_handle = EncodeHandle(static_cast(this->AllocateEntry()), this->AllocateLinearId()); - return ResultSuccess; + R_SUCCEED(); } void KHandleTable::Unreserve(Handle handle) { - KScopedDisableDispatch dd(kernel); + KScopedDisableDispatch dd{m_kernel}; KScopedSpinLock lk(m_lock); // Unpack the handle. @@ -108,7 +122,7 @@ void KHandleTable::Unreserve(Handle handle) { ASSERT(reserved == 0); ASSERT(linear_id != 0); - if (index < m_table_size) { + if (index < m_table_size) [[likely]] { // NOTE: This code does not check the linear id. ASSERT(m_objects[index] == nullptr); this->FreeEntry(index); @@ -116,7 +130,7 @@ void KHandleTable::Unreserve(Handle handle) { } void KHandleTable::Register(Handle handle, KAutoObject* obj) { - KScopedDisableDispatch dd(kernel); + KScopedDisableDispatch dd{m_kernel}; KScopedSpinLock lk(m_lock); // Unpack the handle. @@ -127,7 +141,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj) { ASSERT(reserved == 0); ASSERT(linear_id != 0); - if (index < m_table_size) { + if (index < m_table_size) [[likely]] { // Set the entry. ASSERT(m_objects[index] == nullptr); diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h index 0864a73..37a24e7 100644 --- a/src/core/hle/kernel/k_handle_table.h +++ b/src/core/hle/kernel/k_handle_table.h @@ -21,33 +21,38 @@ namespace Kernel { class KernelCore; class KHandleTable { -public: YUZU_NON_COPYABLE(KHandleTable); YUZU_NON_MOVEABLE(KHandleTable); +public: static constexpr size_t MaxTableSize = 1024; - explicit KHandleTable(KernelCore& kernel_); - ~KHandleTable(); +public: + explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {} Result Initialize(s32 size) { + // Check that the table size is valid. R_UNLESS(size <= static_cast(MaxTableSize), ResultOutOfMemory); + // Lock. + KScopedDisableDispatch dd{m_kernel}; + KScopedSpinLock lk(m_lock); + // Initialize all fields. m_max_count = 0; - m_table_size = static_cast((size <= 0) ? MaxTableSize : size); + m_table_size = static_cast((size <= 0) ? MaxTableSize : size); m_next_linear_id = MinLinearId; m_count = 0; m_free_head_index = -1; // Free all entries. - for (s16 i = 0; i < static_cast(m_table_size); ++i) { + for (s32 i = 0; i < static_cast(m_table_size); ++i) { m_objects[i] = nullptr; - m_entry_infos[i].next_free_index = i - 1; + m_entry_infos[i].next_free_index = static_cast(i - 1); m_free_head_index = i; } - return ResultSuccess; + R_SUCCEED(); } size_t GetTableSize() const { @@ -66,13 +71,13 @@ public: template KScopedAutoObject GetObjectWithoutPseudoHandle(Handle handle) const { // Lock and look up in table. - KScopedDisableDispatch dd(kernel); + KScopedDisableDispatch dd{m_kernel}; KScopedSpinLock lk(m_lock); if constexpr (std::is_same_v) { return this->GetObjectImpl(handle); } else { - if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) { + if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) [[likely]] { return obj->DynamicCast(); } else { return nullptr; @@ -85,13 +90,13 @@ public: // Handle pseudo-handles. if constexpr (std::derived_from) { if (handle == Svc::PseudoHandle::CurrentProcess) { - auto* const cur_process = kernel.CurrentProcess(); + auto* const cur_process = m_kernel.CurrentProcess(); ASSERT(cur_process != nullptr); return cur_process; } } else if constexpr (std::derived_from) { if (handle == Svc::PseudoHandle::CurrentThread) { - auto* const cur_thread = GetCurrentThreadPointer(kernel); + auto* const cur_thread = GetCurrentThreadPointer(m_kernel); ASSERT(cur_thread != nullptr); return cur_thread; } @@ -100,6 +105,23 @@ public: return this->template GetObjectWithoutPseudoHandle(handle); } + KScopedAutoObject GetObjectForIpcWithoutPseudoHandle(Handle handle) const { + // Lock and look up in table. + KScopedDisableDispatch dd{m_kernel}; + KScopedSpinLock lk(m_lock); + + return this->GetObjectImpl(handle); + } + + KScopedAutoObject GetObjectForIpc(Handle handle, KThread* cur_thread) const; + + KScopedAutoObject GetObjectByIndex(Handle* out_handle, size_t index) const { + KScopedDisableDispatch dd{m_kernel}; + KScopedSpinLock lk(m_lock); + + return this->GetObjectByIndexImpl(out_handle, index); + } + Result Reserve(Handle* out_handle); void Unreserve(Handle handle); @@ -112,7 +134,7 @@ public: size_t num_opened; { // Lock the table. - KScopedDisableDispatch dd(kernel); + KScopedDisableDispatch dd{m_kernel}; KScopedSpinLock lk(m_lock); for (num_opened = 0; num_opened < num_handles; num_opened++) { // Get the current handle. @@ -120,13 +142,13 @@ public: // Get the object for the current handle. KAutoObject* cur_object = this->GetObjectImpl(cur_handle); - if (cur_object == nullptr) { + if (cur_object == nullptr) [[unlikely]] { break; } // Cast the current object to the desired type. T* cur_t = cur_object->DynamicCast(); - if (cur_t == nullptr) { + if (cur_t == nullptr) [[unlikely]] { break; } @@ -137,7 +159,7 @@ public: } // If we converted every object, succeed. - if (num_opened == num_handles) { + if (num_opened == num_handles) [[likely]] { return true; } @@ -191,21 +213,21 @@ private: ASSERT(reserved == 0); // Validate our indexing information. - if (raw_value == 0) { + if (raw_value == 0) [[unlikely]] { return false; } - if (linear_id == 0) { + if (linear_id == 0) [[unlikely]] { return false; } - if (index >= m_table_size) { + if (index >= m_table_size) [[unlikely]] { return false; } // Check that there's an object, and our serial id is correct. - if (m_objects[index] == nullptr) { + if (m_objects[index] == nullptr) [[unlikely]] { return false; } - if (m_entry_infos[index].GetLinearId() != linear_id) { + if (m_entry_infos[index].GetLinearId() != linear_id) [[unlikely]] { return false; } @@ -215,11 +237,11 @@ private: KAutoObject* GetObjectImpl(Handle handle) const { // Handles must not have reserved bits set. const auto handle_pack = HandlePack(handle); - if (handle_pack.reserved != 0) { + if (handle_pack.reserved != 0) [[unlikely]] { return nullptr; } - if (this->IsValidHandle(handle)) { + if (this->IsValidHandle(handle)) [[likely]] { return m_objects[handle_pack.index]; } else { return nullptr; @@ -227,9 +249,8 @@ private: } KAutoObject* GetObjectByIndexImpl(Handle* out_handle, size_t index) const { - // Index must be in bounds. - if (index >= m_table_size) { + if (index >= m_table_size) [[unlikely]] { return nullptr; } @@ -244,18 +265,15 @@ private: private: union HandlePack { - HandlePack() = default; - HandlePack(Handle handle) : raw{static_cast(handle)} {} + constexpr HandlePack() = default; + constexpr HandlePack(Handle handle) : raw{static_cast(handle)} {} - u32 raw; + u32 raw{}; BitField<0, 15, u32> index; BitField<15, 15, u32> linear_id; BitField<30, 2, u32> reserved; }; - static constexpr u16 MinLinearId = 1; - static constexpr u16 MaxLinearId = 0x7FFF; - static constexpr Handle EncodeHandle(u16 index, u16 linear_id) { HandlePack handle{}; handle.index.Assign(index); @@ -264,6 +282,10 @@ private: return handle.raw; } +private: + static constexpr u16 MinLinearId = 1; + static constexpr u16 MaxLinearId = 0x7FFF; + union EntryInfo { u16 linear_id; s16 next_free_index; @@ -271,21 +293,21 @@ private: constexpr u16 GetLinearId() const { return linear_id; } - constexpr s16 GetNextFreeIndex() const { + constexpr s32 GetNextFreeIndex() const { return next_free_index; } }; private: + KernelCore& m_kernel; std::array m_entry_infos{}; std::array m_objects{}; - s32 m_free_head_index{-1}; + mutable KSpinLock m_lock; + s32 m_free_head_index{}; u16 m_table_size{}; u16 m_max_count{}; - u16 m_next_linear_id{MinLinearId}; + u16 m_next_linear_id{}; u16 m_count{}; - mutable KSpinLock m_lock; - KernelCore& kernel; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp index 1b577a5..4a6b60d 100644 --- a/src/core/hle/kernel/k_interrupt_manager.cpp +++ b/src/core/hle/kernel/k_interrupt_manager.cpp @@ -11,29 +11,34 @@ namespace Kernel::KInterruptManager { void HandleInterrupt(KernelCore& kernel, s32 core_id) { - auto* process = kernel.CurrentProcess(); - if (!process) { - return; - } - // Acknowledge the interrupt. kernel.PhysicalCore(core_id).ClearInterrupt(); auto& current_thread = GetCurrentThread(kernel); - // If the user disable count is set, we may need to pin the current thread. - if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) { - KScopedSchedulerLock sl{kernel}; + if (auto* process = kernel.CurrentProcess(); process) { + // If the user disable count is set, we may need to pin the current thread. + if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) { + KScopedSchedulerLock sl{kernel}; - // Pin the current thread. - process->PinCurrentThread(core_id); + // Pin the current thread. + process->PinCurrentThread(core_id); - // Set the interrupt flag for the thread. - GetCurrentThread(kernel).SetInterruptFlag(); + // Set the interrupt flag for the thread. + GetCurrentThread(kernel).SetInterruptFlag(); + } } // Request interrupt scheduling. kernel.CurrentScheduler()->RequestScheduleOnInterrupt(); } +void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask) { + for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) { + if (core_mask & (1ULL << core_id)) { + kernel.PhysicalCore(core_id).Interrupt(); + } + } +} + } // namespace Kernel::KInterruptManager diff --git a/src/core/hle/kernel/k_interrupt_manager.h b/src/core/hle/kernel/k_interrupt_manager.h index f103dfe..803dc92 100644 --- a/src/core/hle/kernel/k_interrupt_manager.h +++ b/src/core/hle/kernel/k_interrupt_manager.h @@ -11,6 +11,8 @@ class KernelCore; namespace KInterruptManager { void HandleInterrupt(KernelCore& kernel, s32 core_id); -} +void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask); + +} // namespace KInterruptManager } // namespace Kernel diff --git a/src/core/hle/kernel/k_linked_list.h b/src/core/hle/kernel/k_linked_list.h index 78859ce..29ebd16 100644 --- a/src/core/hle/kernel/k_linked_list.h +++ b/src/core/hle/kernel/k_linked_list.h @@ -16,6 +16,7 @@ class KLinkedListNode : public boost::intrusive::list_base_hook<>, public KSlabAllocated { public: + explicit KLinkedListNode(KernelCore&) {} KLinkedListNode() = default; void Initialize(void* it) { diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index 18df1f8..87ca655 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h @@ -6,6 +6,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/common_types.h" +#include "common/intrusive_red_black_tree.h" #include "core/hle/kernel/memory_types.h" #include "core/hle/kernel/svc_types.h" @@ -34,26 +35,32 @@ enum class KMemoryState : u32 { FlagCanMapProcess = (1 << 23), FlagCanChangeAttribute = (1 << 24), FlagCanCodeMemory = (1 << 25), + FlagLinearMapped = (1 << 26), FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc | FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical | FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagCanIpcUserBuffer | - FlagReferenceCounted | FlagCanChangeAttribute, + FlagReferenceCounted | FlagCanChangeAttribute | FlagLinearMapped, FlagsCode = FlagCanDebug | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc | FlagMapped | FlagCode | FlagCanQueryPhysical | FlagCanDeviceMap | - FlagCanAlignedDeviceMap | FlagReferenceCounted, + FlagCanAlignedDeviceMap | FlagReferenceCounted | FlagLinearMapped, - FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap, + FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap | + FlagLinearMapped, Free = static_cast(Svc::MemoryState::Free), - Io = static_cast(Svc::MemoryState::Io) | FlagMapped, + Io = static_cast(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap | + FlagCanAlignedDeviceMap, Static = static_cast(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical, Code = static_cast(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess, CodeData = static_cast(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess | FlagCanCodeMemory, - Shared = static_cast(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted, Normal = static_cast(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory, + Shared = static_cast(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted | + FlagLinearMapped, + + // Alias was removed after 1.0.0. AliasCode = static_cast(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess | FlagCanCodeAlias, @@ -66,18 +73,18 @@ enum class KMemoryState : u32 { Stack = static_cast(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap | FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, - ThreadLocal = - static_cast(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, + ThreadLocal = static_cast(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagLinearMapped, - Transfered = static_cast(Svc::MemoryState::Transferred) | FlagsMisc | + Transfered = static_cast(Svc::MemoryState::Transfered) | FlagsMisc | FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, - SharedTransfered = static_cast(Svc::MemoryState::SharedTransferred) | FlagsMisc | + SharedTransfered = static_cast(Svc::MemoryState::SharedTransfered) | FlagsMisc | FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, SharedCode = static_cast(Svc::MemoryState::SharedCode) | FlagMapped | - FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, + FlagReferenceCounted | FlagLinearMapped | FlagCanUseNonSecureIpc | + FlagCanUseNonDeviceIpc, Inaccessible = static_cast(Svc::MemoryState::Inaccessible), @@ -90,69 +97,69 @@ enum class KMemoryState : u32 { Kernel = static_cast(Svc::MemoryState::Kernel) | FlagMapped, GeneratedCode = static_cast(Svc::MemoryState::GeneratedCode) | FlagMapped | - FlagReferenceCounted | FlagCanDebug, - CodeOut = static_cast(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted, + FlagReferenceCounted | FlagCanDebug | FlagLinearMapped, + CodeOut = static_cast(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted | + FlagLinearMapped, Coverage = static_cast(Svc::MemoryState::Coverage) | FlagMapped, + + Insecure = static_cast(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted | + FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap | + FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, }; DECLARE_ENUM_FLAG_OPERATORS(KMemoryState); static_assert(static_cast(KMemoryState::Free) == 0x00000000); -static_assert(static_cast(KMemoryState::Io) == 0x00002001); +static_assert(static_cast(KMemoryState::Io) == 0x00182001); static_assert(static_cast(KMemoryState::Static) == 0x00042002); -static_assert(static_cast(KMemoryState::Code) == 0x00DC7E03); -static_assert(static_cast(KMemoryState::CodeData) == 0x03FEBD04); -static_assert(static_cast(KMemoryState::Normal) == 0x037EBD05); -static_assert(static_cast(KMemoryState::Shared) == 0x00402006); -static_assert(static_cast(KMemoryState::AliasCode) == 0x00DD7E08); -static_assert(static_cast(KMemoryState::AliasCodeData) == 0x03FFBD09); -static_assert(static_cast(KMemoryState::Ipc) == 0x005C3C0A); -static_assert(static_cast(KMemoryState::Stack) == 0x005C3C0B); -static_assert(static_cast(KMemoryState::ThreadLocal) == 0x0040200C); -static_assert(static_cast(KMemoryState::Transfered) == 0x015C3C0D); -static_assert(static_cast(KMemoryState::SharedTransfered) == 0x005C380E); -static_assert(static_cast(KMemoryState::SharedCode) == 0x0040380F); +static_assert(static_cast(KMemoryState::Code) == 0x04DC7E03); +static_assert(static_cast(KMemoryState::CodeData) == 0x07FEBD04); +static_assert(static_cast(KMemoryState::Normal) == 0x077EBD05); +static_assert(static_cast(KMemoryState::Shared) == 0x04402006); + +static_assert(static_cast(KMemoryState::AliasCode) == 0x04DD7E08); +static_assert(static_cast(KMemoryState::AliasCodeData) == 0x07FFBD09); +static_assert(static_cast(KMemoryState::Ipc) == 0x045C3C0A); +static_assert(static_cast(KMemoryState::Stack) == 0x045C3C0B); +static_assert(static_cast(KMemoryState::ThreadLocal) == 0x0400200C); +static_assert(static_cast(KMemoryState::Transfered) == 0x055C3C0D); +static_assert(static_cast(KMemoryState::SharedTransfered) == 0x045C380E); +static_assert(static_cast(KMemoryState::SharedCode) == 0x0440380F); static_assert(static_cast(KMemoryState::Inaccessible) == 0x00000010); -static_assert(static_cast(KMemoryState::NonSecureIpc) == 0x005C3811); -static_assert(static_cast(KMemoryState::NonDeviceIpc) == 0x004C2812); +static_assert(static_cast(KMemoryState::NonSecureIpc) == 0x045C3811); +static_assert(static_cast(KMemoryState::NonDeviceIpc) == 0x044C2812); static_assert(static_cast(KMemoryState::Kernel) == 0x00002013); -static_assert(static_cast(KMemoryState::GeneratedCode) == 0x00402214); -static_assert(static_cast(KMemoryState::CodeOut) == 0x00402015); +static_assert(static_cast(KMemoryState::GeneratedCode) == 0x04402214); +static_assert(static_cast(KMemoryState::CodeOut) == 0x04402015); static_assert(static_cast(KMemoryState::Coverage) == 0x00002016); +static_assert(static_cast(KMemoryState::Insecure) == 0x05583817); enum class KMemoryPermission : u8 { None = 0, All = static_cast(~None), - Read = 1 << 0, - Write = 1 << 1, - Execute = 1 << 2, - - ReadAndWrite = Read | Write, - ReadAndExecute = Read | Execute, - - UserMask = static_cast(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write | - Svc::MemoryPermission::Execute), - KernelShift = 3, - KernelRead = Read << KernelShift, - KernelWrite = Write << KernelShift, - KernelExecute = Execute << KernelShift, + KernelRead = static_cast(Svc::MemoryPermission::Read) << KernelShift, + KernelWrite = static_cast(Svc::MemoryPermission::Write) << KernelShift, + KernelExecute = static_cast(Svc::MemoryPermission::Execute) << KernelShift, NotMapped = (1 << (2 * KernelShift)), KernelReadWrite = KernelRead | KernelWrite, KernelReadExecute = KernelRead | KernelExecute, - UserRead = Read | KernelRead, - UserWrite = Write | KernelWrite, - UserExecute = Execute, + UserRead = static_cast(Svc::MemoryPermission::Read) | KernelRead, + UserWrite = static_cast(Svc::MemoryPermission::Write) | KernelWrite, + UserExecute = static_cast(Svc::MemoryPermission::Execute), UserReadWrite = UserRead | UserWrite, UserReadExecute = UserRead | UserExecute, - IpcLockChangeMask = NotMapped | UserReadWrite + UserMask = static_cast(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write | + Svc::MemoryPermission::Execute), + + IpcLockChangeMask = NotMapped | UserReadWrite, }; DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); @@ -168,9 +175,8 @@ constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission per enum class KMemoryAttribute : u8 { None = 0x00, - Mask = 0x7F, - All = Mask, - DontCareMask = 0x80, + All = 0xFF, + UserMask = All, Locked = static_cast(Svc::MemoryAttribute::Locked), IpcLocked = static_cast(Svc::MemoryAttribute::IpcLocked), @@ -178,76 +184,115 @@ enum class KMemoryAttribute : u8 { Uncached = static_cast(Svc::MemoryAttribute::Uncached), SetMask = Uncached, - - IpcAndDeviceMapped = IpcLocked | DeviceShared, - LockedAndIpcLocked = Locked | IpcLocked, - DeviceSharedAndUncached = DeviceShared | Uncached }; DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute); -static_assert((static_cast(KMemoryAttribute::Mask) & - static_cast(KMemoryAttribute::DontCareMask)) == 0); +enum class KMemoryBlockDisableMergeAttribute : u8 { + None = 0, + Normal = (1u << 0), + DeviceLeft = (1u << 1), + IpcLeft = (1u << 2), + Locked = (1u << 3), + DeviceRight = (1u << 4), + + AllLeft = Normal | DeviceLeft | IpcLeft | Locked, + AllRight = DeviceRight, +}; +DECLARE_ENUM_FLAG_OPERATORS(KMemoryBlockDisableMergeAttribute); struct KMemoryInfo { - VAddr addr{}; - std::size_t size{}; - KMemoryState state{}; - KMemoryPermission perm{}; - KMemoryAttribute attribute{}; - KMemoryPermission original_perm{}; - u16 ipc_lock_count{}; - u16 device_use_count{}; + uintptr_t m_address; + size_t m_size; + KMemoryState m_state; + u16 m_device_disable_merge_left_count; + u16 m_device_disable_merge_right_count; + u16 m_ipc_lock_count; + u16 m_device_use_count; + u16 m_ipc_disable_merge_count; + KMemoryPermission m_permission; + KMemoryAttribute m_attribute; + KMemoryPermission m_original_permission; + KMemoryBlockDisableMergeAttribute m_disable_merge_attribute; constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { return { - addr, - size, - static_cast(state & KMemoryState::Mask), - static_cast(attribute & KMemoryAttribute::Mask), - static_cast(perm & KMemoryPermission::UserMask), - ipc_lock_count, - device_use_count, + .base_address = m_address, + .size = m_size, + .state = static_cast(m_state & KMemoryState::Mask), + .attribute = + static_cast(m_attribute & KMemoryAttribute::UserMask), + .permission = + static_cast(m_permission & KMemoryPermission::UserMask), + .ipc_count = m_ipc_lock_count, + .device_count = m_device_use_count, + .padding = {}, }; } - constexpr VAddr GetAddress() const { - return addr; + constexpr uintptr_t GetAddress() const { + return m_address; } - constexpr std::size_t GetSize() const { - return size; + + constexpr size_t GetSize() const { + return m_size; } - constexpr std::size_t GetNumPages() const { - return GetSize() / PageSize; + + constexpr size_t GetNumPages() const { + return this->GetSize() / PageSize; } - constexpr VAddr GetEndAddress() const { - return GetAddress() + GetSize(); + + constexpr uintptr_t GetEndAddress() const { + return this->GetAddress() + this->GetSize(); } - constexpr VAddr GetLastAddress() const { - return GetEndAddress() - 1; + + constexpr uintptr_t GetLastAddress() const { + return this->GetEndAddress() - 1; } + + constexpr u16 GetIpcLockCount() const { + return m_ipc_lock_count; + } + + constexpr u16 GetIpcDisableMergeCount() const { + return m_ipc_disable_merge_count; + } + constexpr KMemoryState GetState() const { - return state; - } - constexpr KMemoryAttribute GetAttribute() const { - return attribute; + return m_state; } + constexpr KMemoryPermission GetPermission() const { - return perm; + return m_permission; + } + + constexpr KMemoryPermission GetOriginalPermission() const { + return m_original_permission; + } + + constexpr KMemoryAttribute GetAttribute() const { + return m_attribute; + } + + constexpr KMemoryBlockDisableMergeAttribute GetDisableMergeAttribute() const { + return m_disable_merge_attribute; } }; -class KMemoryBlock final { - friend class KMemoryBlockManager; - +class KMemoryBlock : public Common::IntrusiveRedBlackTreeBaseNode { private: - VAddr addr{}; - std::size_t num_pages{}; - KMemoryState state{KMemoryState::None}; - u16 ipc_lock_count{}; - u16 device_use_count{}; - KMemoryPermission perm{KMemoryPermission::None}; - KMemoryPermission original_perm{KMemoryPermission::None}; - KMemoryAttribute attribute{KMemoryAttribute::None}; + u16 m_device_disable_merge_left_count{}; + u16 m_device_disable_merge_right_count{}; + VAddr m_address{}; + size_t m_num_pages{}; + KMemoryState m_memory_state{KMemoryState::None}; + u16 m_ipc_lock_count{}; + u16 m_device_use_count{}; + u16 m_ipc_disable_merge_count{}; + KMemoryPermission m_permission{KMemoryPermission::None}; + KMemoryPermission m_original_permission{KMemoryPermission::None}; + KMemoryAttribute m_attribute{KMemoryAttribute::None}; + KMemoryBlockDisableMergeAttribute m_disable_merge_attribute{ + KMemoryBlockDisableMergeAttribute::None}; public: static constexpr int Compare(const KMemoryBlock& lhs, const KMemoryBlock& rhs) { @@ -261,113 +306,357 @@ public: } public: - constexpr KMemoryBlock() = default; - constexpr KMemoryBlock(VAddr addr_, std::size_t num_pages_, KMemoryState state_, - KMemoryPermission perm_, KMemoryAttribute attribute_) - : addr{addr_}, num_pages(num_pages_), state{state_}, perm{perm_}, attribute{attribute_} {} - constexpr VAddr GetAddress() const { - return addr; + return m_address; } - constexpr std::size_t GetNumPages() const { - return num_pages; + constexpr size_t GetNumPages() const { + return m_num_pages; } - constexpr std::size_t GetSize() const { - return GetNumPages() * PageSize; + constexpr size_t GetSize() const { + return this->GetNumPages() * PageSize; } constexpr VAddr GetEndAddress() const { - return GetAddress() + GetSize(); + return this->GetAddress() + this->GetSize(); } constexpr VAddr GetLastAddress() const { - return GetEndAddress() - 1; + return this->GetEndAddress() - 1; + } + + constexpr u16 GetIpcLockCount() const { + return m_ipc_lock_count; + } + + constexpr u16 GetIpcDisableMergeCount() const { + return m_ipc_disable_merge_count; + } + + constexpr KMemoryPermission GetPermission() const { + return m_permission; + } + + constexpr KMemoryPermission GetOriginalPermission() const { + return m_original_permission; + } + + constexpr KMemoryAttribute GetAttribute() const { + return m_attribute; } constexpr KMemoryInfo GetMemoryInfo() const { return { - GetAddress(), GetSize(), state, perm, - attribute, original_perm, ipc_lock_count, device_use_count, + .m_address = this->GetAddress(), + .m_size = this->GetSize(), + .m_state = m_memory_state, + .m_device_disable_merge_left_count = m_device_disable_merge_left_count, + .m_device_disable_merge_right_count = m_device_disable_merge_right_count, + .m_ipc_lock_count = m_ipc_lock_count, + .m_device_use_count = m_device_use_count, + .m_ipc_disable_merge_count = m_ipc_disable_merge_count, + .m_permission = m_permission, + .m_attribute = m_attribute, + .m_original_permission = m_original_permission, + .m_disable_merge_attribute = m_disable_merge_attribute, }; } - void ShareToDevice(KMemoryPermission /*new_perm*/) { - ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared || - device_use_count == 0); - attribute |= KMemoryAttribute::DeviceShared; - const u16 new_use_count{++device_use_count}; - ASSERT(new_use_count > 0); +public: + explicit KMemoryBlock() = default; + + constexpr KMemoryBlock(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p, + KMemoryAttribute attr) + : Common::IntrusiveRedBlackTreeBaseNode(), m_address(addr), m_num_pages(np), + m_memory_state(ms), m_permission(p), m_attribute(attr) {} + + constexpr void Initialize(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p, + KMemoryAttribute attr) { + m_device_disable_merge_left_count = 0; + m_device_disable_merge_right_count = 0; + m_address = addr; + m_num_pages = np; + m_memory_state = ms; + m_ipc_lock_count = 0; + m_device_use_count = 0; + m_permission = p; + m_original_permission = KMemoryPermission::None; + m_attribute = attr; + m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None; } - void UnshareToDevice(KMemoryPermission /*new_perm*/) { - ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared); - const u16 prev_use_count{device_use_count--}; - ASSERT(prev_use_count > 0); - if (prev_use_count == 1) { - attribute &= ~KMemoryAttribute::DeviceShared; - } - } - -private: constexpr bool HasProperties(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) const { - constexpr KMemoryAttribute AttributeIgnoreMask{KMemoryAttribute::DontCareMask | - KMemoryAttribute::IpcLocked | - KMemoryAttribute::DeviceShared}; - return state == s && perm == p && - (attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask); + constexpr auto AttributeIgnoreMask = + KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; + return m_memory_state == s && m_permission == p && + (m_attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask); } constexpr bool HasSameProperties(const KMemoryBlock& rhs) const { - return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm && - attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count && - device_use_count == rhs.device_use_count; + return m_memory_state == rhs.m_memory_state && m_permission == rhs.m_permission && + m_original_permission == rhs.m_original_permission && + m_attribute == rhs.m_attribute && m_ipc_lock_count == rhs.m_ipc_lock_count && + m_device_use_count == rhs.m_device_use_count; } - constexpr bool Contains(VAddr start) const { - return GetAddress() <= start && start <= GetEndAddress(); + constexpr bool CanMergeWith(const KMemoryBlock& rhs) const { + return this->HasSameProperties(rhs) && + (m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllRight) == + KMemoryBlockDisableMergeAttribute::None && + (rhs.m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllLeft) == + KMemoryBlockDisableMergeAttribute::None; } - constexpr void Add(std::size_t count) { - ASSERT(count > 0); - ASSERT(GetAddress() + count * PageSize - 1 < GetEndAddress() + count * PageSize - 1); - - num_pages += count; + constexpr bool Contains(VAddr addr) const { + return this->GetAddress() <= addr && addr <= this->GetEndAddress(); } - constexpr void Update(KMemoryState new_state, KMemoryPermission new_perm, - KMemoryAttribute new_attribute) { - ASSERT(original_perm == KMemoryPermission::None); - ASSERT((attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::None); + constexpr void Add(const KMemoryBlock& added_block) { + ASSERT(added_block.GetNumPages() > 0); + ASSERT(this->GetAddress() + added_block.GetSize() - 1 < + this->GetEndAddress() + added_block.GetSize() - 1); - state = new_state; - perm = new_perm; - - attribute = static_cast( - new_attribute | - (attribute & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared))); + m_num_pages += added_block.GetNumPages(); + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute | added_block.m_disable_merge_attribute); + m_device_disable_merge_right_count = added_block.m_device_disable_merge_right_count; } - constexpr KMemoryBlock Split(VAddr split_addr) { - ASSERT(GetAddress() < split_addr); - ASSERT(Contains(split_addr)); - ASSERT(Common::IsAligned(split_addr, PageSize)); + constexpr void Update(KMemoryState s, KMemoryPermission p, KMemoryAttribute a, + bool set_disable_merge_attr, u8 set_mask, u8 clear_mask) { + ASSERT(m_original_permission == KMemoryPermission::None); + ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::None); - KMemoryBlock block; - block.addr = addr; - block.num_pages = (split_addr - GetAddress()) / PageSize; - block.state = state; - block.ipc_lock_count = ipc_lock_count; - block.device_use_count = device_use_count; - block.perm = perm; - block.original_perm = original_perm; - block.attribute = attribute; + m_memory_state = s; + m_permission = p; + m_attribute = static_cast( + a | (m_attribute & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared))); - addr = split_addr; - num_pages -= block.num_pages; + if (set_disable_merge_attr && set_mask != 0) { + m_disable_merge_attribute = m_disable_merge_attribute | + static_cast(set_mask); + } + if (clear_mask != 0) { + m_disable_merge_attribute = m_disable_merge_attribute & + static_cast(~clear_mask); + } + } - return block; + constexpr void Split(KMemoryBlock* block, VAddr addr) { + ASSERT(this->GetAddress() < addr); + ASSERT(this->Contains(addr)); + ASSERT(Common::IsAligned(addr, PageSize)); + + block->m_address = m_address; + block->m_num_pages = (addr - this->GetAddress()) / PageSize; + block->m_memory_state = m_memory_state; + block->m_ipc_lock_count = m_ipc_lock_count; + block->m_device_use_count = m_device_use_count; + block->m_permission = m_permission; + block->m_original_permission = m_original_permission; + block->m_attribute = m_attribute; + block->m_disable_merge_attribute = static_cast( + m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllLeft); + block->m_ipc_disable_merge_count = m_ipc_disable_merge_count; + block->m_device_disable_merge_left_count = m_device_disable_merge_left_count; + block->m_device_disable_merge_right_count = 0; + + m_address = addr; + m_num_pages -= block->m_num_pages; + + m_ipc_disable_merge_count = 0; + m_device_disable_merge_left_count = 0; + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute::AllRight); + } + + constexpr void UpdateDeviceDisableMergeStateForShareLeft( + [[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) { + // New permission/right aren't used. + if (left) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceLeft); + const u16 new_device_disable_merge_left_count = ++m_device_disable_merge_left_count; + ASSERT(new_device_disable_merge_left_count > 0); + } + } + + constexpr void UpdateDeviceDisableMergeStateForShareRight( + [[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) { + // New permission/left aren't used. + if (right) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceRight); + const u16 new_device_disable_merge_right_count = ++m_device_disable_merge_right_count; + ASSERT(new_device_disable_merge_right_count > 0); + } + } + + constexpr void UpdateDeviceDisableMergeStateForShare(KMemoryPermission new_perm, bool left, + bool right) { + this->UpdateDeviceDisableMergeStateForShareLeft(new_perm, left, right); + this->UpdateDeviceDisableMergeStateForShareRight(new_perm, left, right); + } + + constexpr void ShareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left, + bool right) { + // New permission isn't used. + + // We must either be shared or have a zero lock count. + ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared || + m_device_use_count == 0); + + // Share. + const u16 new_count = ++m_device_use_count; + ASSERT(new_count > 0); + + m_attribute = static_cast(m_attribute | KMemoryAttribute::DeviceShared); + + this->UpdateDeviceDisableMergeStateForShare(new_perm, left, right); + } + + constexpr void UpdateDeviceDisableMergeStateForUnshareLeft( + [[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) { + // New permission/right aren't used. + + if (left) { + if (!m_device_disable_merge_left_count) { + return; + } + --m_device_disable_merge_left_count; + } + + m_device_disable_merge_left_count = + std::min(m_device_disable_merge_left_count, m_device_use_count); + + if (m_device_disable_merge_left_count == 0) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute::DeviceLeft); + } + } + + constexpr void UpdateDeviceDisableMergeStateForUnshareRight( + [[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) { + // New permission/left aren't used. + + if (right) { + const u16 old_device_disable_merge_right_count = m_device_disable_merge_right_count--; + ASSERT(old_device_disable_merge_right_count > 0); + if (old_device_disable_merge_right_count == 1) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute::DeviceRight); + } + } + } + + constexpr void UpdateDeviceDisableMergeStateForUnshare(KMemoryPermission new_perm, bool left, + bool right) { + this->UpdateDeviceDisableMergeStateForUnshareLeft(new_perm, left, right); + this->UpdateDeviceDisableMergeStateForUnshareRight(new_perm, left, right); + } + + constexpr void UnshareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left, + bool right) { + // New permission isn't used. + + // We must be shared. + ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared); + + // Unhare. + const u16 old_count = m_device_use_count--; + ASSERT(old_count > 0); + + if (old_count == 1) { + m_attribute = + static_cast(m_attribute & ~KMemoryAttribute::DeviceShared); + } + + this->UpdateDeviceDisableMergeStateForUnshare(new_perm, left, right); + } + + constexpr void UnshareToDeviceRight([[maybe_unused]] KMemoryPermission new_perm, bool left, + bool right) { + // New permission isn't used. + + // We must be shared. + ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared); + + // Unhare. + const u16 old_count = m_device_use_count--; + ASSERT(old_count > 0); + + if (old_count == 1) { + m_attribute = + static_cast(m_attribute & ~KMemoryAttribute::DeviceShared); + } + + this->UpdateDeviceDisableMergeStateForUnshareRight(new_perm, left, right); + } + + constexpr void LockForIpc(KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) { + // We must either be locked or have a zero lock count. + ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::IpcLocked || + m_ipc_lock_count == 0); + + // Lock. + const u16 new_lock_count = ++m_ipc_lock_count; + ASSERT(new_lock_count > 0); + + // If this is our first lock, update our permissions. + if (new_lock_count == 1) { + ASSERT(m_original_permission == KMemoryPermission::None); + ASSERT((m_permission | new_perm | KMemoryPermission::NotMapped) == + (m_permission | KMemoryPermission::NotMapped)); + ASSERT((m_permission & KMemoryPermission::UserExecute) != + KMemoryPermission::UserExecute || + (new_perm == KMemoryPermission::UserRead)); + m_original_permission = m_permission; + m_permission = static_cast( + (new_perm & KMemoryPermission::IpcLockChangeMask) | + (m_original_permission & ~KMemoryPermission::IpcLockChangeMask)); + } + m_attribute = static_cast(m_attribute | KMemoryAttribute::IpcLocked); + + if (left) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::IpcLeft); + const u16 new_ipc_disable_merge_count = ++m_ipc_disable_merge_count; + ASSERT(new_ipc_disable_merge_count > 0); + } + } + + constexpr void UnlockForIpc([[maybe_unused]] KMemoryPermission new_perm, bool left, + [[maybe_unused]] bool right) { + // New permission isn't used. + + // We must be locked. + ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::IpcLocked); + + // Unlock. + const u16 old_lock_count = m_ipc_lock_count--; + ASSERT(old_lock_count > 0); + + // If this is our last unlock, update our permissions. + if (old_lock_count == 1) { + ASSERT(m_original_permission != KMemoryPermission::None); + m_permission = m_original_permission; + m_original_permission = KMemoryPermission::None; + m_attribute = static_cast(m_attribute & ~KMemoryAttribute::IpcLocked); + } + + if (left) { + const u16 old_ipc_disable_merge_count = m_ipc_disable_merge_count--; + ASSERT(old_ipc_disable_merge_count > 0); + if (old_ipc_disable_merge_count == 1) { + m_disable_merge_attribute = static_cast( + m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute::IpcLeft); + } + } + } + + constexpr KMemoryBlockDisableMergeAttribute GetDisableMergeAttribute() const { + return m_disable_merge_attribute; } }; static_assert(std::is_trivially_destructible::value); diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp index 3ddb998..cf4c1e3 100644 --- a/src/core/hle/kernel/k_memory_block_manager.cpp +++ b/src/core/hle/kernel/k_memory_block_manager.cpp @@ -2,221 +2,336 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_memory_block_manager.h" -#include "core/hle/kernel/memory_types.h" namespace Kernel { -KMemoryBlockManager::KMemoryBlockManager(VAddr start_addr_, VAddr end_addr_) - : start_addr{start_addr_}, end_addr{end_addr_} { - const u64 num_pages{(end_addr - start_addr) / PageSize}; - memory_block_tree.emplace_back(start_addr, num_pages, KMemoryState::Free, - KMemoryPermission::None, KMemoryAttribute::None); +KMemoryBlockManager::KMemoryBlockManager() = default; + +Result KMemoryBlockManager::Initialize(VAddr st, VAddr nd, KMemoryBlockSlabManager* slab_manager) { + // Allocate a block to encapsulate the address space, insert it into the tree. + KMemoryBlock* start_block = slab_manager->Allocate(); + R_UNLESS(start_block != nullptr, ResultOutOfResource); + + // Set our start and end. + m_start_address = st; + m_end_address = nd; + ASSERT(Common::IsAligned(m_start_address, PageSize)); + ASSERT(Common::IsAligned(m_end_address, PageSize)); + + // Initialize and insert the block. + start_block->Initialize(m_start_address, (m_end_address - m_start_address) / PageSize, + KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None); + m_memory_block_tree.insert(*start_block); + + R_SUCCEED(); } -KMemoryBlockManager::iterator KMemoryBlockManager::FindIterator(VAddr addr) { - auto node{memory_block_tree.begin()}; - while (node != end()) { - const VAddr node_end_addr{node->GetNumPages() * PageSize + node->GetAddress()}; - if (node->GetAddress() <= addr && node_end_addr - 1 >= addr) { - return node; - } - node = std::next(node); +void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager, + HostUnmapCallback&& host_unmap_callback) { + // Erase every block until we have none left. + auto it = m_memory_block_tree.begin(); + while (it != m_memory_block_tree.end()) { + KMemoryBlock* block = std::addressof(*it); + it = m_memory_block_tree.erase(it); + slab_manager->Free(block); + host_unmap_callback(block->GetAddress(), block->GetSize()); } - return end(); + + ASSERT(m_memory_block_tree.empty()); } -VAddr KMemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages, - std::size_t num_pages, std::size_t align, - std::size_t offset, std::size_t guard_pages) { - if (num_pages == 0) { - return {}; - } +VAddr KMemoryBlockManager::FindFreeArea(VAddr region_start, size_t region_num_pages, + size_t num_pages, size_t alignment, size_t offset, + size_t guard_pages) const { + if (num_pages > 0) { + const VAddr region_end = region_start + region_num_pages * PageSize; + const VAddr region_last = region_end - 1; + for (const_iterator it = this->FindIterator(region_start); it != m_memory_block_tree.cend(); + it++) { + const KMemoryInfo info = it->GetMemoryInfo(); + if (region_last < info.GetAddress()) { + break; + } + if (info.m_state != KMemoryState::Free) { + continue; + } - const VAddr region_end{region_start + region_num_pages * PageSize}; - const VAddr region_last{region_end - 1}; - for (auto it{FindIterator(region_start)}; it != memory_block_tree.cend(); it++) { - const auto info{it->GetMemoryInfo()}; - if (region_last < info.GetAddress()) { - break; - } + VAddr area = (info.GetAddress() <= region_start) ? region_start : info.GetAddress(); + area += guard_pages * PageSize; - if (info.state != KMemoryState::Free) { - continue; - } + const VAddr offset_area = Common::AlignDown(area, alignment) + offset; + area = (area <= offset_area) ? offset_area : offset_area + alignment; - VAddr area{(info.GetAddress() <= region_start) ? region_start : info.GetAddress()}; - area += guard_pages * PageSize; + const VAddr area_end = area + num_pages * PageSize + guard_pages * PageSize; + const VAddr area_last = area_end - 1; - const VAddr offset_area{Common::AlignDown(area, align) + offset}; - area = (area <= offset_area) ? offset_area : offset_area + align; - - const VAddr area_end{area + num_pages * PageSize + guard_pages * PageSize}; - const VAddr area_last{area_end - 1}; - - if (info.GetAddress() <= area && area < area_last && area_last <= region_last && - area_last <= info.GetLastAddress()) { - return area; + if (info.GetAddress() <= area && area < area_last && area_last <= region_last && + area_last <= info.GetLastAddress()) { + return area; + } } } return {}; } -void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state, - KMemoryPermission prev_perm, KMemoryAttribute prev_attribute, - KMemoryState state, KMemoryPermission perm, - KMemoryAttribute attribute) { - const VAddr update_end_addr{addr + num_pages * PageSize}; - iterator node{memory_block_tree.begin()}; +void KMemoryBlockManager::CoalesceForUpdate(KMemoryBlockManagerUpdateAllocator* allocator, + VAddr address, size_t num_pages) { + // Find the iterator now that we've updated. + iterator it = this->FindIterator(address); + if (address != m_start_address) { + it--; + } - prev_attribute |= KMemoryAttribute::IpcAndDeviceMapped; - - while (node != memory_block_tree.end()) { - KMemoryBlock* block{&(*node)}; - iterator next_node{std::next(node)}; - const VAddr cur_addr{block->GetAddress()}; - const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; - - if (addr < cur_end_addr && cur_addr < update_end_addr) { - if (!block->HasProperties(prev_state, prev_perm, prev_attribute)) { - node = next_node; - continue; - } - - iterator new_node{node}; - if (addr > cur_addr) { - memory_block_tree.insert(node, block->Split(addr)); - } - - if (update_end_addr < cur_end_addr) { - new_node = memory_block_tree.insert(node, block->Split(update_end_addr)); - } - - new_node->Update(state, perm, attribute); - - MergeAdjacent(new_node, next_node); - } - - if (cur_end_addr - 1 >= update_end_addr - 1) { + // Coalesce blocks that we can. + while (true) { + iterator prev = it++; + if (it == m_memory_block_tree.end()) { break; } - node = next_node; - } -} - -void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState state, - KMemoryPermission perm, KMemoryAttribute attribute) { - const VAddr update_end_addr{addr + num_pages * PageSize}; - iterator node{memory_block_tree.begin()}; - - while (node != memory_block_tree.end()) { - KMemoryBlock* block{&(*node)}; - iterator next_node{std::next(node)}; - const VAddr cur_addr{block->GetAddress()}; - const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; - - if (addr < cur_end_addr && cur_addr < update_end_addr) { - iterator new_node{node}; - - if (addr > cur_addr) { - memory_block_tree.insert(node, block->Split(addr)); - } - - if (update_end_addr < cur_end_addr) { - new_node = memory_block_tree.insert(node, block->Split(update_end_addr)); - } - - new_node->Update(state, perm, attribute); - - MergeAdjacent(new_node, next_node); + if (prev->CanMergeWith(*it)) { + KMemoryBlock* block = std::addressof(*it); + m_memory_block_tree.erase(it); + prev->Add(*block); + allocator->Free(block); + it = prev; } - if (cur_end_addr - 1 >= update_end_addr - 1) { + if (address + num_pages * PageSize < it->GetMemoryInfo().GetEndAddress()) { break; } - - node = next_node; } } -void KMemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, +void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, + size_t num_pages, KMemoryState state, KMemoryPermission perm, + KMemoryAttribute attr, + KMemoryBlockDisableMergeAttribute set_disable_attr, + KMemoryBlockDisableMergeAttribute clear_disable_attr) { + // Ensure for auditing that we never end up with an invalid tree. + KScopedMemoryBlockManagerAuditor auditor(this); + ASSERT(Common::IsAligned(address, PageSize)); + ASSERT((attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) == + KMemoryAttribute::None); + + VAddr cur_address = address; + size_t remaining_pages = num_pages; + iterator it = this->FindIterator(address); + + while (remaining_pages > 0) { + const size_t remaining_size = remaining_pages * PageSize; + KMemoryInfo cur_info = it->GetMemoryInfo(); + if (it->HasProperties(state, perm, attr)) { + // If we already have the right properties, just advance. + if (cur_address + remaining_size < cur_info.GetEndAddress()) { + remaining_pages = 0; + cur_address += remaining_size; + } else { + remaining_pages = + (cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize; + cur_address = cur_info.GetEndAddress(); + } + } else { + // If we need to, create a new block before and insert it. + if (cur_info.GetAddress() != cur_address) { + KMemoryBlock* new_block = allocator->Allocate(); + + it->Split(new_block, cur_address); + it = m_memory_block_tree.insert(*new_block); + it++; + + cur_info = it->GetMemoryInfo(); + cur_address = cur_info.GetAddress(); + } + + // If we need to, create a new block after and insert it. + if (cur_info.GetSize() > remaining_size) { + KMemoryBlock* new_block = allocator->Allocate(); + + it->Split(new_block, cur_address + remaining_size); + it = m_memory_block_tree.insert(*new_block); + + cur_info = it->GetMemoryInfo(); + } + + // Update block state. + it->Update(state, perm, attr, cur_address == address, static_cast(set_disable_attr), + static_cast(clear_disable_attr)); + cur_address += cur_info.GetSize(); + remaining_pages -= cur_info.GetNumPages(); + } + it++; + } + + this->CoalesceForUpdate(allocator, address, num_pages); +} + +void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, + VAddr address, size_t num_pages, KMemoryState test_state, + KMemoryPermission test_perm, KMemoryAttribute test_attr, + KMemoryState state, KMemoryPermission perm, + KMemoryAttribute attr) { + // Ensure for auditing that we never end up with an invalid tree. + KScopedMemoryBlockManagerAuditor auditor(this); + ASSERT(Common::IsAligned(address, PageSize)); + ASSERT((attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) == + KMemoryAttribute::None); + + VAddr cur_address = address; + size_t remaining_pages = num_pages; + iterator it = this->FindIterator(address); + + while (remaining_pages > 0) { + const size_t remaining_size = remaining_pages * PageSize; + KMemoryInfo cur_info = it->GetMemoryInfo(); + if (it->HasProperties(test_state, test_perm, test_attr) && + !it->HasProperties(state, perm, attr)) { + // If we need to, create a new block before and insert it. + if (cur_info.GetAddress() != cur_address) { + KMemoryBlock* new_block = allocator->Allocate(); + + it->Split(new_block, cur_address); + it = m_memory_block_tree.insert(*new_block); + it++; + + cur_info = it->GetMemoryInfo(); + cur_address = cur_info.GetAddress(); + } + + // If we need to, create a new block after and insert it. + if (cur_info.GetSize() > remaining_size) { + KMemoryBlock* new_block = allocator->Allocate(); + + it->Split(new_block, cur_address + remaining_size); + it = m_memory_block_tree.insert(*new_block); + + cur_info = it->GetMemoryInfo(); + } + + // Update block state. + it->Update(state, perm, attr, false, 0, 0); + cur_address += cur_info.GetSize(); + remaining_pages -= cur_info.GetNumPages(); + } else { + // If we already have the right properties, just advance. + if (cur_address + remaining_size < cur_info.GetEndAddress()) { + remaining_pages = 0; + cur_address += remaining_size; + } else { + remaining_pages = + (cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize; + cur_address = cur_info.GetEndAddress(); + } + } + it++; + } + + this->CoalesceForUpdate(allocator, address, num_pages); +} + +void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, + size_t num_pages, MemoryBlockLockFunction lock_func, KMemoryPermission perm) { - const VAddr update_end_addr{addr + num_pages * PageSize}; - iterator node{memory_block_tree.begin()}; + // Ensure for auditing that we never end up with an invalid tree. + KScopedMemoryBlockManagerAuditor auditor(this); + ASSERT(Common::IsAligned(address, PageSize)); - while (node != memory_block_tree.end()) { - KMemoryBlock* block{&(*node)}; - iterator next_node{std::next(node)}; - const VAddr cur_addr{block->GetAddress()}; - const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; + VAddr cur_address = address; + size_t remaining_pages = num_pages; + iterator it = this->FindIterator(address); - if (addr < cur_end_addr && cur_addr < update_end_addr) { - iterator new_node{node}; + const VAddr end_address = address + (num_pages * PageSize); - if (addr > cur_addr) { - memory_block_tree.insert(node, block->Split(addr)); - } + while (remaining_pages > 0) { + const size_t remaining_size = remaining_pages * PageSize; + KMemoryInfo cur_info = it->GetMemoryInfo(); - if (update_end_addr < cur_end_addr) { - new_node = memory_block_tree.insert(node, block->Split(update_end_addr)); - } + // If we need to, create a new block before and insert it. + if (cur_info.m_address != cur_address) { + KMemoryBlock* new_block = allocator->Allocate(); - lock_func(new_node, perm); + it->Split(new_block, cur_address); + it = m_memory_block_tree.insert(*new_block); + it++; - MergeAdjacent(new_node, next_node); + cur_info = it->GetMemoryInfo(); + cur_address = cur_info.GetAddress(); } - if (cur_end_addr - 1 >= update_end_addr - 1) { - break; + if (cur_info.GetSize() > remaining_size) { + // If we need to, create a new block after and insert it. + KMemoryBlock* new_block = allocator->Allocate(); + + it->Split(new_block, cur_address + remaining_size); + it = m_memory_block_tree.insert(*new_block); + + cur_info = it->GetMemoryInfo(); } - node = next_node; + // Call the locked update function. + (std::addressof(*it)->*lock_func)(perm, cur_info.GetAddress() == address, + cur_info.GetEndAddress() == end_address); + cur_address += cur_info.GetSize(); + remaining_pages -= cur_info.GetNumPages(); + it++; } + + this->CoalesceForUpdate(allocator, address, num_pages); } -void KMemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) { - const_iterator it{FindIterator(start)}; - KMemoryInfo info{}; - do { - info = it->GetMemoryInfo(); - func(info); - it = std::next(it); - } while (info.addr + info.size - 1 < end - 1 && it != cend()); -} +// Debug. +bool KMemoryBlockManager::CheckState() const { + // Loop over every block, ensuring that we are sorted and coalesced. + auto it = m_memory_block_tree.cbegin(); + auto prev = it++; + while (it != m_memory_block_tree.cend()) { + const KMemoryInfo prev_info = prev->GetMemoryInfo(); + const KMemoryInfo cur_info = it->GetMemoryInfo(); -void KMemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) { - KMemoryBlock* block{&(*it)}; - - auto EraseIt = [&](const iterator it_to_erase) { - if (next_it == it_to_erase) { - next_it = std::next(next_it); + // Sequential blocks which can be merged should be merged. + if (prev->CanMergeWith(*it)) { + return false; } - memory_block_tree.erase(it_to_erase); - }; - if (it != memory_block_tree.begin()) { - KMemoryBlock* prev{&(*std::prev(it))}; + // Sequential blocks should be sequential. + if (prev_info.GetEndAddress() != cur_info.GetAddress()) { + return false; + } - if (block->HasSameProperties(*prev)) { - const iterator prev_it{std::prev(it)}; + // If the block is ipc locked, it must have a count. + if ((cur_info.m_attribute & KMemoryAttribute::IpcLocked) != KMemoryAttribute::None && + cur_info.m_ipc_lock_count == 0) { + return false; + } - prev->Add(block->GetNumPages()); - EraseIt(it); + // If the block is device shared, it must have a count. + if ((cur_info.m_attribute & KMemoryAttribute::DeviceShared) != KMemoryAttribute::None && + cur_info.m_device_use_count == 0) { + return false; + } - it = prev_it; - block = prev; + // Advance the iterator. + prev = it++; + } + + // Our loop will miss checking the last block, potentially, so check it. + if (prev != m_memory_block_tree.cend()) { + const KMemoryInfo prev_info = prev->GetMemoryInfo(); + // If the block is ipc locked, it must have a count. + if ((prev_info.m_attribute & KMemoryAttribute::IpcLocked) != KMemoryAttribute::None && + prev_info.m_ipc_lock_count == 0) { + return false; + } + + // If the block is device shared, it must have a count. + if ((prev_info.m_attribute & KMemoryAttribute::DeviceShared) != KMemoryAttribute::None && + prev_info.m_device_use_count == 0) { + return false; } } - if (it != cend()) { - const KMemoryBlock* const next{&(*std::next(it))}; - - if (block->HasSameProperties(*next)) { - block->Add(next->GetNumPages()); - EraseIt(std::next(it)); - } - } + return true; } } // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h index e14741b..d382722 100644 --- a/src/core/hle/kernel/k_memory_block_manager.h +++ b/src/core/hle/kernel/k_memory_block_manager.h @@ -3,64 +3,156 @@ #pragma once +#include #include -#include +#include "common/common_funcs.h" #include "common/common_types.h" +#include "core/hle/kernel/k_dynamic_resource_manager.h" #include "core/hle/kernel/k_memory_block.h" namespace Kernel { +class KMemoryBlockManagerUpdateAllocator { +public: + static constexpr size_t MaxBlocks = 2; + +private: + std::array m_blocks{}; + size_t m_index{MaxBlocks}; + KMemoryBlockSlabManager* m_slab_manager{}; + +private: + Result Initialize(size_t num_blocks) { + // Check num blocks. + ASSERT(num_blocks <= MaxBlocks); + + // Set index. + m_index = MaxBlocks - num_blocks; + + // Allocate the blocks. + for (size_t i = 0; i < num_blocks && i < MaxBlocks; ++i) { + m_blocks[m_index + i] = m_slab_manager->Allocate(); + R_UNLESS(m_blocks[m_index + i] != nullptr, ResultOutOfResource); + } + + R_SUCCEED(); + } + +public: + KMemoryBlockManagerUpdateAllocator(Result* out_result, KMemoryBlockSlabManager* sm, + size_t num_blocks = MaxBlocks) + : m_slab_manager(sm) { + *out_result = this->Initialize(num_blocks); + } + + ~KMemoryBlockManagerUpdateAllocator() { + for (const auto& block : m_blocks) { + if (block != nullptr) { + m_slab_manager->Free(block); + } + } + } + + KMemoryBlock* Allocate() { + ASSERT(m_index < MaxBlocks); + ASSERT(m_blocks[m_index] != nullptr); + KMemoryBlock* block = nullptr; + std::swap(block, m_blocks[m_index++]); + return block; + } + + void Free(KMemoryBlock* block) { + ASSERT(m_index <= MaxBlocks); + ASSERT(block != nullptr); + if (m_index == 0) { + m_slab_manager->Free(block); + } else { + m_blocks[--m_index] = block; + } + } +}; + class KMemoryBlockManager final { public: - using MemoryBlockTree = std::list; + using MemoryBlockTree = + Common::IntrusiveRedBlackTreeBaseTraits::TreeType; + using MemoryBlockLockFunction = void (KMemoryBlock::*)(KMemoryPermission new_perm, bool left, + bool right); using iterator = MemoryBlockTree::iterator; using const_iterator = MemoryBlockTree::const_iterator; public: - KMemoryBlockManager(VAddr start_addr_, VAddr end_addr_); + KMemoryBlockManager(); + + using HostUnmapCallback = std::function; + + Result Initialize(VAddr st, VAddr nd, KMemoryBlockSlabManager* slab_manager); + void Finalize(KMemoryBlockSlabManager* slab_manager, HostUnmapCallback&& host_unmap_callback); iterator end() { - return memory_block_tree.end(); + return m_memory_block_tree.end(); } const_iterator end() const { - return memory_block_tree.end(); + return m_memory_block_tree.end(); } const_iterator cend() const { - return memory_block_tree.cend(); + return m_memory_block_tree.cend(); } - iterator FindIterator(VAddr addr); + VAddr FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages, + size_t alignment, size_t offset, size_t guard_pages) const; - VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages, - std::size_t align, std::size_t offset, std::size_t guard_pages); + void Update(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, size_t num_pages, + KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr, + KMemoryBlockDisableMergeAttribute set_disable_attr, + KMemoryBlockDisableMergeAttribute clear_disable_attr); + void UpdateLock(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, size_t num_pages, + MemoryBlockLockFunction lock_func, KMemoryPermission perm); - void Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state, - KMemoryPermission prev_perm, KMemoryAttribute prev_attribute, KMemoryState state, - KMemoryPermission perm, KMemoryAttribute attribute); + void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, + size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm, + KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm, + KMemoryAttribute attr); - void Update(VAddr addr, std::size_t num_pages, KMemoryState state, - KMemoryPermission perm = KMemoryPermission::None, - KMemoryAttribute attribute = KMemoryAttribute::None); + iterator FindIterator(VAddr address) const { + return m_memory_block_tree.find(KMemoryBlock( + address, 1, KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None)); + } - using LockFunc = std::function; - void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, - KMemoryPermission perm); + const KMemoryBlock* FindBlock(VAddr address) const { + if (const_iterator it = this->FindIterator(address); it != m_memory_block_tree.end()) { + return std::addressof(*it); + } - using IterateFunc = std::function; - void IterateForRange(VAddr start, VAddr end, IterateFunc&& func); + return nullptr; + } - KMemoryBlock& FindBlock(VAddr addr) { - return *FindIterator(addr); + // Debug. + bool CheckState() const; + +private: + void CoalesceForUpdate(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, + size_t num_pages); + + MemoryBlockTree m_memory_block_tree; + VAddr m_start_address{}; + VAddr m_end_address{}; +}; + +class KScopedMemoryBlockManagerAuditor { +public: + explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager* m) : m_manager(m) { + ASSERT(m_manager->CheckState()); + } + explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager& m) + : KScopedMemoryBlockManagerAuditor(std::addressof(m)) {} + ~KScopedMemoryBlockManagerAuditor() { + ASSERT(m_manager->CheckState()); } private: - void MergeAdjacent(iterator it, iterator& next_it); - - [[maybe_unused]] const VAddr start_addr; - [[maybe_unused]] const VAddr end_addr; - - MemoryBlockTree memory_block_tree; + KMemoryBlockManager* m_manager; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_layout.cpp b/src/core/hle/kernel/k_memory_layout.cpp index 55dc296..72c3ee4 100644 --- a/src/core/hle/kernel/k_memory_layout.cpp +++ b/src/core/hle/kernel/k_memory_layout.cpp @@ -153,13 +153,9 @@ void KMemoryLayout::InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_ } } -size_t KMemoryLayout::GetResourceRegionSizeForInit() { - // Calculate resource region size based on whether we allow extra threads. - const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit(); - size_t resource_region_size = - KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0); - - return resource_region_size; +size_t KMemoryLayout::GetResourceRegionSizeForInit(bool use_extra_resource) { + return KernelResourceSize + KSystemControl::SecureAppletMemorySize + + (use_extra_resource ? KernelSlabHeapAdditionalSize + KernelPageBufferAdditionalSize : 0); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h index 884fc62..fd6e1d3 100644 --- a/src/core/hle/kernel/k_memory_layout.h +++ b/src/core/hle/kernel/k_memory_layout.h @@ -60,10 +60,12 @@ constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB; constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax; // NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. -constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000; +constexpr size_t KernelPageBufferHeapSize = 0x3E0000; +constexpr size_t KernelSlabHeapAdditionalSize = 0x148000; +constexpr size_t KernelPageBufferAdditionalSize = 0x33C000; -constexpr std::size_t KernelResourceSize = - KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize; +constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + + KernelSlabHeapSize + KernelPageBufferHeapSize; constexpr bool IsKernelAddressKey(VAddr key) { return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; @@ -168,6 +170,11 @@ public: KMemoryRegionType_VirtualDramKernelTraceBuffer)); } + const KMemoryRegion& GetSecureAppletMemoryRegion() { + return Dereference(GetVirtualMemoryRegionTree().FindByType( + KMemoryRegionType_VirtualDramKernelSecureAppletMemory)); + } + const KMemoryRegion& GetVirtualLinearRegion(VAddr address) const { return Dereference(FindVirtualLinear(address)); } @@ -229,7 +236,7 @@ public: void InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start, VAddr linear_virtual_start); - static size_t GetResourceRegionSizeForInit(); + static size_t GetResourceRegionSizeForInit(bool use_extra_resource); auto GetKernelRegionExtents() const { return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel); @@ -279,6 +286,10 @@ public: return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( KMemoryRegionType_DramKernelSlab); } + auto GetKernelSecureAppletMemoryRegionPhysicalExtents() { + return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + KMemoryRegionType_DramKernelSecureAppletMemory); + } auto GetKernelPageTableHeapRegionPhysicalExtents() const { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( KMemoryRegionType_DramKernelPtHeap); diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index 5b0a996..bd33571 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp @@ -29,43 +29,44 @@ constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) { } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) { return KMemoryManager::Pool::SystemNonSecure; } else { - ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool"); - return {}; + UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool"); } } } // namespace -KMemoryManager::KMemoryManager(Core::System& system_) - : system{system_}, pool_locks{ - KLightLock{system_.Kernel()}, - KLightLock{system_.Kernel()}, - KLightLock{system_.Kernel()}, - KLightLock{system_.Kernel()}, - } {} +KMemoryManager::KMemoryManager(Core::System& system) + : m_system{system}, m_memory_layout{system.Kernel().MemoryLayout()}, + m_pool_locks{ + KLightLock{system.Kernel()}, + KLightLock{system.Kernel()}, + KLightLock{system.Kernel()}, + KLightLock{system.Kernel()}, + } {} void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) { // Clear the management region to zero. const VAddr management_region_end = management_region + management_region_size; + // std::memset(GetVoidPointer(management_region), 0, management_region_size); // Reset our manager count. - num_managers = 0; + m_num_managers = 0; // Traverse the virtual memory layout tree, initializing each manager as appropriate. - while (num_managers != MaxManagerCount) { + while (m_num_managers != MaxManagerCount) { // Locate the region that should initialize the current manager. PAddr region_address = 0; size_t region_size = 0; Pool region_pool = Pool::Count; - for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { + for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { // We only care about regions that we need to create managers for. if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { continue; } // We want to initialize the managers in order. - if (it.GetAttributes() != num_managers) { + if (it.GetAttributes() != m_num_managers) { continue; } @@ -97,8 +98,8 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio } // Initialize a new manager for the region. - Impl* manager = std::addressof(managers[num_managers++]); - ASSERT(num_managers <= managers.size()); + Impl* manager = std::addressof(m_managers[m_num_managers++]); + ASSERT(m_num_managers <= m_managers.size()); const size_t cur_size = manager->Initialize(region_address, region_size, management_region, management_region_end, region_pool); @@ -107,13 +108,13 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio // Insert the manager into the pool list. const auto region_pool_index = static_cast(region_pool); - if (pool_managers_tail[region_pool_index] == nullptr) { - pool_managers_head[region_pool_index] = manager; + if (m_pool_managers_tail[region_pool_index] == nullptr) { + m_pool_managers_head[region_pool_index] = manager; } else { - pool_managers_tail[region_pool_index]->SetNext(manager); - manager->SetPrev(pool_managers_tail[region_pool_index]); + m_pool_managers_tail[region_pool_index]->SetNext(manager); + manager->SetPrev(m_pool_managers_tail[region_pool_index]); } - pool_managers_tail[region_pool_index] = manager; + m_pool_managers_tail[region_pool_index] = manager; } // Free each region to its corresponding heap. @@ -121,11 +122,10 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress(); const PAddr ini_end = ini_start + InitialProcessBinarySizeMax; const PAddr ini_last = ini_end - 1; - for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { + for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { // Get the manager for the region. - auto index = it.GetAttributes(); - auto& manager = managers[index]; + auto& manager = m_managers[it.GetAttributes()]; const PAddr cur_start = it.GetAddress(); const PAddr cur_last = it.GetLastAddress(); @@ -162,11 +162,19 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio } // Update the used size for all managers. - for (size_t i = 0; i < num_managers; ++i) { - managers[i].SetInitialUsedHeapSize(reserved_sizes[i]); + for (size_t i = 0; i < m_num_managers; ++i) { + m_managers[i].SetInitialUsedHeapSize(reserved_sizes[i]); } } +Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) { + UNREACHABLE(); +} + +void KMemoryManager::FinalizeOptimizedMemory(u64 process_id, Pool pool) { + UNREACHABLE(); +} + PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) { // Early return if we're allocating no pages. if (num_pages == 0) { @@ -175,7 +183,7 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p // Lock the pool that we're allocating from. const auto [pool, dir] = DecodeOption(option); - KScopedLightLock lk(pool_locks[static_cast(pool)]); + KScopedLightLock lk(m_pool_locks[static_cast(pool)]); // Choose a heap based on our page size request. const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages); @@ -185,7 +193,7 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p PAddr allocated_block = 0; for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr; chosen_manager = this->GetNextManager(chosen_manager, dir)) { - allocated_block = chosen_manager->AllocateBlock(heap_index, true); + allocated_block = chosen_manager->AllocateAligned(heap_index, num_pages, align_pages); if (allocated_block != 0) { break; } @@ -196,10 +204,9 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p return 0; } - // If we allocated more than we need, free some. - const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index); - if (allocated_pages > num_pages) { - chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages); + // Maintain the optimized memory bitmap, if we should. + if (m_has_optimized_process[static_cast(pool)]) { + UNIMPLEMENTED(); } // Open the first reference to the pages. @@ -209,20 +216,21 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p } Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, - Direction dir, bool random) { + Direction dir, bool unoptimized, bool random) { // Choose a heap based on our page size request. const s32 heap_index = KPageHeap::GetBlockIndex(num_pages); R_UNLESS(0 <= heap_index, ResultOutOfMemory); // Ensure that we don't leave anything un-freed. - auto group_guard = SCOPE_GUARD({ + ON_RESULT_FAILURE { for (const auto& it : out->Nodes()) { - auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress()); - const size_t num_pages_to_free = - std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); - manager.Free(it.GetAddress(), num_pages_to_free); + auto& manager = this->GetManager(it.GetAddress()); + const size_t node_num_pages = std::min( + it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); + manager.Free(it.GetAddress(), node_num_pages); } - }); + out->Finalize(); + }; // Keep allocating until we've allocated all our pages. for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) { @@ -236,12 +244,17 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, break; } - // Safely add it to our group. - { - auto block_guard = - SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); }); - R_TRY(out->AddBlock(allocated_block, pages_per_alloc)); - block_guard.Cancel(); + // Ensure we don't leak the block if we fail. + ON_RESULT_FAILURE_2 { + cur_manager->Free(allocated_block, pages_per_alloc); + }; + + // Add the block to our group. + R_TRY(out->AddBlock(allocated_block, pages_per_alloc)); + + // Maintain the optimized memory bitmap, if we should. + if (unoptimized) { + UNIMPLEMENTED(); } num_pages -= pages_per_alloc; @@ -253,8 +266,7 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, R_UNLESS(num_pages == 0, ResultOutOfMemory); // We succeeded! - group_guard.Cancel(); - return ResultSuccess; + R_SUCCEED(); } Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) { @@ -266,10 +278,11 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op // Lock the pool that we're allocating from. const auto [pool, dir] = DecodeOption(option); - KScopedLightLock lk(pool_locks[static_cast(pool)]); + KScopedLightLock lk(m_pool_locks[static_cast(pool)]); // Allocate the page group. - R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false)); + R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, + m_has_optimized_process[static_cast(pool)], true)); // Open the first reference to the pages. for (const auto& block : out->Nodes()) { @@ -277,7 +290,7 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op size_t remaining_pages = block.GetNumPages(); while (remaining_pages > 0) { // Get the manager for the current address. - auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address); + auto& manager = this->GetManager(cur_address); // Process part or all of the block. const size_t cur_pages = @@ -290,11 +303,11 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op } } - return ResultSuccess; + R_SUCCEED(); } -Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, - u64 process_id, u8 fill_pattern) { +Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32 option, + u64 process_id, u8 fill_pattern) { ASSERT(out != nullptr); ASSERT(out->GetNumPages() == 0); @@ -302,83 +315,89 @@ Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pag const auto [pool, dir] = DecodeOption(option); // Allocate the memory. + bool optimized; { // Lock the pool that we're allocating from. - KScopedLightLock lk(pool_locks[static_cast(pool)]); + KScopedLightLock lk(m_pool_locks[static_cast(pool)]); + + // Check if we have an optimized process. + const bool has_optimized = m_has_optimized_process[static_cast(pool)]; + const bool is_optimized = m_optimized_process_ids[static_cast(pool)] == process_id; // Allocate the page group. - R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false)); + R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, has_optimized && !is_optimized, + false)); - // Open the first reference to the pages. + // Set whether we should optimize. + optimized = has_optimized && is_optimized; + } + + // Perform optimized memory tracking, if we should. + if (optimized) { + // Iterate over the allocated blocks. for (const auto& block : out->Nodes()) { - PAddr cur_address = block.GetAddress(); - size_t remaining_pages = block.GetNumPages(); - while (remaining_pages > 0) { - // Get the manager for the current address. - auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address); + // Get the block extents. + const PAddr block_address = block.GetAddress(); + const size_t block_pages = block.GetNumPages(); - // Process part or all of the block. - const size_t cur_pages = - std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); - manager.OpenFirst(cur_address, cur_pages); + // If it has no pages, we don't need to do anything. + if (block_pages == 0) { + continue; + } - // Advance. - cur_address += cur_pages * PageSize; - remaining_pages -= cur_pages; + // Fill all the pages that we need to fill. + bool any_new = false; + { + PAddr cur_address = block_address; + size_t remaining_pages = block_pages; + while (remaining_pages > 0) { + // Get the manager for the current address. + auto& manager = this->GetManager(cur_address); + + // Process part or all of the block. + const size_t cur_pages = + std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); + any_new = + manager.ProcessOptimizedAllocation(cur_address, cur_pages, fill_pattern); + + // Advance. + cur_address += cur_pages * PageSize; + remaining_pages -= cur_pages; + } + } + + // If there are new pages, update tracking for the allocation. + if (any_new) { + // Update tracking for the allocation. + PAddr cur_address = block_address; + size_t remaining_pages = block_pages; + while (remaining_pages > 0) { + // Get the manager for the current address. + auto& manager = this->GetManager(cur_address); + + // Lock the pool for the manager. + KScopedLightLock lk(m_pool_locks[static_cast(manager.GetPool())]); + + // Track some or all of the current pages. + const size_t cur_pages = + std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); + manager.TrackOptimizedAllocation(cur_address, cur_pages); + + // Advance. + cur_address += cur_pages * PageSize; + remaining_pages -= cur_pages; + } } } - } - - // Set all the allocated memory. - for (const auto& block : out->Nodes()) { - std::memset(system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern, - block.GetSize()); - } - - return ResultSuccess; -} - -void KMemoryManager::Open(PAddr address, size_t num_pages) { - // Repeatedly open references until we've done so for all pages. - while (num_pages) { - auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address); - const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); - - { - KScopedLightLock lk(pool_locks[static_cast(manager.GetPool())]); - manager.Open(address, cur_pages); + } else { + // Set all the allocated memory. + for (const auto& block : out->Nodes()) { + std::memset(m_system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern, + block.GetSize()); } - - num_pages -= cur_pages; - address += cur_pages * PageSize; } -} -void KMemoryManager::Close(PAddr address, size_t num_pages) { - // Repeatedly close references until we've done so for all pages. - while (num_pages) { - auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address); - const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); - - { - KScopedLightLock lk(pool_locks[static_cast(manager.GetPool())]); - manager.Close(address, cur_pages); - } - - num_pages -= cur_pages; - address += cur_pages * PageSize; - } -} - -void KMemoryManager::Close(const KPageGroup& pg) { - for (const auto& node : pg.Nodes()) { - Close(node.GetAddress(), node.GetNumPages()); - } -} -void KMemoryManager::Open(const KPageGroup& pg) { - for (const auto& node : pg.Nodes()) { - Open(node.GetAddress(), node.GetNumPages()); - } + R_SUCCEED(); } size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management, @@ -394,18 +413,31 @@ size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr manage ASSERT(Common::IsAligned(total_management_size, PageSize)); // Setup region. - pool = p; - management_region = management; - page_reference_counts.resize( + m_pool = p; + m_management_region = management; + m_page_reference_counts.resize( Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize); - ASSERT(Common::IsAligned(management_region, PageSize)); + ASSERT(Common::IsAligned(m_management_region, PageSize)); // Initialize the manager's KPageHeap. - heap.Initialize(address, size, management + manager_size, page_heap_size); + m_heap.Initialize(address, size, management + manager_size, page_heap_size); return total_management_size; } +void KMemoryManager::Impl::TrackUnoptimizedAllocation(PAddr block, size_t num_pages) { + UNREACHABLE(); +} + +void KMemoryManager::Impl::TrackOptimizedAllocation(PAddr block, size_t num_pages) { + UNREACHABLE(); +} + +bool KMemoryManager::Impl::ProcessOptimizedAllocation(PAddr block, size_t num_pages, + u8 fill_pattern) { + UNREACHABLE(); +} + size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) { const size_t ref_count_size = (region_size / PageSize) * sizeof(u16); const size_t optimize_map_size = diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h index dcb9b63..401d4e6 100644 --- a/src/core/hle/kernel/k_memory_manager.h +++ b/src/core/hle/kernel/k_memory_manager.h @@ -21,11 +21,8 @@ namespace Kernel { class KPageGroup; -class KMemoryManager final { +class KMemoryManager { public: - YUZU_NON_COPYABLE(KMemoryManager); - YUZU_NON_MOVEABLE(KMemoryManager); - enum class Pool : u32 { Application = 0, Applet = 1, @@ -45,16 +42,85 @@ public: enum class Direction : u32 { FromFront = 0, FromBack = 1, - Shift = 0, Mask = (0xF << Shift), }; - explicit KMemoryManager(Core::System& system_); + static constexpr size_t MaxManagerCount = 10; + + explicit KMemoryManager(Core::System& system); void Initialize(VAddr management_region, size_t management_region_size); - constexpr size_t GetSize(Pool pool) const { + Result InitializeOptimizedMemory(u64 process_id, Pool pool); + void FinalizeOptimizedMemory(u64 process_id, Pool pool); + + PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); + Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option); + Result AllocateForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id, + u8 fill_pattern); + + Pool GetPool(PAddr address) const { + return this->GetManager(address).GetPool(); + } + + void Open(PAddr address, size_t num_pages) { + // Repeatedly open references until we've done so for all pages. + while (num_pages) { + auto& manager = this->GetManager(address); + const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); + + { + KScopedLightLock lk(m_pool_locks[static_cast(manager.GetPool())]); + manager.Open(address, cur_pages); + } + + num_pages -= cur_pages; + address += cur_pages * PageSize; + } + } + + void OpenFirst(PAddr address, size_t num_pages) { + // Repeatedly open references until we've done so for all pages. + while (num_pages) { + auto& manager = this->GetManager(address); + const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); + + { + KScopedLightLock lk(m_pool_locks[static_cast(manager.GetPool())]); + manager.OpenFirst(address, cur_pages); + } + + num_pages -= cur_pages; + address += cur_pages * PageSize; + } + } + + void Close(PAddr address, size_t num_pages) { + // Repeatedly close references until we've done so for all pages. + while (num_pages) { + auto& manager = this->GetManager(address); + const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); + + { + KScopedLightLock lk(m_pool_locks[static_cast(manager.GetPool())]); + manager.Close(address, cur_pages); + } + + num_pages -= cur_pages; + address += cur_pages * PageSize; + } + } + + size_t GetSize() { + size_t total = 0; + for (size_t i = 0; i < m_num_managers; i++) { + total += m_managers[i].GetSize(); + } + return total; + } + + size_t GetSize(Pool pool) { constexpr Direction GetSizeDirection = Direction::FromFront; size_t total = 0; for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr; @@ -64,18 +130,36 @@ public: return total; } - PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); - Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option); - Result AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id, - u8 fill_pattern); + size_t GetFreeSize() { + size_t total = 0; + for (size_t i = 0; i < m_num_managers; i++) { + KScopedLightLock lk(m_pool_locks[static_cast(m_managers[i].GetPool())]); + total += m_managers[i].GetFreeSize(); + } + return total; + } - static constexpr size_t MaxManagerCount = 10; + size_t GetFreeSize(Pool pool) { + KScopedLightLock lk(m_pool_locks[static_cast(pool)]); - void Close(PAddr address, size_t num_pages); - void Close(const KPageGroup& pg); + constexpr Direction GetSizeDirection = Direction::FromFront; + size_t total = 0; + for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr; + manager = this->GetNextManager(manager, GetSizeDirection)) { + total += manager->GetFreeSize(); + } + return total; + } - void Open(PAddr address, size_t num_pages); - void Open(const KPageGroup& pg); + void DumpFreeList(Pool pool) { + KScopedLightLock lk(m_pool_locks[static_cast(pool)]); + + constexpr Direction DumpDirection = Direction::FromFront; + for (auto* manager = this->GetFirstManager(pool, DumpDirection); manager != nullptr; + manager = this->GetNextManager(manager, DumpDirection)) { + manager->DumpFreeList(); + } + } public: static size_t CalculateManagementOverheadSize(size_t region_size) { @@ -88,14 +172,13 @@ public: } static constexpr Pool GetPool(u32 option) { - return static_cast((static_cast(option) & static_cast(Pool::Mask)) >> + return static_cast((option & static_cast(Pool::Mask)) >> static_cast(Pool::Shift)); } static constexpr Direction GetDirection(u32 option) { - return static_cast( - (static_cast(option) & static_cast(Direction::Mask)) >> - static_cast(Direction::Shift)); + return static_cast((option & static_cast(Direction::Mask)) >> + static_cast(Direction::Shift)); } static constexpr std::tuple DecodeOption(u32 option) { @@ -103,74 +186,88 @@ public: } private: - class Impl final { + class Impl { public: - YUZU_NON_COPYABLE(Impl); - YUZU_NON_MOVEABLE(Impl); + static size_t CalculateManagementOverheadSize(size_t region_size); + static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) { + return (Common::AlignUp((region_size / PageSize), Common::BitSize()) / + Common::BitSize()) * + sizeof(u64); + } + + public: Impl() = default; - ~Impl() = default; size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end, Pool p); - VAddr AllocateBlock(s32 index, bool random) { - return heap.AllocateBlock(index, random); + PAddr AllocateBlock(s32 index, bool random) { + return m_heap.AllocateBlock(index, random); } - - void Free(VAddr addr, size_t num_pages) { - heap.Free(addr, num_pages); + PAddr AllocateAligned(s32 index, size_t num_pages, size_t align_pages) { + return m_heap.AllocateAligned(index, num_pages, align_pages); + } + void Free(PAddr addr, size_t num_pages) { + m_heap.Free(addr, num_pages); } void SetInitialUsedHeapSize(size_t reserved_size) { - heap.SetInitialUsedSize(reserved_size); + m_heap.SetInitialUsedSize(reserved_size); } + void InitializeOptimizedMemory() { + UNIMPLEMENTED(); + } + + void TrackUnoptimizedAllocation(PAddr block, size_t num_pages); + void TrackOptimizedAllocation(PAddr block, size_t num_pages); + + bool ProcessOptimizedAllocation(PAddr block, size_t num_pages, u8 fill_pattern); + constexpr Pool GetPool() const { - return pool; + return m_pool; } - constexpr size_t GetSize() const { - return heap.GetSize(); + return m_heap.GetSize(); + } + constexpr PAddr GetEndAddress() const { + return m_heap.GetEndAddress(); } - constexpr VAddr GetAddress() const { - return heap.GetAddress(); + size_t GetFreeSize() const { + return m_heap.GetFreeSize(); } - constexpr VAddr GetEndAddress() const { - return heap.GetEndAddress(); + void DumpFreeList() const { + UNIMPLEMENTED(); } constexpr size_t GetPageOffset(PAddr address) const { - return heap.GetPageOffset(address); + return m_heap.GetPageOffset(address); } - constexpr size_t GetPageOffsetToEnd(PAddr address) const { - return heap.GetPageOffsetToEnd(address); + return m_heap.GetPageOffsetToEnd(address); } constexpr void SetNext(Impl* n) { - next = n; + m_next = n; } - constexpr void SetPrev(Impl* n) { - prev = n; + m_prev = n; } - constexpr Impl* GetNext() const { - return next; + return m_next; } - constexpr Impl* GetPrev() const { - return prev; + return m_prev; } void OpenFirst(PAddr address, size_t num_pages) { size_t index = this->GetPageOffset(address); const size_t end = index + num_pages; while (index < end) { - const RefCount ref_count = (++page_reference_counts[index]); + const RefCount ref_count = (++m_page_reference_counts[index]); ASSERT(ref_count == 1); index++; @@ -181,7 +278,7 @@ private: size_t index = this->GetPageOffset(address); const size_t end = index + num_pages; while (index < end) { - const RefCount ref_count = (++page_reference_counts[index]); + const RefCount ref_count = (++m_page_reference_counts[index]); ASSERT(ref_count > 1); index++; @@ -195,8 +292,8 @@ private: size_t free_start = 0; size_t free_count = 0; while (index < end) { - ASSERT(page_reference_counts[index] > 0); - const RefCount ref_count = (--page_reference_counts[index]); + ASSERT(m_page_reference_counts[index] > 0); + const RefCount ref_count = (--m_page_reference_counts[index]); // Keep track of how many zero refcounts we see in a row, to minimize calls to free. if (ref_count == 0) { @@ -208,7 +305,7 @@ private: } } else { if (free_count > 0) { - this->Free(heap.GetAddress() + free_start * PageSize, free_count); + this->Free(m_heap.GetAddress() + free_start * PageSize, free_count); free_count = 0; } } @@ -217,44 +314,36 @@ private: } if (free_count > 0) { - this->Free(heap.GetAddress() + free_start * PageSize, free_count); + this->Free(m_heap.GetAddress() + free_start * PageSize, free_count); } } - static size_t CalculateManagementOverheadSize(size_t region_size); - - static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) { - return (Common::AlignUp((region_size / PageSize), Common::BitSize()) / - Common::BitSize()) * - sizeof(u64); - } - private: using RefCount = u16; - KPageHeap heap; - std::vector page_reference_counts; - VAddr management_region{}; - Pool pool{}; - Impl* next{}; - Impl* prev{}; + KPageHeap m_heap; + std::vector m_page_reference_counts; + VAddr m_management_region{}; + Pool m_pool{}; + Impl* m_next{}; + Impl* m_prev{}; }; private: - Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) { - return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; + Impl& GetManager(PAddr address) { + return m_managers[m_memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; } - const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const { - return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; + const Impl& GetManager(PAddr address) const { + return m_managers[m_memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; } - constexpr Impl* GetFirstManager(Pool pool, Direction dir) const { - return dir == Direction::FromBack ? pool_managers_tail[static_cast(pool)] - : pool_managers_head[static_cast(pool)]; + constexpr Impl* GetFirstManager(Pool pool, Direction dir) { + return dir == Direction::FromBack ? m_pool_managers_tail[static_cast(pool)] + : m_pool_managers_head[static_cast(pool)]; } - constexpr Impl* GetNextManager(Impl* cur, Direction dir) const { + constexpr Impl* GetNextManager(Impl* cur, Direction dir) { if (dir == Direction::FromBack) { return cur->GetPrev(); } else { @@ -263,15 +352,21 @@ private: } Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir, - bool random); + bool unoptimized, bool random); private: - Core::System& system; - std::array(Pool::Count)> pool_locks; - std::array pool_managers_head{}; - std::array pool_managers_tail{}; - std::array managers; - size_t num_managers{}; + template + using PoolArray = std::array(Pool::Count)>; + + Core::System& m_system; + const KMemoryLayout& m_memory_layout; + PoolArray m_pool_locks; + std::array m_pool_managers_head{}; + std::array m_pool_managers_tail{}; + std::array m_managers; + size_t m_num_managers{}; + PoolArray m_optimized_process_ids{}; + PoolArray m_has_optimized_process{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_region_type.h b/src/core/hle/kernel/k_memory_region_type.h index 7e2fccc..e5630c1 100644 --- a/src/core/hle/kernel/k_memory_region_type.h +++ b/src/core/hle/kernel/k_memory_region_type.h @@ -142,32 +142,38 @@ private: } // namespace impl -constexpr auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue(); -constexpr auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2); -constexpr auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2); +constexpr inline auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue(); + +constexpr inline auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2); +constexpr inline auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2); static_assert(KMemoryRegionType_Kernel.GetValue() == 0x1); static_assert(KMemoryRegionType_Dram.GetValue() == 0x2); -constexpr auto KMemoryRegionType_DramKernelBase = +// constexpr inline auto KMemoryRegionType_CoreLocalRegion = +// KMemoryRegionType_None.DeriveInitial(2).Finalize(); +// static_assert(KMemoryRegionType_CoreLocalRegion.GetValue() == 0x4); + +constexpr inline auto KMemoryRegionType_DramKernelBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 0) .SetAttribute(KMemoryRegionAttr_NoUserMap) .SetAttribute(KMemoryRegionAttr_CarveoutProtected); -constexpr auto KMemoryRegionType_DramReservedBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 1); -constexpr auto KMemoryRegionType_DramHeapBase = +constexpr inline auto KMemoryRegionType_DramReservedBase = + KMemoryRegionType_Dram.DeriveSparse(0, 3, 1); +constexpr inline auto KMemoryRegionType_DramHeapBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr_LinearMapped); static_assert(KMemoryRegionType_DramKernelBase.GetValue() == (0xE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap)); static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16)); static_assert(KMemoryRegionType_DramHeapBase.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped)); -constexpr auto KMemoryRegionType_DramKernelCode = +constexpr inline auto KMemoryRegionType_DramKernelCode = KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0); -constexpr auto KMemoryRegionType_DramKernelSlab = +constexpr inline auto KMemoryRegionType_DramKernelSlab = KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1); -constexpr auto KMemoryRegionType_DramKernelPtHeap = +constexpr inline auto KMemoryRegionType_DramKernelPtHeap = KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute( KMemoryRegionAttr_LinearMapped); -constexpr auto KMemoryRegionType_DramKernelInitPt = +constexpr inline auto KMemoryRegionType_DramKernelInitPt = KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute( KMemoryRegionAttr_LinearMapped); static_assert(KMemoryRegionType_DramKernelCode.GetValue() == @@ -181,32 +187,40 @@ static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() == (0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped)); -constexpr auto KMemoryRegionType_DramReservedEarly = +constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory = + KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute( + KMemoryRegionAttr_LinearMapped); +static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() == + (0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap | + KMemoryRegionAttr_LinearMapped)); + +constexpr inline auto KMemoryRegionType_DramReservedEarly = KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap); static_assert(KMemoryRegionType_DramReservedEarly.GetValue() == (0x16 | KMemoryRegionAttr_NoUserMap)); -constexpr auto KMemoryRegionType_KernelTraceBuffer = +constexpr inline auto KMemoryRegionType_KernelTraceBuffer = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 0) .SetAttribute(KMemoryRegionAttr_LinearMapped) .SetAttribute(KMemoryRegionAttr_UserReadOnly); -constexpr auto KMemoryRegionType_OnMemoryBootImage = +constexpr inline auto KMemoryRegionType_OnMemoryBootImage = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1); -constexpr auto KMemoryRegionType_DTB = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2); +constexpr inline auto KMemoryRegionType_DTB = + KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2); static_assert(KMemoryRegionType_KernelTraceBuffer.GetValue() == (0xD6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_UserReadOnly)); static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156); static_assert(KMemoryRegionType_DTB.GetValue() == 0x256); -constexpr auto KMemoryRegionType_DramPoolPartition = +constexpr inline auto KMemoryRegionType_DramPoolPartition = KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap); static_assert(KMemoryRegionType_DramPoolPartition.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); -constexpr auto KMemoryRegionType_DramPoolManagement = +constexpr inline auto KMemoryRegionType_DramPoolManagement = KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute( KMemoryRegionAttr_CarveoutProtected); -constexpr auto KMemoryRegionType_DramUserPool = +constexpr inline auto KMemoryRegionType_DramUserPool = KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition(); static_assert(KMemoryRegionType_DramPoolManagement.GetValue() == (0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap | @@ -214,11 +228,13 @@ static_assert(KMemoryRegionType_DramPoolManagement.GetValue() == static_assert(KMemoryRegionType_DramUserPool.GetValue() == (0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); -constexpr auto KMemoryRegionType_DramApplicationPool = KMemoryRegionType_DramUserPool.Derive(4, 0); -constexpr auto KMemoryRegionType_DramAppletPool = KMemoryRegionType_DramUserPool.Derive(4, 1); -constexpr auto KMemoryRegionType_DramSystemNonSecurePool = +constexpr inline auto KMemoryRegionType_DramApplicationPool = + KMemoryRegionType_DramUserPool.Derive(4, 0); +constexpr inline auto KMemoryRegionType_DramAppletPool = + KMemoryRegionType_DramUserPool.Derive(4, 1); +constexpr inline auto KMemoryRegionType_DramSystemNonSecurePool = KMemoryRegionType_DramUserPool.Derive(4, 2); -constexpr auto KMemoryRegionType_DramSystemPool = +constexpr inline auto KMemoryRegionType_DramSystemPool = KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected); static_assert(KMemoryRegionType_DramApplicationPool.GetValue() == (0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); @@ -230,50 +246,55 @@ static_assert(KMemoryRegionType_DramSystemPool.GetValue() == (0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected)); -constexpr auto KMemoryRegionType_VirtualDramHeapBase = KMemoryRegionType_Dram.DeriveSparse(1, 3, 0); -constexpr auto KMemoryRegionType_VirtualDramKernelPtHeap = +constexpr inline auto KMemoryRegionType_VirtualDramHeapBase = + KMemoryRegionType_Dram.DeriveSparse(1, 3, 0); +constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap = KMemoryRegionType_Dram.DeriveSparse(1, 3, 1); -constexpr auto KMemoryRegionType_VirtualDramKernelTraceBuffer = +constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer = KMemoryRegionType_Dram.DeriveSparse(1, 3, 2); static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A); static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A); static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); // UNUSED: .DeriveSparse(2, 2, 0); -constexpr auto KMemoryRegionType_VirtualDramUnknownDebug = +constexpr inline auto KMemoryRegionType_VirtualDramUnknownDebug = KMemoryRegionType_Dram.DeriveSparse(2, 2, 1); static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52)); -constexpr auto KMemoryRegionType_VirtualDramKernelInitPt = +constexpr inline auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory = + KMemoryRegionType_Dram.DeriveSparse(3, 1, 0); +static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x62)); + +constexpr inline auto KMemoryRegionType_VirtualDramKernelInitPt = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); -constexpr auto KMemoryRegionType_VirtualDramPoolManagement = +constexpr inline auto KMemoryRegionType_VirtualDramPoolManagement = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1); -constexpr auto KMemoryRegionType_VirtualDramUserPool = +constexpr inline auto KMemoryRegionType_VirtualDramUserPool = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2); static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A); static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A); static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A); -// NOTE: For unknown reason, the pools are derived out-of-order here. It's worth eventually trying -// to understand why Nintendo made this choice. +// NOTE: For unknown reason, the pools are derived out-of-order here. +// It's worth eventually trying to understand why Nintendo made this choice. // UNUSED: .Derive(6, 0); // UNUSED: .Derive(6, 1); -constexpr auto KMemoryRegionType_VirtualDramAppletPool = +constexpr inline auto KMemoryRegionType_VirtualDramAppletPool = KMemoryRegionType_VirtualDramUserPool.Derive(6, 2); -constexpr auto KMemoryRegionType_VirtualDramApplicationPool = +constexpr inline auto KMemoryRegionType_VirtualDramApplicationPool = KMemoryRegionType_VirtualDramUserPool.Derive(6, 3); -constexpr auto KMemoryRegionType_VirtualDramSystemNonSecurePool = +constexpr inline auto KMemoryRegionType_VirtualDramSystemNonSecurePool = KMemoryRegionType_VirtualDramUserPool.Derive(6, 4); -constexpr auto KMemoryRegionType_VirtualDramSystemPool = +constexpr inline auto KMemoryRegionType_VirtualDramSystemPool = KMemoryRegionType_VirtualDramUserPool.Derive(6, 5); static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A); static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A); static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A); static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A); -constexpr auto KMemoryRegionType_ArchDeviceBase = +constexpr inline auto KMemoryRegionType_ArchDeviceBase = KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly(); -constexpr auto KMemoryRegionType_BoardDeviceBase = +constexpr inline auto KMemoryRegionType_BoardDeviceBase = KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly(); static_assert(KMemoryRegionType_ArchDeviceBase.GetValue() == 0x5); static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5); @@ -284,7 +305,7 @@ static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5); #error "Unimplemented" #else // Default to no architecture devices. -constexpr auto NumArchitectureDeviceRegions = 0; +constexpr inline auto NumArchitectureDeviceRegions = 0; #endif static_assert(NumArchitectureDeviceRegions >= 0); @@ -292,34 +313,35 @@ static_assert(NumArchitectureDeviceRegions >= 0); #include "core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc" #else // Default to no board devices. -constexpr auto NumBoardDeviceRegions = 0; +constexpr inline auto NumBoardDeviceRegions = 0; #endif static_assert(NumBoardDeviceRegions >= 0); -constexpr auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0); -constexpr auto KMemoryRegionType_KernelStack = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1); -constexpr auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2); -constexpr auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3); +constexpr inline auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0); +constexpr inline auto KMemoryRegionType_KernelStack = + KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1); +constexpr inline auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2); +constexpr inline auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3); static_assert(KMemoryRegionType_KernelCode.GetValue() == 0x19); static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29); static_assert(KMemoryRegionType_KernelMisc.GetValue() == 0x49); static_assert(KMemoryRegionType_KernelSlab.GetValue() == 0x89); -constexpr auto KMemoryRegionType_KernelMiscDerivedBase = +constexpr inline auto KMemoryRegionType_KernelMiscDerivedBase = KMemoryRegionType_KernelMisc.DeriveTransition(); static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149); // UNUSED: .Derive(7, 0); -constexpr auto KMemoryRegionType_KernelMiscMainStack = +constexpr inline auto KMemoryRegionType_KernelMiscMainStack = KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1); -constexpr auto KMemoryRegionType_KernelMiscMappedDevice = +constexpr inline auto KMemoryRegionType_KernelMiscMappedDevice = KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2); -constexpr auto KMemoryRegionType_KernelMiscExceptionStack = +constexpr inline auto KMemoryRegionType_KernelMiscExceptionStack = KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3); -constexpr auto KMemoryRegionType_KernelMiscUnknownDebug = +constexpr inline auto KMemoryRegionType_KernelMiscUnknownDebug = KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4); // UNUSED: .Derive(7, 5); -constexpr auto KMemoryRegionType_KernelMiscIdleStack = +constexpr inline auto KMemoryRegionType_KernelMiscIdleStack = KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6); static_assert(KMemoryRegionType_KernelMiscMainStack.GetValue() == 0xB49); static_assert(KMemoryRegionType_KernelMiscMappedDevice.GetValue() == 0xD49); @@ -327,7 +349,8 @@ static_assert(KMemoryRegionType_KernelMiscExceptionStack.GetValue() == 0x1349); static_assert(KMemoryRegionType_KernelMiscUnknownDebug.GetValue() == 0x1549); static_assert(KMemoryRegionType_KernelMiscIdleStack.GetValue() == 0x2349); -constexpr auto KMemoryRegionType_KernelTemp = KMemoryRegionType_Kernel.Advance(2).Derive(2, 0); +constexpr inline auto KMemoryRegionType_KernelTemp = + KMemoryRegionType_Kernel.Advance(2).Derive(2, 0); static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31); constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) { @@ -335,6 +358,8 @@ constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) { return KMemoryRegionType_VirtualDramKernelTraceBuffer; } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) { return KMemoryRegionType_VirtualDramKernelPtHeap; + } else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) { + return KMemoryRegionType_VirtualDramKernelSecureAppletMemory; } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) { return KMemoryRegionType_VirtualDramUnknownDebug; } else { diff --git a/src/core/hle/kernel/k_page_bitmap.h b/src/core/hle/kernel/k_page_bitmap.h index c97b3dc..0ff9877 100644 --- a/src/core/hle/kernel/k_page_bitmap.h +++ b/src/core/hle/kernel/k_page_bitmap.h @@ -16,107 +16,126 @@ namespace Kernel { class KPageBitmap { -private: +public: class RandomBitGenerator { - private: - Common::TinyMT rng{}; - u32 entropy{}; - u32 bits_available{}; - - private: - void RefreshEntropy() { - entropy = rng.GenerateRandomU32(); - bits_available = static_cast(Common::BitSize()); - } - - bool GenerateRandomBit() { - if (bits_available == 0) { - this->RefreshEntropy(); - } - - const bool rnd_bit = (entropy & 1) != 0; - entropy >>= 1; - --bits_available; - return rnd_bit; - } - public: RandomBitGenerator() { - rng.Initialize(static_cast(KSystemControl::GenerateRandomU64())); + m_rng.Initialize(static_cast(KSystemControl::GenerateRandomU64())); } - std::size_t SelectRandomBit(u64 bitmap) { + u64 SelectRandomBit(u64 bitmap) { u64 selected = 0; - u64 cur_num_bits = Common::BitSize() / 2; - u64 cur_mask = (1ULL << cur_num_bits) - 1; + for (size_t cur_num_bits = Common::BitSize() / 2; cur_num_bits != 0; + cur_num_bits /= 2) { + const u64 high = (bitmap >> cur_num_bits); + const u64 low = (bitmap & (~(UINT64_C(0xFFFFFFFFFFFFFFFF) << cur_num_bits))); - while (cur_num_bits) { - const u64 low = (bitmap >> 0) & cur_mask; - const u64 high = (bitmap >> cur_num_bits) & cur_mask; - - bool choose_low; - if (high == 0) { - // If only low val is set, choose low. - choose_low = true; - } else if (low == 0) { - // If only high val is set, choose high. - choose_low = false; - } else { - // If both are set, choose random. - choose_low = this->GenerateRandomBit(); - } - - // If we chose low, proceed with low. - if (choose_low) { - bitmap = low; - selected += 0; - } else { + // Choose high if we have high and (don't have low or select high randomly). + if (high && (low == 0 || this->GenerateRandomBit())) { bitmap = high; selected += cur_num_bits; + } else { + bitmap = low; + selected += 0; } - - // Proceed. - cur_num_bits /= 2; - cur_mask >>= cur_num_bits; } return selected; } + + u64 GenerateRandom(u64 max) { + // Determine the number of bits we need. + const u64 bits_needed = 1 + (Common::BitSize() - std::countl_zero(max)); + + // Generate a random value of the desired bitwidth. + const u64 rnd = this->GenerateRandomBits(static_cast(bits_needed)); + + // Adjust the value to be in range. + return rnd - ((rnd / max) * max); + } + + private: + void RefreshEntropy() { + m_entropy = m_rng.GenerateRandomU32(); + m_bits_available = static_cast(Common::BitSize()); + } + + bool GenerateRandomBit() { + if (m_bits_available == 0) { + this->RefreshEntropy(); + } + + const bool rnd_bit = (m_entropy & 1) != 0; + m_entropy >>= 1; + --m_bits_available; + return rnd_bit; + } + + u64 GenerateRandomBits(u32 num_bits) { + u64 result = 0; + + // Iteratively add random bits to our result. + while (num_bits > 0) { + // Ensure we have random bits to take from. + if (m_bits_available == 0) { + this->RefreshEntropy(); + } + + // Determine how many bits to take this round. + const auto cur_bits = std::min(num_bits, m_bits_available); + + // Generate mask for our current bits. + const u64 mask = (static_cast(1) << cur_bits) - 1; + + // Add bits to output from our entropy. + result <<= cur_bits; + result |= (m_entropy & mask); + + // Remove bits from our entropy. + m_entropy >>= cur_bits; + m_bits_available -= cur_bits; + + // Advance. + num_bits -= cur_bits; + } + + return result; + } + + private: + Common::TinyMT m_rng; + u32 m_entropy{}; + u32 m_bits_available{}; }; public: - static constexpr std::size_t MaxDepth = 4; - -private: - std::array bit_storages{}; - RandomBitGenerator rng{}; - std::size_t num_bits{}; - std::size_t used_depths{}; + static constexpr size_t MaxDepth = 4; public: KPageBitmap() = default; - constexpr std::size_t GetNumBits() const { - return num_bits; + constexpr size_t GetNumBits() const { + return m_num_bits; } constexpr s32 GetHighestDepthIndex() const { - return static_cast(used_depths) - 1; + return static_cast(m_used_depths) - 1; } - u64* Initialize(u64* storage, std::size_t size) { + u64* Initialize(u64* storage, size_t size) { // Initially, everything is un-set. - num_bits = 0; + m_num_bits = 0; // Calculate the needed bitmap depth. - used_depths = static_cast(GetRequiredDepth(size)); - ASSERT(used_depths <= MaxDepth); + m_used_depths = static_cast(GetRequiredDepth(size)); + ASSERT(m_used_depths <= MaxDepth); // Set the bitmap pointers. for (s32 depth = this->GetHighestDepthIndex(); depth >= 0; depth--) { - bit_storages[depth] = storage; + m_bit_storages[depth] = storage; size = Common::AlignUp(size, Common::BitSize()) / Common::BitSize(); storage += size; + m_end_storages[depth] = storage; } return storage; @@ -128,19 +147,19 @@ public: if (random) { do { - const u64 v = bit_storages[depth][offset]; + const u64 v = m_bit_storages[depth][offset]; if (v == 0) { // If depth is bigger than zero, then a previous level indicated a block was // free. ASSERT(depth == 0); return -1; } - offset = offset * Common::BitSize() + rng.SelectRandomBit(v); + offset = offset * Common::BitSize() + m_rng.SelectRandomBit(v); ++depth; - } while (depth < static_cast(used_depths)); + } while (depth < static_cast(m_used_depths)); } else { do { - const u64 v = bit_storages[depth][offset]; + const u64 v = m_bit_storages[depth][offset]; if (v == 0) { // If depth is bigger than zero, then a previous level indicated a block was // free. @@ -149,28 +168,69 @@ public: } offset = offset * Common::BitSize() + std::countr_zero(v); ++depth; - } while (depth < static_cast(used_depths)); + } while (depth < static_cast(m_used_depths)); } return static_cast(offset); } - void SetBit(std::size_t offset) { + s64 FindFreeRange(size_t count) { + // Check that it is possible to find a range. + const u64* const storage_start = m_bit_storages[m_used_depths - 1]; + const u64* const storage_end = m_end_storages[m_used_depths - 1]; + + // If we don't have a storage to iterate (or want more blocks than fit in a single storage), + // we can't find a free range. + if (!(storage_start < storage_end && count <= Common::BitSize())) { + return -1; + } + + // Walk the storages to select a random free range. + const size_t options_per_storage = std::max(Common::BitSize() / count, 1); + const size_t num_entries = std::max(storage_end - storage_start, 1); + + const u64 free_mask = (static_cast(1) << count) - 1; + + size_t num_valid_options = 0; + s64 chosen_offset = -1; + for (size_t storage_index = 0; storage_index < num_entries; ++storage_index) { + u64 storage = storage_start[storage_index]; + for (size_t option = 0; option < options_per_storage; ++option) { + if ((storage & free_mask) == free_mask) { + // We've found a new valid option. + ++num_valid_options; + + // Select the Kth valid option with probability 1/K. This leads to an overall + // uniform distribution. + if (num_valid_options == 1 || m_rng.GenerateRandom(num_valid_options) == 0) { + // This is our first option, so select it. + chosen_offset = storage_index * Common::BitSize() + option * count; + } + } + storage >>= count; + } + } + + // Return the random offset we chose.*/ + return chosen_offset; + } + + void SetBit(size_t offset) { this->SetBit(this->GetHighestDepthIndex(), offset); - num_bits++; + m_num_bits++; } - void ClearBit(std::size_t offset) { + void ClearBit(size_t offset) { this->ClearBit(this->GetHighestDepthIndex(), offset); - num_bits--; + m_num_bits--; } - bool ClearRange(std::size_t offset, std::size_t count) { + bool ClearRange(size_t offset, size_t count) { s32 depth = this->GetHighestDepthIndex(); - u64* bits = bit_storages[depth]; - std::size_t bit_ind = offset / Common::BitSize(); - if (count < Common::BitSize()) { - const std::size_t shift = offset % Common::BitSize(); + u64* bits = m_bit_storages[depth]; + size_t bit_ind = offset / Common::BitSize(); + if (count < Common::BitSize()) [[likely]] { + const size_t shift = offset % Common::BitSize(); ASSERT(shift + count <= Common::BitSize()); // Check that all the bits are set. const u64 mask = ((u64(1) << count) - 1) << shift; @@ -189,8 +249,8 @@ public: ASSERT(offset % Common::BitSize() == 0); ASSERT(count % Common::BitSize() == 0); // Check that all the bits are set. - std::size_t remaining = count; - std::size_t i = 0; + size_t remaining = count; + size_t i = 0; do { if (bits[bit_ind + i++] != ~u64(0)) { return false; @@ -209,18 +269,18 @@ public: } while (remaining > 0); } - num_bits -= count; + m_num_bits -= count; return true; } private: - void SetBit(s32 depth, std::size_t offset) { + void SetBit(s32 depth, size_t offset) { while (depth >= 0) { - std::size_t ind = offset / Common::BitSize(); - std::size_t which = offset % Common::BitSize(); + size_t ind = offset / Common::BitSize(); + size_t which = offset % Common::BitSize(); const u64 mask = u64(1) << which; - u64* bit = std::addressof(bit_storages[depth][ind]); + u64* bit = std::addressof(m_bit_storages[depth][ind]); u64 v = *bit; ASSERT((v & mask) == 0); *bit = v | mask; @@ -232,13 +292,13 @@ private: } } - void ClearBit(s32 depth, std::size_t offset) { + void ClearBit(s32 depth, size_t offset) { while (depth >= 0) { - std::size_t ind = offset / Common::BitSize(); - std::size_t which = offset % Common::BitSize(); + size_t ind = offset / Common::BitSize(); + size_t which = offset % Common::BitSize(); const u64 mask = u64(1) << which; - u64* bit = std::addressof(bit_storages[depth][ind]); + u64* bit = std::addressof(m_bit_storages[depth][ind]); u64 v = *bit; ASSERT((v & mask) != 0); v &= ~mask; @@ -252,7 +312,7 @@ private: } private: - static constexpr s32 GetRequiredDepth(std::size_t region_size) { + static constexpr s32 GetRequiredDepth(size_t region_size) { s32 depth = 0; while (true) { region_size /= Common::BitSize(); @@ -264,8 +324,8 @@ private: } public: - static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size) { - std::size_t overhead_bits = 0; + static constexpr size_t CalculateManagementOverheadSize(size_t region_size) { + size_t overhead_bits = 0; for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) { region_size = Common::AlignUp(region_size, Common::BitSize()) / Common::BitSize(); @@ -273,6 +333,13 @@ public: } return overhead_bits * sizeof(u64); } + +private: + std::array m_bit_storages{}; + std::array m_end_storages{}; + RandomBitGenerator m_rng; + size_t m_num_bits{}; + size_t m_used_depths{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_buffer.cpp b/src/core/hle/kernel/k_page_buffer.cpp index 1a0bf44..0c16dde 100644 --- a/src/core/hle/kernel/k_page_buffer.cpp +++ b/src/core/hle/kernel/k_page_buffer.cpp @@ -12,7 +12,7 @@ namespace Kernel { KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) { ASSERT(Common::IsAligned(phys_addr, PageSize)); - return reinterpret_cast(system.DeviceMemory().GetPointer(phys_addr)); + return system.DeviceMemory().GetPointer(phys_addr); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_buffer.h b/src/core/hle/kernel/k_page_buffer.h index 7e50dc1..cfedaae 100644 --- a/src/core/hle/kernel/k_page_buffer.h +++ b/src/core/hle/kernel/k_page_buffer.h @@ -11,8 +11,19 @@ namespace Kernel { +class KernelCore; + +class KPageBufferSlabHeap : protected impl::KSlabHeapImpl { +public: + static constexpr size_t BufferSize = PageSize; + +public: + void Initialize(Core::System& system); +}; + class KPageBuffer final : public KSlabAllocated { public: + explicit KPageBuffer(KernelCore&) {} KPageBuffer() = default; static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr); @@ -20,8 +31,6 @@ public: private: [[maybe_unused]] alignas(PageSize) std::array m_buffer{}; }; - -static_assert(sizeof(KPageBuffer) == PageSize); -static_assert(alignof(KPageBuffer) == PageSize); +static_assert(sizeof(KPageBuffer) == KPageBufferSlabHeap::BufferSize); } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_group.h b/src/core/hle/kernel/k_page_group.h index 9687539..316f172 100644 --- a/src/core/hle/kernel/k_page_group.h +++ b/src/core/hle/kernel/k_page_group.h @@ -5,6 +5,7 @@ #include +#include "common/alignment.h" #include "common/assert.h" #include "common/common_types.h" #include "core/hle/kernel/memory_types.h" @@ -12,6 +13,89 @@ namespace Kernel { +class KPageGroup; + +class KBlockInfo { +private: + friend class KPageGroup; + +public: + constexpr KBlockInfo() = default; + + constexpr void Initialize(PAddr addr, size_t np) { + ASSERT(Common::IsAligned(addr, PageSize)); + ASSERT(static_cast(np) == np); + + m_page_index = static_cast(addr) / PageSize; + m_num_pages = static_cast(np); + } + + constexpr PAddr GetAddress() const { + return m_page_index * PageSize; + } + constexpr size_t GetNumPages() const { + return m_num_pages; + } + constexpr size_t GetSize() const { + return this->GetNumPages() * PageSize; + } + constexpr PAddr GetEndAddress() const { + return (m_page_index + m_num_pages) * PageSize; + } + constexpr PAddr GetLastAddress() const { + return this->GetEndAddress() - 1; + } + + constexpr KBlockInfo* GetNext() const { + return m_next; + } + + constexpr bool IsEquivalentTo(const KBlockInfo& rhs) const { + return m_page_index == rhs.m_page_index && m_num_pages == rhs.m_num_pages; + } + + constexpr bool operator==(const KBlockInfo& rhs) const { + return this->IsEquivalentTo(rhs); + } + + constexpr bool operator!=(const KBlockInfo& rhs) const { + return !(*this == rhs); + } + + constexpr bool IsStrictlyBefore(PAddr addr) const { + const PAddr end = this->GetEndAddress(); + + if (m_page_index != 0 && end == 0) { + return false; + } + + return end < addr; + } + + constexpr bool operator<(PAddr addr) const { + return this->IsStrictlyBefore(addr); + } + + constexpr bool TryConcatenate(PAddr addr, size_t np) { + if (addr != 0 && addr == this->GetEndAddress()) { + m_num_pages += static_cast(np); + return true; + } + return false; + } + +private: + constexpr void SetNext(KBlockInfo* next) { + m_next = next; + } + +private: + KBlockInfo* m_next{}; + u32 m_page_index{}; + u32 m_num_pages{}; +}; +static_assert(sizeof(KBlockInfo) <= 0x10); + class KPageGroup final { public: class Node final { @@ -92,6 +176,8 @@ public: return nodes.empty(); } + void Finalize() {} + private: std::list nodes; }; diff --git a/src/core/hle/kernel/k_page_heap.cpp b/src/core/hle/kernel/k_page_heap.cpp index 5ede601..7b02c7d 100644 --- a/src/core/hle/kernel/k_page_heap.cpp +++ b/src/core/hle/kernel/k_page_heap.cpp @@ -44,11 +44,11 @@ size_t KPageHeap::GetNumFreePages() const { return num_free; } -PAddr KPageHeap::AllocateBlock(s32 index, bool random) { +PAddr KPageHeap::AllocateByLinearSearch(s32 index) { const size_t needed_size = m_blocks[index].GetSize(); for (s32 i = index; i < static_cast(m_num_blocks); i++) { - if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) { + if (const PAddr addr = m_blocks[i].PopBlock(false); addr != 0) { if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) { this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize); } @@ -59,6 +59,88 @@ PAddr KPageHeap::AllocateBlock(s32 index, bool random) { return 0; } +PAddr KPageHeap::AllocateByRandom(s32 index, size_t num_pages, size_t align_pages) { + // Get the size and required alignment. + const size_t needed_size = num_pages * PageSize; + const size_t align_size = align_pages * PageSize; + + // Determine meta-alignment of our desired alignment size. + const size_t align_shift = std::countr_zero(align_size); + + // Decide on a block to allocate from. + constexpr size_t MinimumPossibleAlignmentsForRandomAllocation = 4; + { + // By default, we'll want to look at all blocks larger than our current one. + s32 max_blocks = static_cast(m_num_blocks); + + // Determine the maximum block we should try to allocate from. + size_t possible_alignments = 0; + for (s32 i = index; i < max_blocks; ++i) { + // Add the possible alignments from blocks at the current size. + possible_alignments += (1 + ((m_blocks[i].GetSize() - needed_size) >> align_shift)) * + m_blocks[i].GetNumFreeBlocks(); + + // If there are enough possible alignments, we don't need to look at larger blocks. + if (possible_alignments >= MinimumPossibleAlignmentsForRandomAllocation) { + max_blocks = i + 1; + break; + } + } + + // If we have any possible alignments which require a larger block, we need to pick one. + if (possible_alignments > 0 && index + 1 < max_blocks) { + // Select a random alignment from the possibilities. + const size_t rnd = m_rng.GenerateRandom(possible_alignments); + + // Determine which block corresponds to the random alignment we chose. + possible_alignments = 0; + for (s32 i = index; i < max_blocks; ++i) { + // Add the possible alignments from blocks at the current size. + possible_alignments += + (1 + ((m_blocks[i].GetSize() - needed_size) >> align_shift)) * + m_blocks[i].GetNumFreeBlocks(); + + // If the current block gets us to our random choice, use the current block. + if (rnd < possible_alignments) { + index = i; + break; + } + } + } + } + + // Pop a block from the index we selected. + if (PAddr addr = m_blocks[index].PopBlock(true); addr != 0) { + // Determine how much size we have left over. + if (const size_t leftover_size = m_blocks[index].GetSize() - needed_size; + leftover_size > 0) { + // Determine how many valid alignments we can have. + const size_t possible_alignments = 1 + (leftover_size >> align_shift); + + // Select a random valid alignment. + const size_t random_offset = m_rng.GenerateRandom(possible_alignments) << align_shift; + + // Free memory before the random offset. + if (random_offset != 0) { + this->Free(addr, random_offset / PageSize); + } + + // Advance our block by the random offset. + addr += random_offset; + + // Free memory after our allocated block. + if (random_offset != leftover_size) { + this->Free(addr + needed_size, (leftover_size - random_offset) / PageSize); + } + } + + // Return the block we allocated. + return addr; + } + + return 0; +} + void KPageHeap::FreeBlock(PAddr block, s32 index) { do { block = m_blocks[index++].PushBlock(block); diff --git a/src/core/hle/kernel/k_page_heap.h b/src/core/hle/kernel/k_page_heap.h index 0917a8b..9021edc 100644 --- a/src/core/hle/kernel/k_page_heap.h +++ b/src/core/hle/kernel/k_page_heap.h @@ -14,13 +14,9 @@ namespace Kernel { -class KPageHeap final { +class KPageHeap { public: - YUZU_NON_COPYABLE(KPageHeap); - YUZU_NON_MOVEABLE(KPageHeap); - KPageHeap() = default; - ~KPageHeap() = default; constexpr PAddr GetAddress() const { return m_heap_address; @@ -57,7 +53,20 @@ public: m_initial_used_size = m_heap_size - free_size - reserved_size; } - PAddr AllocateBlock(s32 index, bool random); + PAddr AllocateBlock(s32 index, bool random) { + if (random) { + const size_t block_pages = m_blocks[index].GetNumPages(); + return this->AllocateByRandom(index, block_pages, block_pages); + } else { + return this->AllocateByLinearSearch(index); + } + } + + PAddr AllocateAligned(s32 index, size_t num_pages, size_t align_pages) { + // TODO: linear search support? + return this->AllocateByRandom(index, num_pages, align_pages); + } + void Free(PAddr addr, size_t num_pages); static size_t CalculateManagementOverheadSize(size_t region_size) { @@ -68,7 +77,7 @@ public: static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) { const size_t target_pages = std::max(num_pages, align_pages); for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) { - if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) { + if (target_pages <= (static_cast(1) << MemoryBlockPageShifts[i]) / PageSize) { return static_cast(i); } } @@ -77,7 +86,7 @@ public: static constexpr s32 GetBlockIndex(size_t num_pages) { for (s32 i = static_cast(NumMemoryBlockPageShifts) - 1; i >= 0; i--) { - if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) { + if (num_pages >= (static_cast(1) << MemoryBlockPageShifts[i]) / PageSize) { return i; } } @@ -85,7 +94,7 @@ public: } static constexpr size_t GetBlockSize(size_t index) { - return size_t(1) << MemoryBlockPageShifts[index]; + return static_cast(1) << MemoryBlockPageShifts[index]; } static constexpr size_t GetBlockNumPages(size_t index) { @@ -93,13 +102,9 @@ public: } private: - class Block final { + class Block { public: - YUZU_NON_COPYABLE(Block); - YUZU_NON_MOVEABLE(Block); - Block() = default; - ~Block() = default; constexpr size_t GetShift() const { return m_block_shift; @@ -201,6 +206,9 @@ private: }; private: + PAddr AllocateByLinearSearch(s32 index); + PAddr AllocateByRandom(s32 index, size_t num_pages, size_t align_pages); + static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts, size_t num_block_shifts); @@ -209,7 +217,8 @@ private: size_t m_heap_size{}; size_t m_initial_used_size{}; size_t m_num_blocks{}; - std::array m_blocks{}; + std::array m_blocks; + KPageBitmap::RandomBitGenerator m_rng; std::vector m_management_data; }; diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index d975de8..612fc76 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -15,6 +15,7 @@ #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_system_control.h" +#include "core/hle/kernel/k_system_resource.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" #include "core/memory.h" @@ -23,9 +24,64 @@ namespace Kernel { namespace { +class KScopedLightLockPair { + YUZU_NON_COPYABLE(KScopedLightLockPair); + YUZU_NON_MOVEABLE(KScopedLightLockPair); + +private: + KLightLock* m_lower; + KLightLock* m_upper; + +public: + KScopedLightLockPair(KLightLock& lhs, KLightLock& rhs) { + // Ensure our locks are in a consistent order. + if (std::addressof(lhs) <= std::addressof(rhs)) { + m_lower = std::addressof(lhs); + m_upper = std::addressof(rhs); + } else { + m_lower = std::addressof(rhs); + m_upper = std::addressof(lhs); + } + + // Acquire both locks. + m_lower->Lock(); + if (m_lower != m_upper) { + m_upper->Lock(); + } + } + + ~KScopedLightLockPair() { + // Unlock the upper lock. + if (m_upper != nullptr && m_upper != m_lower) { + m_upper->Unlock(); + } + + // Unlock the lower lock. + if (m_lower != nullptr) { + m_lower->Unlock(); + } + } + +public: + // Utility. + void TryUnlockHalf(KLightLock& lock) { + // Only allow unlocking if the lock is half the pair. + if (m_lower != m_upper) { + // We want to be sure the lock is one we own. + if (m_lower == std::addressof(lock)) { + lock.Unlock(); + m_lower = nullptr; + } else if (m_upper == std::addressof(lock)) { + lock.Unlock(); + m_upper = nullptr; + } + } + } +}; + using namespace Common::Literals; -constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) { +constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) { switch (as_type) { case FileSys::ProgramAddressSpaceType::Is32Bit: case FileSys::ProgramAddressSpaceType::Is32BitNoMap: @@ -43,27 +99,30 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT } // namespace KPageTable::KPageTable(Core::System& system_) - : general_lock{system_.Kernel()}, map_physical_memory_lock{system_.Kernel()}, system{system_} {} + : m_general_lock{system_.Kernel()}, + m_map_physical_memory_lock{system_.Kernel()}, m_system{system_} {} KPageTable::~KPageTable() = default; Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, - VAddr code_addr, std::size_t code_size, - KMemoryManager::Pool pool) { + bool enable_das_merge, bool from_back, + KMemoryManager::Pool pool, VAddr code_addr, + size_t code_size, KSystemResource* system_resource, + KResourceLimit* resource_limit) { const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) { - return KAddressSpaceInfo::GetAddressSpaceStart(address_space_width, type); + return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type); }; const auto GetSpaceSize = [this](KAddressSpaceInfo::Type type) { - return KAddressSpaceInfo::GetAddressSpaceSize(address_space_width, type); + return KAddressSpaceInfo::GetAddressSpaceSize(m_address_space_width, type); }; // Set our width and heap/alias sizes - address_space_width = GetAddressSpaceWidthFromType(as_type); + m_address_space_width = GetAddressSpaceWidthFromType(as_type); const VAddr start = 0; - const VAddr end{1ULL << address_space_width}; - std::size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)}; - std::size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)}; + const VAddr end{1ULL << m_address_space_width}; + size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)}; + size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)}; ASSERT(code_addr < code_addr + code_size); ASSERT(code_addr + code_size - 1 <= end - 1); @@ -75,66 +134,67 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type } // Set code regions and determine remaining - constexpr std::size_t RegionAlignment{2_MiB}; + constexpr size_t RegionAlignment{2_MiB}; VAddr process_code_start{}; VAddr process_code_end{}; - std::size_t stack_region_size{}; - std::size_t kernel_map_region_size{}; + size_t stack_region_size{}; + size_t kernel_map_region_size{}; - if (address_space_width == 39) { + if (m_address_space_width == 39) { alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias); heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap); stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack); kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); - code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit); - code_region_end = code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit); - alias_code_region_start = code_region_start; - alias_code_region_end = code_region_end; + m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit); + m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit); + m_alias_code_region_start = m_code_region_start; + m_alias_code_region_end = m_code_region_end; process_code_start = Common::AlignDown(code_addr, RegionAlignment); process_code_end = Common::AlignUp(code_addr + code_size, RegionAlignment); } else { stack_region_size = 0; kernel_map_region_size = 0; - code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall); - code_region_end = code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); - stack_region_start = code_region_start; - alias_code_region_start = code_region_start; - alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) + - GetSpaceSize(KAddressSpaceInfo::Type::MapLarge); - stack_region_end = code_region_end; - kernel_map_region_start = code_region_start; - kernel_map_region_end = code_region_end; - process_code_start = code_region_start; - process_code_end = code_region_end; + m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall); + m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); + m_stack_region_start = m_code_region_start; + m_alias_code_region_start = m_code_region_start; + m_alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) + + GetSpaceSize(KAddressSpaceInfo::Type::MapLarge); + m_stack_region_end = m_code_region_end; + m_kernel_map_region_start = m_code_region_start; + m_kernel_map_region_end = m_code_region_end; + process_code_start = m_code_region_start; + process_code_end = m_code_region_end; } // Set other basic fields - is_aslr_enabled = enable_aslr; - address_space_start = start; - address_space_end = end; - is_kernel = false; + m_enable_aslr = enable_aslr; + m_enable_device_address_space_merge = enable_das_merge; + m_address_space_start = start; + m_address_space_end = end; + m_is_kernel = false; + m_memory_block_slab_manager = system_resource->GetMemoryBlockSlabManagerPointer(); + m_block_info_manager = system_resource->GetBlockInfoManagerPointer(); + m_resource_limit = resource_limit; // Determine the region we can place our undetermineds in VAddr alloc_start{}; - std::size_t alloc_size{}; - if ((process_code_start - code_region_start) >= (end - process_code_end)) { - alloc_start = code_region_start; - alloc_size = process_code_start - code_region_start; + size_t alloc_size{}; + if ((process_code_start - m_code_region_start) >= (end - process_code_end)) { + alloc_start = m_code_region_start; + alloc_size = process_code_start - m_code_region_start; } else { alloc_start = process_code_end; alloc_size = end - process_code_end; } - const std::size_t needed_size{ - (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)}; - if (alloc_size < needed_size) { - ASSERT(false); - return ResultOutOfMemory; - } + const size_t needed_size = + (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size); + R_UNLESS(alloc_size >= needed_size, ResultOutOfMemory); - const std::size_t remaining_size{alloc_size - needed_size}; + const size_t remaining_size{alloc_size - needed_size}; // Determine random placements for each region - std::size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{}; + size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{}; if (enable_aslr) { alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment; @@ -147,117 +207,152 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type } // Setup heap and alias regions - alias_region_start = alloc_start + alias_rnd; - alias_region_end = alias_region_start + alias_region_size; - heap_region_start = alloc_start + heap_rnd; - heap_region_end = heap_region_start + heap_region_size; + m_alias_region_start = alloc_start + alias_rnd; + m_alias_region_end = m_alias_region_start + alias_region_size; + m_heap_region_start = alloc_start + heap_rnd; + m_heap_region_end = m_heap_region_start + heap_region_size; if (alias_rnd <= heap_rnd) { - heap_region_start += alias_region_size; - heap_region_end += alias_region_size; + m_heap_region_start += alias_region_size; + m_heap_region_end += alias_region_size; } else { - alias_region_start += heap_region_size; - alias_region_end += heap_region_size; + m_alias_region_start += heap_region_size; + m_alias_region_end += heap_region_size; } // Setup stack region if (stack_region_size) { - stack_region_start = alloc_start + stack_rnd; - stack_region_end = stack_region_start + stack_region_size; + m_stack_region_start = alloc_start + stack_rnd; + m_stack_region_end = m_stack_region_start + stack_region_size; if (alias_rnd < stack_rnd) { - stack_region_start += alias_region_size; - stack_region_end += alias_region_size; + m_stack_region_start += alias_region_size; + m_stack_region_end += alias_region_size; } else { - alias_region_start += stack_region_size; - alias_region_end += stack_region_size; + m_alias_region_start += stack_region_size; + m_alias_region_end += stack_region_size; } if (heap_rnd < stack_rnd) { - stack_region_start += heap_region_size; - stack_region_end += heap_region_size; + m_stack_region_start += heap_region_size; + m_stack_region_end += heap_region_size; } else { - heap_region_start += stack_region_size; - heap_region_end += stack_region_size; + m_heap_region_start += stack_region_size; + m_heap_region_end += stack_region_size; } } // Setup kernel map region if (kernel_map_region_size) { - kernel_map_region_start = alloc_start + kmap_rnd; - kernel_map_region_end = kernel_map_region_start + kernel_map_region_size; + m_kernel_map_region_start = alloc_start + kmap_rnd; + m_kernel_map_region_end = m_kernel_map_region_start + kernel_map_region_size; if (alias_rnd < kmap_rnd) { - kernel_map_region_start += alias_region_size; - kernel_map_region_end += alias_region_size; + m_kernel_map_region_start += alias_region_size; + m_kernel_map_region_end += alias_region_size; } else { - alias_region_start += kernel_map_region_size; - alias_region_end += kernel_map_region_size; + m_alias_region_start += kernel_map_region_size; + m_alias_region_end += kernel_map_region_size; } if (heap_rnd < kmap_rnd) { - kernel_map_region_start += heap_region_size; - kernel_map_region_end += heap_region_size; + m_kernel_map_region_start += heap_region_size; + m_kernel_map_region_end += heap_region_size; } else { - heap_region_start += kernel_map_region_size; - heap_region_end += kernel_map_region_size; + m_heap_region_start += kernel_map_region_size; + m_heap_region_end += kernel_map_region_size; } if (stack_region_size) { if (stack_rnd < kmap_rnd) { - kernel_map_region_start += stack_region_size; - kernel_map_region_end += stack_region_size; + m_kernel_map_region_start += stack_region_size; + m_kernel_map_region_end += stack_region_size; } else { - stack_region_start += kernel_map_region_size; - stack_region_end += kernel_map_region_size; + m_stack_region_start += kernel_map_region_size; + m_stack_region_end += kernel_map_region_size; } } } - // Set heap members - current_heap_end = heap_region_start; - max_heap_size = 0; - max_physical_memory_size = 0; + // Set heap and fill members. + m_current_heap_end = m_heap_region_start; + m_max_heap_size = 0; + m_mapped_physical_memory_size = 0; + m_mapped_unsafe_physical_memory = 0; + m_mapped_insecure_memory = 0; + m_mapped_ipc_server_memory = 0; + + m_heap_fill_value = 0; + m_ipc_fill_value = 0; + m_stack_fill_value = 0; + + // Set allocation option. + m_allocate_option = + KMemoryManager::EncodeOption(pool, from_back ? KMemoryManager::Direction::FromBack + : KMemoryManager::Direction::FromFront); // Ensure that we regions inside our address space auto IsInAddressSpace = [&](VAddr addr) { - return address_space_start <= addr && addr <= address_space_end; + return m_address_space_start <= addr && addr <= m_address_space_end; }; - ASSERT(IsInAddressSpace(alias_region_start)); - ASSERT(IsInAddressSpace(alias_region_end)); - ASSERT(IsInAddressSpace(heap_region_start)); - ASSERT(IsInAddressSpace(heap_region_end)); - ASSERT(IsInAddressSpace(stack_region_start)); - ASSERT(IsInAddressSpace(stack_region_end)); - ASSERT(IsInAddressSpace(kernel_map_region_start)); - ASSERT(IsInAddressSpace(kernel_map_region_end)); + ASSERT(IsInAddressSpace(m_alias_region_start)); + ASSERT(IsInAddressSpace(m_alias_region_end)); + ASSERT(IsInAddressSpace(m_heap_region_start)); + ASSERT(IsInAddressSpace(m_heap_region_end)); + ASSERT(IsInAddressSpace(m_stack_region_start)); + ASSERT(IsInAddressSpace(m_stack_region_end)); + ASSERT(IsInAddressSpace(m_kernel_map_region_start)); + ASSERT(IsInAddressSpace(m_kernel_map_region_end)); // Ensure that we selected regions that don't overlap - const VAddr alias_start{alias_region_start}; - const VAddr alias_last{alias_region_end - 1}; - const VAddr heap_start{heap_region_start}; - const VAddr heap_last{heap_region_end - 1}; - const VAddr stack_start{stack_region_start}; - const VAddr stack_last{stack_region_end - 1}; - const VAddr kmap_start{kernel_map_region_start}; - const VAddr kmap_last{kernel_map_region_end - 1}; + const VAddr alias_start{m_alias_region_start}; + const VAddr alias_last{m_alias_region_end - 1}; + const VAddr heap_start{m_heap_region_start}; + const VAddr heap_last{m_heap_region_end - 1}; + const VAddr stack_start{m_stack_region_start}; + const VAddr stack_last{m_stack_region_end - 1}; + const VAddr kmap_start{m_kernel_map_region_start}; + const VAddr kmap_last{m_kernel_map_region_end - 1}; ASSERT(alias_last < heap_start || heap_last < alias_start); ASSERT(alias_last < stack_start || stack_last < alias_start); ASSERT(alias_last < kmap_start || kmap_last < alias_start); ASSERT(heap_last < stack_start || stack_last < heap_start); ASSERT(heap_last < kmap_start || kmap_last < heap_start); - current_heap_end = heap_region_start; - max_heap_size = 0; - mapped_physical_memory_size = 0; - memory_pool = pool; + m_current_heap_end = m_heap_region_start; + m_max_heap_size = 0; + m_mapped_physical_memory_size = 0; + m_memory_pool = pool; - page_table_impl.Resize(address_space_width, PageBits); + m_page_table_impl = std::make_unique(); + m_page_table_impl->Resize(m_address_space_width, PageBits); - return InitializeMemoryLayout(start, end); + // Initialize our memory block manager. + R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end, + m_memory_block_slab_manager)); } -Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state, +void KPageTable::Finalize() { + // Finalize memory blocks. + m_memory_block_manager.Finalize(m_memory_block_slab_manager, [&](VAddr addr, u64 size) { + m_system.Memory().UnmapRegion(*m_page_table_impl, addr, size); + }); + + // Release any insecure mapped memory. + if (m_mapped_insecure_memory) { + UNIMPLEMENTED(); + } + + // Release any ipc server memory. + if (m_mapped_ipc_server_memory) { + UNIMPLEMENTED(); + } + + // Close the backing page table, as the destructor is not called for guest objects. + m_page_table_impl.reset(); +} + +Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState state, KMemoryPermission perm) { const u64 size{num_pages * PageSize}; @@ -265,52 +360,76 @@ Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryStat R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify that the destination memory is unmapped. R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); + + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager); + + // Allocate and open. KPageGroup pg; - R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( + R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( &pg, num_pages, - KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, allocation_option))); + KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option))); R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup)); - block_manager->Update(addr, num_pages, state, perm); + // Update the blocks. + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { +Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t size) { // Validate the mapping request. R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), ResultInvalidMemoryRegion); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify that the source memory is normal heap. KMemoryState src_state{}; KMemoryPermission src_perm{}; - std::size_t num_src_allocator_blocks{}; + size_t num_src_allocator_blocks{}; R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks, src_address, size, KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)); // Verify that the destination memory is unmapped. - std::size_t num_dst_allocator_blocks{}; + size_t num_dst_allocator_blocks{}; R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); + // Create an update allocator for the source. + Result src_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), + m_memory_block_slab_manager, + num_src_allocator_blocks); + R_TRY(src_allocator_result); + + // Create an update allocator for the destination. + Result dst_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), + m_memory_block_slab_manager, + num_dst_allocator_blocks); + R_TRY(dst_allocator_result); + // Map the code memory. { // Determine the number of pages being operated on. - const std::size_t num_pages = size / PageSize; + const size_t num_pages = size / PageSize; // Create page groups for the memory being mapped. KPageGroup pg; @@ -335,33 +454,37 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size unprot_guard.Cancel(); // Apply the memory block updates. - block_manager->Update(src_address, num_pages, src_state, new_perm, - KMemoryAttribute::Locked); - block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm, - KMemoryAttribute::None); + m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, + src_state, new_perm, KMemoryAttribute::Locked, + KMemoryBlockDisableMergeAttribute::Locked, + KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, + KMemoryState::AliasCode, new_perm, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); } - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, +Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t size, ICacheInvalidationStrategy icache_invalidation_strategy) { // Validate the mapping request. R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), ResultInvalidMemoryRegion); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify that the source memory is locked normal heap. - std::size_t num_src_allocator_blocks{}; + size_t num_src_allocator_blocks{}; R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size, KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::Locked)); // Verify that the destination memory is aliasable code. - std::size_t num_dst_allocator_blocks{}; + size_t num_dst_allocator_blocks{}; R_TRY(this->CheckMemoryStateContiguous( std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias, KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, @@ -370,7 +493,7 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si // Determine whether any pages being unmapped are code. bool any_code_pages = false; { - KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address); + KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(dst_address); while (true) { // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -396,9 +519,9 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si SCOPE_EXIT({ if (reprotected_pages && any_code_pages) { if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) { - system.InvalidateCpuInstructionCacheRange(dst_address, size); + m_system.InvalidateCpuInstructionCacheRange(dst_address, size); } else { - system.InvalidateCpuInstructionCaches(); + m_system.InvalidateCpuInstructionCaches(); } } }); @@ -406,7 +529,21 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si // Unmap. { // Determine the number of pages being operated on. - const std::size_t num_pages = size / PageSize; + const size_t num_pages = size / PageSize; + + // Create an update allocator for the source. + Result src_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), + m_memory_block_slab_manager, + num_src_allocator_blocks); + R_TRY(src_allocator_result); + + // Create an update allocator for the destination. + Result dst_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), + m_memory_block_slab_manager, + num_dst_allocator_blocks); + R_TRY(dst_allocator_result); // Unmap the aliased copy of the pages. R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); @@ -416,73 +553,34 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::si OperationType::ChangePermissions)); // Apply the memory block updates. - block_manager->Update(dst_address, num_pages, KMemoryState::None); - block_manager->Update(src_address, num_pages, KMemoryState::Normal, - KMemoryPermission::UserReadWrite); + m_memory_block_manager.Update( + std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); + m_memory_block_manager.Update( + std::addressof(src_allocator), src_address, num_pages, KMemoryState::Normal, + KMemoryPermission::UserReadWrite, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked); // Note that we reprotected pages. reprotected_pages = true; } - return ResultSuccess; + R_SUCCEED(); } -VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages, - std::size_t num_pages, std::size_t alignment, std::size_t offset, - std::size_t guard_pages) { +VAddr KPageTable::FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages, + size_t alignment, size_t offset, size_t guard_pages) { VAddr address = 0; if (num_pages <= region_num_pages) { if (this->IsAslrEnabled()) { - // Try to directly find a free area up to 8 times. - for (std::size_t i = 0; i < 8; i++) { - const std::size_t random_offset = - KSystemControl::GenerateRandomRange( - 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) * - alignment; - const VAddr candidate = - Common::AlignDown((region_start + random_offset), alignment) + offset; - - KMemoryInfo info = this->QueryInfoImpl(candidate); - - if (info.state != KMemoryState::Free) { - continue; - } - if (region_start > candidate) { - continue; - } - if (info.GetAddress() + guard_pages * PageSize > candidate) { - continue; - } - - const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1; - if (candidate_end > info.GetLastAddress()) { - continue; - } - if (candidate_end > region_start + region_num_pages * PageSize - 1) { - continue; - } - - address = candidate; - break; - } - // Fall back to finding the first free area with a random offset. - if (address == 0) { - // NOTE: Nintendo does not account for guard pages here. - // This may theoretically cause an offset to be chosen that cannot be mapped. We - // will account for guard pages. - const std::size_t offset_pages = KSystemControl::GenerateRandomRange( - 0, region_num_pages - num_pages - guard_pages); - address = block_manager->FindFreeArea(region_start + offset_pages * PageSize, - region_num_pages - offset_pages, num_pages, - alignment, offset, guard_pages); - } + UNIMPLEMENTED(); } - // Find the first free area. if (address == 0) { - address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages, - alignment, offset, guard_pages); + address = m_memory_block_manager.FindFreeArea(region_start, region_num_pages, num_pages, + alignment, offset, guard_pages); } } @@ -500,7 +598,8 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { // Begin traversal. Common::PageTable::TraversalContext context; Common::PageTable::TraversalEntry next_entry; - R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory); + R_UNLESS(m_page_table_impl->BeginTraversal(next_entry, context, addr), + ResultInvalidCurrentMemory); // Prepare tracking variables. PAddr cur_addr = next_entry.phys_addr; @@ -508,9 +607,9 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { size_t tot_size = cur_size; // Iterate, adding to group as we go. - const auto& memory_layout = system.Kernel().MemoryLayout(); + const auto& memory_layout = m_system.Kernel().MemoryLayout(); while (tot_size < size) { - R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context), + R_UNLESS(m_page_table_impl->ContinueTraversal(next_entry, context), ResultInvalidCurrentMemory); if (next_entry.phys_addr != (cur_addr + cur_size)) { @@ -538,7 +637,7 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory); R_TRY(pg.AddBlock(cur_addr, cur_pages)); - return ResultSuccess; + R_SUCCEED(); } bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) { @@ -546,7 +645,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu const size_t size = num_pages * PageSize; const auto& pg = pg_ll.Nodes(); - const auto& memory_layout = system.Kernel().MemoryLayout(); + const auto& memory_layout = m_system.Kernel().MemoryLayout(); // Empty groups are necessarily invalid. if (pg.empty()) { @@ -573,7 +672,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu // Begin traversal. Common::PageTable::TraversalContext context; Common::PageTable::TraversalEntry next_entry; - if (!page_table_impl.BeginTraversal(next_entry, context, addr)) { + if (!m_page_table_impl->BeginTraversal(next_entry, context, addr)) { return false; } @@ -584,7 +683,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu // Iterate, comparing expected to actual. while (tot_size < size) { - if (!page_table_impl.ContinueTraversal(next_entry, context)) { + if (!m_page_table_impl->ContinueTraversal(next_entry, context)) { return false; } @@ -630,11 +729,12 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize); } -Result KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, +Result KPageTable::UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& src_page_table, VAddr src_addr) { - KScopedLightLock lk(general_lock); + // Acquire the table locks. + KScopedLightLockPair lk(src_page_table.m_general_lock, m_general_lock); - const std::size_t num_pages{size / PageSize}; + const size_t num_pages{size / PageSize}; // Check that the memory is mapped in the destination process. size_t num_allocator_blocks; @@ -649,43 +749,795 @@ Result KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTab KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); // Apply the memory block update. - block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, - KMemoryAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), dst_addr, num_pages, + KMemoryState::Free, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); - system.InvalidateCpuInstructionCaches(); + m_system.InvalidateCpuInstructionCaches(); - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { +Result KPageTable::SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed, + VAddr address, size_t size, KMemoryPermission test_perm, + KMemoryState dst_state) { + // Validate pre-conditions. + ASSERT(this->IsLockedByCurrentThread()); + ASSERT(test_perm == KMemoryPermission::UserReadWrite || + test_perm == KMemoryPermission::UserRead); + + // Check that the address is in range. + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Get the source permission. + const auto src_perm = (test_perm == KMemoryPermission::UserReadWrite) + ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped + : KMemoryPermission::UserRead; + + // Get aligned extents. + const VAddr aligned_src_start = Common::AlignDown((address), PageSize); + const VAddr aligned_src_end = Common::AlignUp((address) + size, PageSize); + const VAddr mapping_src_start = Common::AlignUp((address), PageSize); + const VAddr mapping_src_end = Common::AlignDown((address) + size, PageSize); + + const auto aligned_src_last = (aligned_src_end)-1; + const auto mapping_src_last = (mapping_src_end)-1; + + // Get the test state and attribute mask. + KMemoryState test_state; + KMemoryAttribute test_attr_mask; + switch (dst_state) { + case KMemoryState::Ipc: + test_state = KMemoryState::FlagCanUseIpc; + test_attr_mask = + KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked; + break; + case KMemoryState::NonSecureIpc: + test_state = KMemoryState::FlagCanUseNonSecureIpc; + test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; + break; + case KMemoryState::NonDeviceIpc: + test_state = KMemoryState::FlagCanUseNonDeviceIpc; + test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; + break; + default: + R_THROW(ResultInvalidCombination); + } + + // Ensure that on failure, we roll back appropriately. + size_t mapped_size = 0; + ON_RESULT_FAILURE { + if (mapped_size > 0) { + this->CleanupForIpcClientOnServerSetupFailure(page_list, mapping_src_start, mapped_size, + src_perm); + } + }; + + size_t blocks_needed = 0; + + // Iterate, mapping as needed. + KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(aligned_src_start); + while (true) { + const KMemoryInfo info = it->GetMemoryInfo(); + + // Validate the current block. + R_TRY(this->CheckMemoryState(info, test_state, test_state, test_perm, test_perm, + test_attr_mask, KMemoryAttribute::None)); + + if (mapping_src_start < mapping_src_end && (mapping_src_start) < info.GetEndAddress() && + info.GetAddress() < (mapping_src_end)) { + const auto cur_start = + info.GetAddress() >= (mapping_src_start) ? info.GetAddress() : (mapping_src_start); + const auto cur_end = mapping_src_last >= info.GetLastAddress() ? info.GetEndAddress() + : (mapping_src_end); + const size_t cur_size = cur_end - cur_start; + + if (info.GetAddress() < (mapping_src_start)) { + ++blocks_needed; + } + if (mapping_src_last < info.GetLastAddress()) { + ++blocks_needed; + } + + // Set the permissions on the block, if we need to. + if ((info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != src_perm) { + R_TRY(Operate(cur_start, cur_size / PageSize, src_perm, + OperationType::ChangePermissions)); + } + + // Note that we mapped this part. + mapped_size += cur_size; + } + + // If the block is at the end, we're done. + if (aligned_src_last <= info.GetLastAddress()) { + break; + } + + // Advance. + ++it; + ASSERT(it != m_memory_block_manager.end()); + } + + if (out_blocks_needed != nullptr) { + ASSERT(blocks_needed <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); + *out_blocks_needed = blocks_needed; + } + + R_SUCCEED(); +} + +Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_addr, + KMemoryPermission test_perm, KMemoryState dst_state, + KPageTable& src_page_table, bool send) { + ASSERT(this->IsLockedByCurrentThread()); + ASSERT(src_page_table.IsLockedByCurrentThread()); + + // Check that we can theoretically map. + const VAddr region_start = m_alias_region_start; + const size_t region_size = m_alias_region_end - m_alias_region_start; + R_UNLESS(size < region_size, ResultOutOfAddressSpace); + + // Get aligned source extents. + const VAddr src_start = src_addr; + const VAddr src_end = src_addr + size; + const VAddr aligned_src_start = Common::AlignDown((src_start), PageSize); + const VAddr aligned_src_end = Common::AlignUp((src_start) + size, PageSize); + const VAddr mapping_src_start = Common::AlignUp((src_start), PageSize); + const VAddr mapping_src_end = Common::AlignDown((src_start) + size, PageSize); + const size_t aligned_src_size = aligned_src_end - aligned_src_start; + const size_t mapping_src_size = + (mapping_src_start < mapping_src_end) ? (mapping_src_end - mapping_src_start) : 0; + + // Select a random address to map at. + VAddr dst_addr = + this->FindFreeArea(region_start, region_size / PageSize, aligned_src_size / PageSize, + PageSize, 0, this->GetNumGuardPages()); + + R_UNLESS(dst_addr != 0, ResultOutOfAddressSpace); + + // Check that we can perform the operation we're about to perform. + ASSERT(this->CanContain(dst_addr, aligned_src_size, dst_state)); + + // Create an update allocator. + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager); + R_TRY(allocator_result); + + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); + + // Reserve space for any partial pages we allocate. + const size_t unmapped_size = aligned_src_size - mapping_src_size; + KScopedResourceReservation memory_reservation( + m_resource_limit, LimitableResource::PhysicalMemoryMax, unmapped_size); + R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); + + // Ensure that we manage page references correctly. + PAddr start_partial_page = 0; + PAddr end_partial_page = 0; + VAddr cur_mapped_addr = dst_addr; + + // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll + // free on scope exit. + SCOPE_EXIT({ + if (start_partial_page != 0) { + m_system.Kernel().MemoryManager().Close(start_partial_page, 1); + } + if (end_partial_page != 0) { + m_system.Kernel().MemoryManager().Close(end_partial_page, 1); + } + }); + + ON_RESULT_FAILURE { + if (cur_mapped_addr != dst_addr) { + // HACK: Manually close the pages. + HACK_ClosePages(dst_addr, (cur_mapped_addr - dst_addr) / PageSize); + + ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize, + KMemoryPermission::None, OperationType::Unmap) + .IsSuccess()); + } + }; + + // Allocate the start page as needed. + if (aligned_src_start < mapping_src_start) { + start_partial_page = + m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option); + R_UNLESS(start_partial_page != 0, ResultOutOfMemory); + } + + // Allocate the end page as needed. + if (mapping_src_end < aligned_src_end && + (aligned_src_start < mapping_src_end || aligned_src_start == mapping_src_start)) { + end_partial_page = + m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option); + R_UNLESS(end_partial_page != 0, ResultOutOfMemory); + } + + // Get the implementation. + auto& src_impl = src_page_table.PageTableImpl(); + + // Get the fill value for partial pages. + const auto fill_val = m_ipc_fill_value; + + // Begin traversal. + Common::PageTable::TraversalContext context; + Common::PageTable::TraversalEntry next_entry; + bool traverse_valid = src_impl.BeginTraversal(next_entry, context, aligned_src_start); + ASSERT(traverse_valid); + + // Prepare tracking variables. + PAddr cur_block_addr = next_entry.phys_addr; + size_t cur_block_size = + next_entry.block_size - ((cur_block_addr) & (next_entry.block_size - 1)); + size_t tot_block_size = cur_block_size; + + // Map the start page, if we have one. + if (start_partial_page != 0) { + // Ensure the page holds correct data. + const VAddr start_partial_virt = + GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), start_partial_page); + if (send) { + const size_t partial_offset = src_start - aligned_src_start; + size_t copy_size, clear_size; + if (src_end < mapping_src_start) { + copy_size = size; + clear_size = mapping_src_start - src_end; + } else { + copy_size = mapping_src_start - src_start; + clear_size = 0; + } + + std::memset(m_system.Memory().GetPointer(start_partial_virt), fill_val, + partial_offset); + std::memcpy( + m_system.Memory().GetPointer(start_partial_virt + partial_offset), + m_system.Memory().GetPointer( + GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), cur_block_addr) + + partial_offset), + copy_size); + if (clear_size > 0) { + std::memset(m_system.Memory().GetPointer(start_partial_virt + partial_offset + + copy_size), + fill_val, clear_size); + } + } else { + std::memset(m_system.Memory().GetPointer(start_partial_virt), fill_val, PageSize); + } + + // Map the page. + R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page)); + + // HACK: Manually open the pages. + HACK_OpenPages(start_partial_page, 1); + + // Update tracking extents. + cur_mapped_addr += PageSize; + cur_block_addr += PageSize; + cur_block_size -= PageSize; + + // If the block's size was one page, we may need to continue traversal. + if (cur_block_size == 0 && aligned_src_size > PageSize) { + traverse_valid = src_impl.ContinueTraversal(next_entry, context); + ASSERT(traverse_valid); + + cur_block_addr = next_entry.phys_addr; + cur_block_size = next_entry.block_size; + tot_block_size += next_entry.block_size; + } + } + + // Map the remaining pages. + while (aligned_src_start + tot_block_size < mapping_src_end) { + // Continue the traversal. + traverse_valid = src_impl.ContinueTraversal(next_entry, context); + ASSERT(traverse_valid); + + // Process the block. + if (next_entry.phys_addr != cur_block_addr + cur_block_size) { + // Map the block we've been processing so far. + R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map, + cur_block_addr)); + + // HACK: Manually open the pages. + HACK_OpenPages(cur_block_addr, cur_block_size / PageSize); + + // Update tracking extents. + cur_mapped_addr += cur_block_size; + cur_block_addr = next_entry.phys_addr; + cur_block_size = next_entry.block_size; + } else { + cur_block_size += next_entry.block_size; + } + tot_block_size += next_entry.block_size; + } + + // Handle the last direct-mapped page. + if (const VAddr mapped_block_end = aligned_src_start + tot_block_size - cur_block_size; + mapped_block_end < mapping_src_end) { + const size_t last_block_size = mapping_src_end - mapped_block_end; + + // Map the last block. + R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map, + cur_block_addr)); + + // HACK: Manually open the pages. + HACK_OpenPages(cur_block_addr, last_block_size / PageSize); + + // Update tracking extents. + cur_mapped_addr += last_block_size; + cur_block_addr += last_block_size; + if (mapped_block_end + cur_block_size < aligned_src_end && + cur_block_size == last_block_size) { + traverse_valid = src_impl.ContinueTraversal(next_entry, context); + ASSERT(traverse_valid); + + cur_block_addr = next_entry.phys_addr; + } + } + + // Map the end page, if we have one. + if (end_partial_page != 0) { + // Ensure the page holds correct data. + const VAddr end_partial_virt = + GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), end_partial_page); + if (send) { + const size_t copy_size = src_end - mapping_src_end; + std::memcpy(m_system.Memory().GetPointer(end_partial_virt), + m_system.Memory().GetPointer(GetHeapVirtualAddress( + m_system.Kernel().MemoryLayout(), cur_block_addr)), + copy_size); + std::memset(m_system.Memory().GetPointer(end_partial_virt + copy_size), fill_val, + PageSize - copy_size); + } else { + std::memset(m_system.Memory().GetPointer(end_partial_virt), fill_val, PageSize); + } + + // Map the page. + R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page)); + + // HACK: Manually open the pages. + HACK_OpenPages(end_partial_page, 1); + } + + // Update memory blocks to reflect our changes + m_memory_block_manager.Update(std::addressof(allocator), dst_addr, aligned_src_size / PageSize, + dst_state, test_perm, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); + + // Set the output address. + *out_addr = dst_addr + (src_start - aligned_src_start); + + // We succeeded. + memory_reservation.Commit(); + R_SUCCEED(); +} + +Result KPageTable::SetupForIpc(VAddr* out_dst_addr, size_t size, VAddr src_addr, + KPageTable& src_page_table, KMemoryPermission test_perm, + KMemoryState dst_state, bool send) { + // For convenience, alias this. + KPageTable& dst_page_table = *this; + + // Acquire the table locks. + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); + + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(std::addressof(src_page_table)); + + // Perform client setup. + size_t num_allocator_blocks; + R_TRY(src_page_table.SetupForIpcClient(updater.GetPageList(), + std::addressof(num_allocator_blocks), src_addr, size, + test_perm, dst_state)); + + // Create an update allocator. + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + src_page_table.m_memory_block_slab_manager, + num_allocator_blocks); + R_TRY(allocator_result); + + // Get the mapped extents. + const VAddr src_map_start = Common::AlignUp((src_addr), PageSize); + const VAddr src_map_end = Common::AlignDown((src_addr) + size, PageSize); + const size_t src_map_size = src_map_end - src_map_start; + + // Ensure that we clean up appropriately if we fail after this. + const auto src_perm = (test_perm == KMemoryPermission::UserReadWrite) + ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped + : KMemoryPermission::UserRead; + ON_RESULT_FAILURE { + if (src_map_end > src_map_start) { + src_page_table.CleanupForIpcClientOnServerSetupFailure( + updater.GetPageList(), src_map_start, src_map_size, src_perm); + } + }; + + // Perform server setup. + R_TRY(dst_page_table.SetupForIpcServer(out_dst_addr, size, src_addr, test_perm, dst_state, + src_page_table, send)); + + // If anything was mapped, ipc-lock the pages. + if (src_map_start < src_map_end) { + // Get the source permission. + src_page_table.m_memory_block_manager.UpdateLock(std::addressof(allocator), src_map_start, + (src_map_end - src_map_start) / PageSize, + &KMemoryBlock::LockForIpc, src_perm); + } + + R_SUCCEED(); +} + +Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState dst_state) { + // Validate the address. + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(m_general_lock); + + // Validate the memory state. + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, + KMemoryState::All, dst_state, KMemoryPermission::UserRead, + KMemoryPermission::UserRead, KMemoryAttribute::All, + KMemoryAttribute::None)); + + // Create an update allocator. + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); + + // Get aligned extents. + const VAddr aligned_start = Common::AlignDown((address), PageSize); + const VAddr aligned_end = Common::AlignUp((address) + size, PageSize); + const size_t aligned_size = aligned_end - aligned_start; + const size_t aligned_num_pages = aligned_size / PageSize; + + // HACK: Manually close the pages. + HACK_ClosePages(aligned_start, aligned_num_pages); + + // Unmap the pages. + R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap)); + + // Update memory blocks. + m_memory_block_manager.Update(std::addressof(allocator), aligned_start, aligned_num_pages, + KMemoryState::None, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); + + // Release from the resource limit as relevant. + const VAddr mapping_start = Common::AlignUp((address), PageSize); + const VAddr mapping_end = Common::AlignDown((address) + size, PageSize); + const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; + m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size); + + R_SUCCEED(); +} + +Result KPageTable::CleanupForIpcClient(VAddr address, size_t size, KMemoryState dst_state) { + // Validate the address. + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Get aligned source extents. + const VAddr mapping_start = Common::AlignUp((address), PageSize); + const VAddr mapping_end = Common::AlignDown((address) + size, PageSize); + const VAddr mapping_last = mapping_end - 1; + const size_t mapping_size = (mapping_start < mapping_end) ? (mapping_end - mapping_start) : 0; + + // If nothing was mapped, we're actually done immediately. + R_SUCCEED_IF(mapping_size == 0); + + // Get the test state and attribute mask. + KMemoryState test_state; + KMemoryAttribute test_attr_mask; + switch (dst_state) { + case KMemoryState::Ipc: + test_state = KMemoryState::FlagCanUseIpc; + test_attr_mask = + KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked; + break; + case KMemoryState::NonSecureIpc: + test_state = KMemoryState::FlagCanUseNonSecureIpc; + test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; + break; + case KMemoryState::NonDeviceIpc: + test_state = KMemoryState::FlagCanUseNonDeviceIpc; + test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; + break; + default: + R_THROW(ResultInvalidCombination); + } + + // Lock the table. + // NOTE: Nintendo does this *after* creating the updater below, but this does not follow + // convention elsewhere in KPageTable. + KScopedLightLock lk(m_general_lock); + + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); + + // Ensure that on failure, we roll back appropriately. + size_t mapped_size = 0; + ON_RESULT_FAILURE { + if (mapped_size > 0) { + // Determine where the mapping ends. + const auto mapped_end = (mapping_start) + mapped_size; + const auto mapped_last = mapped_end - 1; + + // Get current and next iterators. + KMemoryBlockManager::const_iterator start_it = + m_memory_block_manager.FindIterator(mapping_start); + KMemoryBlockManager::const_iterator next_it = start_it; + ++next_it; + + // Get the current block info. + KMemoryInfo cur_info = start_it->GetMemoryInfo(); + + // Create tracking variables. + VAddr cur_address = cur_info.GetAddress(); + size_t cur_size = cur_info.GetSize(); + bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission(); + bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1; + bool first = + cur_info.GetIpcDisableMergeCount() == 1 && + (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked) == + KMemoryBlockDisableMergeAttribute::None; + + while (((cur_address) + cur_size - 1) < mapped_last) { + // Check that we have a next block. + ASSERT(next_it != m_memory_block_manager.end()); + + // Get the next info. + const KMemoryInfo next_info = next_it->GetMemoryInfo(); + + // Check if we can consolidate the next block's permission set with the current one. + + const bool next_perm_eq = + next_info.GetPermission() == next_info.GetOriginalPermission(); + const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1; + if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm && + cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) { + // We can consolidate the reprotection for the current and next block into a + // single call. + cur_size += next_info.GetSize(); + } else { + // We have to operate on the current block. + if ((cur_needs_set_perm || first) && !cur_perm_eq) { + ASSERT(Operate(cur_address, cur_size / PageSize, cur_info.GetPermission(), + OperationType::ChangePermissions) + .IsSuccess()); + } + + // Advance. + cur_address = next_info.GetAddress(); + cur_size = next_info.GetSize(); + first = false; + } + + // Advance. + cur_info = next_info; + cur_perm_eq = next_perm_eq; + cur_needs_set_perm = next_needs_set_perm; + ++next_it; + } + + // Process the last block. + if ((first || cur_needs_set_perm) && !cur_perm_eq) { + ASSERT(Operate(cur_address, cur_size / PageSize, cur_info.GetPermission(), + OperationType::ChangePermissions) + .IsSuccess()); + } + } + }; + + // Iterate, reprotecting as needed. + { + // Get current and next iterators. + KMemoryBlockManager::const_iterator start_it = + m_memory_block_manager.FindIterator(mapping_start); + KMemoryBlockManager::const_iterator next_it = start_it; + ++next_it; + + // Validate the current block. + KMemoryInfo cur_info = start_it->GetMemoryInfo(); + ASSERT(this->CheckMemoryState(cur_info, test_state, test_state, KMemoryPermission::None, + KMemoryPermission::None, + test_attr_mask | KMemoryAttribute::IpcLocked, + KMemoryAttribute::IpcLocked) + .IsSuccess()); + + // Create tracking variables. + VAddr cur_address = cur_info.GetAddress(); + size_t cur_size = cur_info.GetSize(); + bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission(); + bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1; + bool first = + cur_info.GetIpcDisableMergeCount() == 1 && + (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked) == + KMemoryBlockDisableMergeAttribute::None; + + while ((cur_address + cur_size - 1) < mapping_last) { + // Check that we have a next block. + ASSERT(next_it != m_memory_block_manager.end()); + + // Get the next info. + const KMemoryInfo next_info = next_it->GetMemoryInfo(); + + // Validate the next block. + ASSERT(this->CheckMemoryState(next_info, test_state, test_state, + KMemoryPermission::None, KMemoryPermission::None, + test_attr_mask | KMemoryAttribute::IpcLocked, + KMemoryAttribute::IpcLocked) + .IsSuccess()); + + // Check if we can consolidate the next block's permission set with the current one. + const bool next_perm_eq = + next_info.GetPermission() == next_info.GetOriginalPermission(); + const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1; + if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm && + cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) { + // We can consolidate the reprotection for the current and next block into a single + // call. + cur_size += next_info.GetSize(); + } else { + // We have to operate on the current block. + if ((cur_needs_set_perm || first) && !cur_perm_eq) { + R_TRY(Operate(cur_address, cur_size / PageSize, + cur_needs_set_perm ? cur_info.GetOriginalPermission() + : cur_info.GetPermission(), + OperationType::ChangePermissions)); + } + + // Mark that we mapped the block. + mapped_size += cur_size; + + // Advance. + cur_address = next_info.GetAddress(); + cur_size = next_info.GetSize(); + first = false; + } + + // Advance. + cur_info = next_info; + cur_perm_eq = next_perm_eq; + cur_needs_set_perm = next_needs_set_perm; + ++next_it; + } + + // Process the last block. + const auto lock_count = + cur_info.GetIpcLockCount() + + (next_it != m_memory_block_manager.end() + ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount()) + : 0); + if ((first || cur_needs_set_perm || (lock_count == 1)) && !cur_perm_eq) { + R_TRY(Operate(cur_address, cur_size / PageSize, + cur_needs_set_perm ? cur_info.GetOriginalPermission() + : cur_info.GetPermission(), + OperationType::ChangePermissions)); + } + } + + // Create an update allocator. + // NOTE: Guaranteed zero blocks needed here. + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, 0); + R_TRY(allocator_result); + + // Unlock the pages. + m_memory_block_manager.UpdateLock(std::addressof(allocator), mapping_start, + mapping_size / PageSize, &KMemoryBlock::UnlockForIpc, + KMemoryPermission::None); + + R_SUCCEED(); +} + +void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLinkedList* page_list, + VAddr address, size_t size, + KMemoryPermission prot_perm) { + ASSERT(this->IsLockedByCurrentThread()); + ASSERT(Common::IsAligned(address, PageSize)); + ASSERT(Common::IsAligned(size, PageSize)); + + // Get the mapped extents. + const VAddr src_map_start = address; + const VAddr src_map_end = address + size; + const VAddr src_map_last = src_map_end - 1; + + // This function is only invoked when there's something to do. + ASSERT(src_map_end > src_map_start); + + // Iterate over blocks, fixing permissions. + KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(address); + while (true) { + const KMemoryInfo info = it->GetMemoryInfo(); + + const auto cur_start = + info.GetAddress() >= src_map_start ? info.GetAddress() : src_map_start; + const auto cur_end = + src_map_last <= info.GetLastAddress() ? src_map_end : info.GetEndAddress(); + + // If we can, fix the protections on the block. + if ((info.GetIpcLockCount() == 0 && + (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) || + (info.GetIpcLockCount() != 0 && + (info.GetOriginalPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm)) { + // Check if we actually need to fix the protections on the block. + if (cur_end == src_map_end || info.GetAddress() <= src_map_start || + (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) { + ASSERT(Operate(cur_start, (cur_end - cur_start) / PageSize, info.GetPermission(), + OperationType::ChangePermissions) + .IsSuccess()); + } + } + + // If we're past the end of the region, we're done. + if (src_map_last <= info.GetLastAddress()) { + break; + } + + // Advance. + ++it; + ASSERT(it != m_memory_block_manager.end()); + } +} + +void KPageTable::HACK_OpenPages(PAddr phys_addr, size_t num_pages) { + m_system.Kernel().MemoryManager().OpenFirst(phys_addr, num_pages); +} + +void KPageTable::HACK_ClosePages(VAddr virt_addr, size_t num_pages) { + for (size_t index = 0; index < num_pages; ++index) { + const auto paddr = GetPhysicalAddr(virt_addr + (index * PageSize)); + m_system.Kernel().MemoryManager().Close(paddr, 1); + } +} + +Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { // Lock the physical memory lock. - KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); + KScopedLightLock phys_lk(m_map_physical_memory_lock); // Calculate the last address for convenience. const VAddr last_address = address + size - 1; // Define iteration variables. VAddr cur_address; - std::size_t mapped_size; + size_t mapped_size; // The entire mapping process can be retried. while (true) { // Check if the memory is already mapped. { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Iterate over the memory. cur_address = address; mapped_size = 0; - auto it = block_manager->FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -716,20 +1568,24 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { { // Reserve the memory from the process resource limit. KScopedResourceReservation memory_reservation( - system.Kernel().CurrentProcess()->GetResourceLimit(), - LimitableResource::PhysicalMemory, size - mapped_size); + m_resource_limit, LimitableResource::PhysicalMemoryMax, size - mapped_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the new memory. KPageGroup pg; - R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( - &pg, (size - mapped_size) / PageSize, - KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); + R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess( + &pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0)); + + // If we fail in the next bit (or retry), we need to cleanup the pages. + // auto pg_guard = SCOPE_GUARD { + // pg.OpenFirst(); + // pg.Close(); + //}; // Map the memory. { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); size_t num_allocator_blocks = 0; @@ -739,10 +1595,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { size_t checked_mapped_size = 0; cur_address = address; - auto it = block_manager->FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -782,19 +1638,36 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { } } + // Create an update allocator. + ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, + num_allocator_blocks); + R_TRY(allocator_result); + + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); + + // Prepare to iterate over the memory. + auto pg_it = pg.Nodes().begin(); + PAddr pg_phys_addr = pg_it->GetAddress(); + size_t pg_pages = pg_it->GetNumPages(); + // Reset the current tracking address, and make sure we clean up on failure. + // pg_guard.Cancel(); cur_address = address; - auto unmap_guard = detail::ScopeExit([&] { + ON_RESULT_FAILURE { if (cur_address > address) { const VAddr last_unmap_address = cur_address - 1; // Iterate, unmapping the pages. cur_address = address; - auto it = block_manager->FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -807,6 +1680,9 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { last_unmap_address + 1 - cur_address) / PageSize; + // HACK: Manually close the pages. + HACK_ClosePages(cur_address, cur_pages); + // Unmap. ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap) @@ -823,17 +1699,22 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { ++it; } } - }); - // Iterate over the memory. - auto pg_it = pg.Nodes().begin(); - PAddr pg_phys_addr = pg_it->GetAddress(); - size_t pg_pages = pg_it->GetNumPages(); + // Release any remaining unmapped memory. + m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages); + m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages); + for (++pg_it; pg_it != pg.Nodes().end(); ++pg_it) { + m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(), + pg_it->GetNumPages()); + m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(), + pg_it->GetNumPages()); + } + }; - auto it = block_manager->FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -863,6 +1744,9 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite, OperationType::Map, pg_phys_addr)); + // HACK: Manually open the pages. + HACK_OpenPages(pg_phys_addr, cur_pages); + // Advance. cur_address += cur_pages * PageSize; map_pages -= cur_pages; @@ -886,37 +1770,37 @@ Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { memory_reservation.Commit(); // Increase our tracked mapped size. - mapped_physical_memory_size += (size - mapped_size); + m_mapped_physical_memory_size += (size - mapped_size); // Update the relevant memory blocks. - block_manager->Update(address, size / PageSize, KMemoryState::Free, - KMemoryPermission::None, KMemoryAttribute::None, - KMemoryState::Normal, KMemoryPermission::UserReadWrite, - KMemoryAttribute::None); + m_memory_block_manager.UpdateIfMatch( + std::addressof(allocator), address, size / PageSize, KMemoryState::Free, + KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal, + KMemoryPermission::UserReadWrite, KMemoryAttribute::None); - // Cancel our guard. - unmap_guard.Cancel(); - - return ResultSuccess; + R_SUCCEED(); } } } } -Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { +Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { // Lock the physical memory lock. - KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); + KScopedLightLock phys_lk(m_map_physical_memory_lock); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Calculate the last address for convenience. const VAddr last_address = address + size - 1; // Define iteration variables. - VAddr cur_address = 0; - std::size_t mapped_size = 0; - std::size_t num_allocator_blocks = 0; + VAddr map_start_address = 0; + VAddr map_last_address = 0; + + VAddr cur_address; + size_t mapped_size; + size_t num_allocator_blocks = 0; // Check if the memory is mapped. { @@ -924,10 +1808,10 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { cur_address = address; mapped_size = 0; - auto it = block_manager->FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -941,27 +1825,27 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { if (is_normal) { R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory); + if (map_start_address == 0) { + map_start_address = cur_address; + } + map_last_address = + (last_address >= info.GetLastAddress()) ? info.GetLastAddress() : last_address; + if (info.GetAddress() < address) { ++num_allocator_blocks; } if (last_address < info.GetLastAddress()) { ++num_allocator_blocks; } + + mapped_size += (map_last_address + 1 - cur_address); } // Check if we're done. if (last_address <= info.GetLastAddress()) { - if (is_normal) { - mapped_size += (last_address + 1 - cur_address); - } break; } - // Track the memory if it's mapped. - if (is_normal) { - mapped_size += VAddr(info.GetEndAddress()) - cur_address; - } - // Advance. cur_address = info.GetEndAddress(); ++it; @@ -971,124 +1855,28 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { R_SUCCEED_IF(mapped_size == 0); } - // Make a page group for the unmap region. - KPageGroup pg; - { - auto& impl = this->PageTableImpl(); + // Create an update allocator. + ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); - // Begin traversal. - Common::PageTable::TraversalContext context; - Common::PageTable::TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0}; - bool cur_valid = false; - Common::PageTable::TraversalEntry next_entry; - bool next_valid = false; - size_t tot_size = 0; + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); - cur_address = address; - next_valid = impl.BeginTraversal(next_entry, context, cur_address); - next_entry.block_size = - (next_entry.block_size - (next_entry.phys_addr & (next_entry.block_size - 1))); - - // Iterate, building the group. - while (true) { - if ((!next_valid && !cur_valid) || - (next_valid && cur_valid && - next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) { - cur_entry.block_size += next_entry.block_size; - } else { - if (cur_valid) { - // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr)); - R_TRY(pg.AddBlock(cur_entry.phys_addr, cur_entry.block_size / PageSize)); - } - - // Update tracking variables. - tot_size += cur_entry.block_size; - cur_entry = next_entry; - cur_valid = next_valid; - } - - if (cur_entry.block_size + tot_size >= size) { - break; - } - - next_valid = impl.ContinueTraversal(next_entry, context); - } - - // Add the last block. - if (cur_valid) { - // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr)); - R_TRY(pg.AddBlock(cur_entry.phys_addr, (size - tot_size) / PageSize)); - } - } - ASSERT(pg.GetNumPages() == mapped_size / PageSize); + // Separate the mapping. + R_TRY(Operate(map_start_address, (map_last_address + 1 - map_start_address) / PageSize, + KMemoryPermission::None, OperationType::Separate)); // Reset the current tracking address, and make sure we clean up on failure. cur_address = address; - auto remap_guard = detail::ScopeExit([&] { - if (cur_address > address) { - const VAddr last_map_address = cur_address - 1; - cur_address = address; - - // Iterate over the memory we unmapped. - auto it = block_manager->FindIterator(cur_address); - auto pg_it = pg.Nodes().begin(); - PAddr pg_phys_addr = pg_it->GetAddress(); - size_t pg_pages = pg_it->GetNumPages(); - - while (true) { - // Get the memory info for the pages we unmapped, convert to property. - const KMemoryInfo info = it->GetMemoryInfo(); - - // If the memory is normal, we unmapped it and need to re-map it. - if (info.GetState() == KMemoryState::Normal) { - // Determine the range to map. - size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, - last_map_address + 1 - cur_address) / - PageSize; - - // While we have pages to map, map them. - while (map_pages > 0) { - // Check if we're at the end of the physical block. - if (pg_pages == 0) { - // Ensure there are more pages to map. - ASSERT(pg_it != pg.Nodes().end()); - - // Advance our physical block. - ++pg_it; - pg_phys_addr = pg_it->GetAddress(); - pg_pages = pg_it->GetNumPages(); - } - - // Map whatever we can. - const size_t cur_pages = std::min(pg_pages, map_pages); - ASSERT(this->Operate(cur_address, cur_pages, info.GetPermission(), - OperationType::Map, pg_phys_addr) == ResultSuccess); - - // Advance. - cur_address += cur_pages * PageSize; - map_pages -= cur_pages; - - pg_phys_addr += cur_pages * PageSize; - pg_pages -= cur_pages; - } - } - - // Check if we're done. - if (last_map_address <= info.GetLastAddress()) { - break; - } - - // Advance. - ++it; - } - } - }); // Iterate over the memory, unmapping as we go. - auto it = block_manager->FindIterator(cur_address); + auto it = m_memory_block_manager.FindIterator(cur_address); while (true) { // Check that the iterator is valid. - ASSERT(it != block_manager->end()); + ASSERT(it != m_memory_block_manager.end()); // Get the memory info. const KMemoryInfo info = it->GetMemoryInfo(); @@ -1100,8 +1888,12 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { last_address + 1 - cur_address) / PageSize; + // HACK: Manually close the pages. + HACK_ClosePages(cur_address, cur_pages); + // Unmap. - R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)); + ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap) + .IsSuccess()); } // Check if we're done. @@ -1115,104 +1907,151 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { } // Release the memory resource. - mapped_physical_memory_size -= mapped_size; - auto process{system.Kernel().CurrentProcess()}; - process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); + m_mapped_physical_memory_size -= mapped_size; + m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, mapped_size); // Update memory blocks. - block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None, - KMemoryAttribute::None); - - // TODO(bunnei): This is a workaround until the next set of changes, where we add reference - // counting for mapped pages. Until then, we must manually close the reference to the page - // group. - system.Kernel().MemoryManager().Close(pg); + m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, + KMemoryState::Free, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); // We succeeded. - remap_guard.Cancel(); - - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { - KScopedLightLock lk(general_lock); +Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) { + // Lock the table. + KScopedLightLock lk(m_general_lock); - KMemoryState src_state{}; - CASCADE_CODE(CheckMemoryState( - &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, - KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::UserReadWrite, - KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); + // Validate that the source address's state is valid. + KMemoryState src_state; + size_t num_src_allocator_blocks; + R_TRY(this->CheckMemoryState(std::addressof(src_state), nullptr, nullptr, + std::addressof(num_src_allocator_blocks), src_address, size, + KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias, + KMemoryPermission::All, KMemoryPermission::UserReadWrite, + KMemoryAttribute::All, KMemoryAttribute::None)); - if (IsRegionMapped(dst_addr, size)) { - return ResultInvalidCurrentMemory; - } + // Validate that the dst address's state is valid. + size_t num_dst_allocator_blocks; + R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size, + KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryAttribute::None)); + // Create an update allocator for the source. + Result src_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), + m_memory_block_slab_manager, + num_src_allocator_blocks); + R_TRY(src_allocator_result); + + // Create an update allocator for the destination. + Result dst_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), + m_memory_block_slab_manager, + num_dst_allocator_blocks); + R_TRY(dst_allocator_result); + + // Map the memory. KPageGroup page_linked_list; - const std::size_t num_pages{size / PageSize}; - - AddRegionToPages(src_addr, num_pages, page_linked_list); + const size_t num_pages{size / PageSize}; + const KMemoryPermission new_src_perm = static_cast( + KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); + const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked; + AddRegionToPages(src_address, num_pages, page_linked_list); { + // Reprotect the source as kernel-read/not mapped. auto block_guard = detail::ScopeExit([&] { - Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite, + Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, OperationType::ChangePermissions); }); - - CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, - OperationType::ChangePermissions)); - CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::UserReadWrite)); + R_TRY(Operate(src_address, num_pages, new_src_perm, OperationType::ChangePermissions)); + R_TRY(MapPages(dst_address, page_linked_list, KMemoryPermission::UserReadWrite)); block_guard.Cancel(); } - block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::None, - KMemoryAttribute::Locked); - block_manager->Update(dst_addr, num_pages, KMemoryState::Stack, - KMemoryPermission::UserReadWrite); + // Apply the memory block updates. + m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, + new_src_perm, new_src_attr, + KMemoryBlockDisableMergeAttribute::Locked, + KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, + KMemoryState::Stack, KMemoryPermission::UserReadWrite, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { - KScopedLightLock lk(general_lock); +Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) { + // Lock the table. + KScopedLightLock lk(m_general_lock); - KMemoryState src_state{}; - CASCADE_CODE(CheckMemoryState( - &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, - KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None, - KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); + // Validate that the source address's state is valid. + KMemoryState src_state; + size_t num_src_allocator_blocks; + R_TRY(this->CheckMemoryState( + std::addressof(src_state), nullptr, nullptr, std::addressof(num_src_allocator_blocks), + src_address, size, KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias, + KMemoryPermission::All, KMemoryPermission::NotMapped | KMemoryPermission::KernelRead, + KMemoryAttribute::All, KMemoryAttribute::Locked)); - KMemoryPermission dst_perm{}; - CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, nullptr, dst_addr, size, - KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None, - KMemoryPermission::None, KMemoryAttribute::Mask, - KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); + // Validate that the dst address's state is valid. + KMemoryPermission dst_perm; + size_t num_dst_allocator_blocks; + R_TRY(this->CheckMemoryState( + nullptr, std::addressof(dst_perm), nullptr, std::addressof(num_dst_allocator_blocks), + dst_address, size, KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); + + // Create an update allocator for the source. + Result src_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), + m_memory_block_slab_manager, + num_src_allocator_blocks); + R_TRY(src_allocator_result); + + // Create an update allocator for the destination. + Result dst_allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), + m_memory_block_slab_manager, + num_dst_allocator_blocks); + R_TRY(dst_allocator_result); KPageGroup src_pages; KPageGroup dst_pages; - const std::size_t num_pages{size / PageSize}; + const size_t num_pages{size / PageSize}; - AddRegionToPages(src_addr, num_pages, src_pages); - AddRegionToPages(dst_addr, num_pages, dst_pages); + AddRegionToPages(src_address, num_pages, src_pages); + AddRegionToPages(dst_address, num_pages, dst_pages); - if (!dst_pages.IsEqual(src_pages)) { - return ResultInvalidMemoryRegion; - } + R_UNLESS(dst_pages.IsEqual(src_pages), ResultInvalidMemoryRegion); { - auto block_guard = detail::ScopeExit([&] { MapPages(dst_addr, dst_pages, dst_perm); }); + auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); - CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); - CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite, - OperationType::ChangePermissions)); + R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); + R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, + OperationType::ChangePermissions)); block_guard.Cancel(); } - block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::UserReadWrite); - block_manager->Update(dst_addr, num_pages, KMemoryState::Free); + // Apply the memory block updates. + m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, + KMemoryPermission::UserReadWrite, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Locked); + m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, + KMemoryState::None, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, @@ -1225,48 +2064,54 @@ Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, if (const auto result{ Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; result.IsError()) { - const std::size_t num_pages{(addr - cur_addr) / PageSize}; + const size_t num_pages{(addr - cur_addr) / PageSize}; ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap) .IsSuccess()); - return result; + R_RETURN(result); } cur_addr += node.GetNumPages() * PageSize; } - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, KMemoryPermission perm) { // Check that the map is in range. - const std::size_t num_pages{page_linked_list.GetNumPages()}; - const std::size_t size{num_pages * PageSize}; + const size_t num_pages{page_linked_list.GetNumPages()}; + const size_t size{num_pages * PageSize}; R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Check the memory state. R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager); + // Map the pages. R_TRY(MapPages(address, page_linked_list, perm)); // Update the blocks. - block_manager->Update(address, num_pages, state, perm); + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, - PAddr phys_addr, bool is_pa_valid, VAddr region_start, - std::size_t region_num_pages, KMemoryState state, - KMemoryPermission perm) { +Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, + bool is_pa_valid, VAddr region_start, size_t region_num_pages, + KMemoryState state, KMemoryPermission perm) { ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); // Ensure this is a valid map request. @@ -1275,7 +2120,7 @@ Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Find a random address to map at. VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, @@ -1288,6 +2133,11 @@ Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t KMemoryAttribute::None, KMemoryAttribute::None) .IsSuccess()); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager); + // Perform mapping operation. if (is_pa_valid) { R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); @@ -1296,11 +2146,13 @@ Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t } // Update the blocks. - block_manager->Update(addr, num_pages, state, perm); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); // We successfully mapped the pages. *out_addr = addr; - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { @@ -1312,60 +2164,80 @@ Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, OperationType::Unmap)}; result.IsError()) { - return result; + R_RETURN(result); } cur_addr += node.GetNumPages() * PageSize; } - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state) { +Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) { // Check that the unmap is in range. - const std::size_t num_pages{page_linked_list.GetNumPages()}; - const std::size_t size{num_pages * PageSize}; - R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); - - // Lock the table. - KScopedLightLock lk(general_lock); - - // Check the memory state. - R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None, - KMemoryPermission::None, KMemoryAttribute::All, - KMemoryAttribute::None)); - - // Perform the unmap. - R_TRY(UnmapPages(addr, page_linked_list)); - - // Update the blocks. - block_manager->Update(addr, num_pages, state, KMemoryPermission::None); - - return ResultSuccess; -} - -Result KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) { - // Check that the unmap is in range. - const std::size_t size = num_pages * PageSize; + const size_t num_pages{page_linked_list.GetNumPages()}; + const size_t size{num_pages * PageSize}; R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Check the memory state. - std::size_t num_allocator_blocks{}; + size_t num_allocator_blocks; R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState::All, state, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + // Perform the unmap. + R_TRY(UnmapPages(address, page_linked_list)); + + // Update the blocks. + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); + + R_SUCCEED(); +} + +Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) { + // Check that the unmap is in range. + const size_t size = num_pages * PageSize; + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(m_general_lock); + + // Check the memory state. + size_t num_allocator_blocks{}; + R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, + KMemoryState::All, state, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::All, + KMemoryAttribute::None)); + + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Perform the unmap. R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); // Update the blocks. - block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None); + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, @@ -1380,7 +2252,7 @@ Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t n R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Check if state allows us to create the group. R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted, @@ -1390,15 +2262,15 @@ Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t n // Create a new page group for the region. R_TRY(this->MakePageGroup(*out, address, num_pages)); - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, +Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify we can change the memory permission. KMemoryState old_state; @@ -1429,111 +2301,108 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, break; default: ASSERT(false); + break; } } // Succeed if there's nothing to do. R_SUCCEED_IF(old_perm == new_perm && old_state == new_state); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Perform mapping operation. const auto operation = was_x ? OperationType::ChangePermissionsAndRefresh : OperationType::ChangePermissions; R_TRY(Operate(addr, num_pages, new_perm, operation)); // Update the blocks. - block_manager->Update(addr, num_pages, new_state, new_perm, KMemoryAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); // Ensure cache coherency, if we're setting pages as executable. if (is_x) { - system.InvalidateCpuInstructionCacheRange(addr, size); + m_system.InvalidateCpuInstructionCacheRange(addr, size); } - return ResultSuccess; + R_SUCCEED(); } KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) { - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); - return block_manager->FindBlock(addr).GetMemoryInfo(); + return m_memory_block_manager.FindBlock(addr)->GetMemoryInfo(); } KMemoryInfo KPageTable::QueryInfo(VAddr addr) { if (!Contains(addr, 1)) { - return {address_space_end, 0 - address_space_end, KMemoryState::Inaccessible, - KMemoryPermission::None, KMemoryAttribute::None, KMemoryPermission::None}; + return { + .m_address = m_address_space_end, + .m_size = 0 - m_address_space_end, + .m_state = static_cast(Svc::MemoryState::Inaccessible), + .m_device_disable_merge_left_count = 0, + .m_device_disable_merge_right_count = 0, + .m_ipc_lock_count = 0, + .m_device_use_count = 0, + .m_ipc_disable_merge_count = 0, + .m_permission = KMemoryPermission::None, + .m_attribute = KMemoryAttribute::None, + .m_original_permission = KMemoryPermission::None, + .m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None, + }; } return QueryInfoImpl(addr); } -Result KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) { - KScopedLightLock lk(general_lock); - - KMemoryState state{}; - KMemoryAttribute attribute{}; - - R_TRY(CheckMemoryState(&state, nullptr, &attribute, nullptr, addr, size, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, - KMemoryPermission::All, KMemoryPermission::UserReadWrite, - KMemoryAttribute::Mask, KMemoryAttribute::None, - KMemoryAttribute::IpcAndDeviceMapped)); - - block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked); - - return ResultSuccess; -} - -Result KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { - KScopedLightLock lk(general_lock); - - KMemoryState state{}; - - R_TRY(CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, - KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask, - KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); - - block_manager->Update(addr, size / PageSize, state, KMemoryPermission::UserReadWrite); - return ResultSuccess; -} - -Result KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, - Svc::MemoryPermission svc_perm) { +Result KPageTable::SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify we can change the memory permission. KMemoryState old_state; KMemoryPermission old_perm; - R_TRY(this->CheckMemoryState( - std::addressof(old_state), std::addressof(old_perm), nullptr, nullptr, addr, size, - KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None, - KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr, + std::addressof(num_allocator_blocks), addr, size, + KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, + KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::All, KMemoryAttribute::None)); // Determine new perm. const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm); R_SUCCEED_IF(old_perm == new_perm); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Perform mapping operation. R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); // Update the blocks. - block_manager->Update(addr, num_pages, old_state, new_perm, KMemoryAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) { +Result KPageTable::SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr) { const size_t num_pages = size / PageSize; ASSERT((static_cast(mask) | KMemoryAttribute::SetMask) == KMemoryAttribute::SetMask); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Verify we can change the memory attribute. KMemoryState old_state; @@ -1548,6 +2417,12 @@ Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u3 KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Determine the new attribute. const KMemoryAttribute new_attr = static_cast(((old_attr & static_cast(~mask)) | @@ -1557,123 +2432,140 @@ Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u3 this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh); // Update the blocks. - block_manager->Update(addr, num_pages, old_state, old_perm, new_attr); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm, + new_attr, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::SetMaxHeapSize(std::size_t size) { +Result KPageTable::SetMaxHeapSize(size_t size) { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Only process page tables are allowed to set heap size. ASSERT(!this->IsKernel()); - max_heap_size = size; + m_max_heap_size = size; - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { +Result KPageTable::SetHeapSize(VAddr* out, size_t size) { // Lock the physical memory mutex. - KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); + KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock); // Try to perform a reduction in heap, instead of an extension. VAddr cur_address{}; - std::size_t allocation_size{}; + size_t allocation_size{}; { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Validate that setting heap size is possible at all. - R_UNLESS(!is_kernel, ResultOutOfMemory); - R_UNLESS(size <= static_cast(heap_region_end - heap_region_start), + R_UNLESS(!m_is_kernel, ResultOutOfMemory); + R_UNLESS(size <= static_cast(m_heap_region_end - m_heap_region_start), ResultOutOfMemory); - R_UNLESS(size <= max_heap_size, ResultOutOfMemory); + R_UNLESS(size <= m_max_heap_size, ResultOutOfMemory); if (size < GetHeapSize()) { // The size being requested is less than the current size, so we need to free the end of // the heap. // Validate memory state. - std::size_t num_allocator_blocks; + size_t num_allocator_blocks; R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), - heap_region_start + size, GetHeapSize() - size, + m_heap_region_start + size, GetHeapSize() - size, KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, + num_allocator_blocks); + R_TRY(allocator_result); + // Unmap the end of the heap. const auto num_pages = (GetHeapSize() - size) / PageSize; - R_TRY(Operate(heap_region_start + size, num_pages, KMemoryPermission::None, + R_TRY(Operate(m_heap_region_start + size, num_pages, KMemoryPermission::None, OperationType::Unmap)); // Release the memory from the resource limit. - system.Kernel().CurrentProcess()->GetResourceLimit()->Release( - LimitableResource::PhysicalMemory, num_pages * PageSize); + m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, num_pages * PageSize); // Apply the memory block update. - block_manager->Update(heap_region_start + size, num_pages, KMemoryState::Free, - KMemoryPermission::None, KMemoryAttribute::None); + m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, + num_pages, KMemoryState::Free, KMemoryPermission::None, + KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, + size == 0 ? KMemoryBlockDisableMergeAttribute::Normal + : KMemoryBlockDisableMergeAttribute::None); // Update the current heap end. - current_heap_end = heap_region_start + size; + m_current_heap_end = m_heap_region_start + size; // Set the output. - *out = heap_region_start; - return ResultSuccess; + *out = m_heap_region_start; + R_SUCCEED(); } else if (size == GetHeapSize()) { // The size requested is exactly the current size. - *out = heap_region_start; - return ResultSuccess; + *out = m_heap_region_start; + R_SUCCEED(); } else { // We have to allocate memory. Determine how much to allocate and where while the table // is locked. - cur_address = current_heap_end; + cur_address = m_current_heap_end; allocation_size = size - GetHeapSize(); } } // Reserve memory for the heap extension. KScopedResourceReservation memory_reservation( - system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, - allocation_size); + m_resource_limit, LimitableResource::PhysicalMemoryMax, allocation_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the heap extension. KPageGroup pg; - R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( + R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( &pg, allocation_size / PageSize, - KMemoryManager::EncodeOption(memory_pool, allocation_option))); + KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option))); // Clear all the newly allocated pages. for (const auto& it : pg.Nodes()) { - std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, + std::memset(m_system.DeviceMemory().GetPointer(it.GetAddress()), m_heap_fill_value, it.GetSize()); } // Map the pages. { // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Ensure that the heap hasn't changed since we began executing. - ASSERT(cur_address == current_heap_end); + ASSERT(cur_address == m_current_heap_end); // Check the memory state. - std::size_t num_allocator_blocks{}; - R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), current_heap_end, + size_t num_allocator_blocks{}; + R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), m_current_heap_end, allocation_size, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator( + std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Map the pages. const auto num_pages = allocation_size / PageSize; - R_TRY(Operate(current_heap_end, num_pages, pg, OperationType::MapGroup)); + R_TRY(Operate(m_current_heap_end, num_pages, pg, OperationType::MapGroup)); // Clear all the newly allocated pages. - for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) { - std::memset(system.Memory().GetPointer(current_heap_end + (cur_page * PageSize)), 0, + for (size_t cur_page = 0; cur_page < num_pages; ++cur_page) { + std::memset(m_system.Memory().GetPointer(m_current_heap_end + (cur_page * PageSize)), 0, PageSize); } @@ -1681,133 +2573,194 @@ Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { memory_reservation.Commit(); // Apply the memory block update. - block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal, - KMemoryPermission::UserReadWrite, KMemoryAttribute::None); + m_memory_block_manager.Update( + std::addressof(allocator), m_current_heap_end, num_pages, KMemoryState::Normal, + KMemoryPermission::UserReadWrite, KMemoryAttribute::None, + m_heap_region_start == m_current_heap_end ? KMemoryBlockDisableMergeAttribute::Normal + : KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::None); // Update the current heap end. - current_heap_end = heap_region_start + size; + m_current_heap_end = m_heap_region_start + size; // Set the output. - *out = heap_region_start; - return ResultSuccess; + *out = m_heap_region_start; + R_SUCCEED(); } } -ResultVal KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, +ResultVal KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only, VAddr region_start, - std::size_t region_num_pages, KMemoryState state, + size_t region_num_pages, KMemoryState state, KMemoryPermission perm, PAddr map_addr) { - KScopedLightLock lk(general_lock); - - if (!CanContain(region_start, region_num_pages * PageSize, state)) { - return ResultInvalidCurrentMemory; - } - - if (region_num_pages <= needed_num_pages) { - return ResultOutOfMemory; - } + KScopedLightLock lk(m_general_lock); + R_UNLESS(CanContain(region_start, region_num_pages * PageSize, state), + ResultInvalidCurrentMemory); + R_UNLESS(region_num_pages > needed_num_pages, ResultOutOfMemory); const VAddr addr{ AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)}; - if (!addr) { - return ResultOutOfMemory; - } + R_UNLESS(addr, ResultOutOfMemory); + + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager); if (is_map_only) { R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); } else { KPageGroup page_group; - R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( + R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess( &page_group, needed_num_pages, - KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); + KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0)); R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); } - block_manager->Update(addr, needed_num_pages, state, perm); + // Update the blocks. + m_memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); return addr; } -Result KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { - KScopedLightLock lk(general_lock); +Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, + KMemoryPermission perm, bool is_aligned, + bool check_heap) { + // Lightly validate the range before doing anything else. + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); - KMemoryPermission perm{}; - if (const Result result{CheckMemoryState( - nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, - KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, - KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, - KMemoryAttribute::DeviceSharedAndUncached)}; - result.IsError()) { - return result; - } + // Lock the table. + KScopedLightLock lk(m_general_lock); - block_manager->UpdateLock( - addr, size / PageSize, - [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { - block->ShareToDevice(permission); - }, - perm); + // Check the memory state. + const auto test_state = + (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap) | + (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None); + size_t num_allocator_blocks; + KMemoryState old_state; + R_TRY(this->CheckMemoryState(std::addressof(old_state), nullptr, nullptr, + std::addressof(num_allocator_blocks), address, size, test_state, + test_state, perm, perm, + KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked, + KMemoryAttribute::None, KMemoryAttribute::DeviceShared)); - return ResultSuccess; + // Create an update allocator. + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + // Update the memory blocks. + m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, + &KMemoryBlock::ShareToDevice, KMemoryPermission::None); + + // Set whether the locked memory was io. + *out_is_io = old_state == KMemoryState::Io; + + R_SUCCEED(); } -Result KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { - KScopedLightLock lk(general_lock); +Result KPageTable::LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap) { + // Lightly validate the range before doing anything else. + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); - KMemoryPermission perm{}; - if (const Result result{CheckMemoryState( - nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, - KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, - KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, - KMemoryAttribute::DeviceSharedAndUncached)}; - result.IsError()) { - return result; - } + // Lock the table. + KScopedLightLock lk(m_general_lock); - block_manager->UpdateLock( - addr, size / PageSize, - [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { - block->UnshareToDevice(permission); - }, - perm); + // Check the memory state. + const auto test_state = KMemoryState::FlagCanDeviceMap | + (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None); + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryStateContiguous( + std::addressof(num_allocator_blocks), address, size, test_state, test_state, + KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); - return ResultSuccess; + // Create an update allocator. + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + // Update the memory blocks. + const KMemoryBlockManager::MemoryBlockLockFunction lock_func = + m_enable_device_address_space_merge + ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare + : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight; + m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func, + KMemoryPermission::None); + + R_SUCCEED(); } -Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size) { - return this->LockMemoryAndOpen( +Result KPageTable::UnlockForDeviceAddressSpace(VAddr address, size_t size) { + // Lightly validate the range before doing anything else. + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(m_general_lock); + + // Check the memory state. + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryStateContiguous( + std::addressof(num_allocator_blocks), address, size, KMemoryState::FlagCanDeviceMap, + KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); + + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + // Update the memory blocks. + m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, + &KMemoryBlock::UnshareToDevice, KMemoryPermission::None); + + R_SUCCEED(); +} + +Result KPageTable::LockForIpcUserBuffer(PAddr* out, VAddr address, size_t size) { + R_RETURN(this->LockMemoryAndOpen( + nullptr, out, address, size, KMemoryState::FlagCanIpcUserBuffer, + KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::All, + KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None, + KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite, + KMemoryAttribute::Locked)); +} + +Result KPageTable::UnlockForIpcUserBuffer(VAddr address, size_t size) { + R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanIpcUserBuffer, + KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::All, + KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, + KMemoryAttribute::Locked, nullptr)); +} + +Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size) { + R_RETURN(this->LockMemoryAndOpen( out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, - KMemoryAttribute::None, - static_cast(KMemoryPermission::NotMapped | - KMemoryPermission::KernelReadWrite), - KMemoryAttribute::Locked); + KMemoryAttribute::None, KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite, + KMemoryAttribute::Locked)); } -Result KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg) { - return this->UnlockMemory( +Result KPageTable::UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg) { + R_RETURN(this->UnlockMemory( addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, - KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg); -} - -Result KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { - block_manager = std::make_unique(start, end); - - return ResultSuccess; -} - -bool KPageTable::IsRegionMapped(VAddr address, u64 size) { - return CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, - KMemoryPermission::All, KMemoryPermission::None, KMemoryAttribute::Mask, - KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped) - .IsError(); + KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg)); } bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { - auto start_ptr = system.Memory().GetPointer(addr); + auto start_ptr = m_system.DeviceMemory().GetPointer(addr); for (u64 offset{}; offset < size; offset += PageSize) { - if (start_ptr != system.Memory().GetPointer(addr + offset)) { + if (start_ptr != m_system.DeviceMemory().GetPointer(addr + offset)) { return false; } start_ptr += PageSize; @@ -1815,8 +2768,7 @@ bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { return true; } -void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages, - KPageGroup& page_linked_list) { +void KPageTable::AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list) { VAddr addr{start}; while (addr < start + (num_pages * PageSize)) { const PAddr paddr{GetPhysicalAddr(addr)}; @@ -1826,16 +2778,16 @@ void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages, } } -VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, - u64 needed_num_pages, std::size_t align) { - if (is_aslr_enabled) { +VAddr KPageTable::AllocateVirtualMemory(VAddr start, size_t region_num_pages, u64 needed_num_pages, + size_t align) { + if (m_enable_aslr) { UNIMPLEMENTED(); } - return block_manager->FindFreeArea(start, region_num_pages, needed_num_pages, align, 0, - IsKernel() ? 1 : 4); + return m_memory_block_manager.FindFreeArea(start, region_num_pages, needed_num_pages, align, 0, + IsKernel() ? 1 : 4); } -Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group, +Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_group, OperationType operation) { ASSERT(this->IsLockedByCurrentThread()); @@ -1844,23 +2796,24 @@ Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup& ASSERT(num_pages == page_group.GetNumPages()); for (const auto& node : page_group.Nodes()) { - const std::size_t size{node.GetNumPages() * PageSize}; + const size_t size{node.GetNumPages() * PageSize}; switch (operation) { case OperationType::MapGroup: - system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress()); + m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress()); break; default: ASSERT(false); + break; } addr += size; } - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, +Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, OperationType operation, PAddr map_addr) { ASSERT(this->IsLockedByCurrentThread()); @@ -1870,12 +2823,16 @@ Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission switch (operation) { case OperationType::Unmap: - system.Memory().UnmapRegion(page_table_impl, addr, num_pages * PageSize); + m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize); break; case OperationType::Map: { ASSERT(map_addr); ASSERT(Common::IsAligned(map_addr, PageSize)); - system.Memory().MapMemoryRegion(page_table_impl, addr, num_pages * PageSize, map_addr); + m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr); + break; + } + case OperationType::Separate: { + // HACK: Unimplemented. break; } case OperationType::ChangePermissions: @@ -1883,26 +2840,38 @@ Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission break; default: ASSERT(false); + break; + } + R_SUCCEED(); +} + +void KPageTable::FinalizeUpdate(PageLinkedList* page_list) { + while (page_list->Peek()) { + [[maybe_unused]] auto page = page_list->Pop(); + + // TODO(bunnei): Free pages once they are allocated in guest memory + // ASSERT(this->GetPageTableManager().IsInPageTableHeap(page)); + // ASSERT(this->GetPageTableManager().GetRefCount(page) == 0); + // this->GetPageTableManager().Free(page); } - return ResultSuccess; } VAddr KPageTable::GetRegionAddress(KMemoryState state) const { switch (state) { case KMemoryState::Free: case KMemoryState::Kernel: - return address_space_start; + return m_address_space_start; case KMemoryState::Normal: - return heap_region_start; + return m_heap_region_start; case KMemoryState::Ipc: case KMemoryState::NonSecureIpc: case KMemoryState::NonDeviceIpc: - return alias_region_start; + return m_alias_region_start; case KMemoryState::Stack: - return stack_region_start; + return m_stack_region_start; case KMemoryState::Static: case KMemoryState::ThreadLocal: - return kernel_map_region_start; + return m_kernel_map_region_start; case KMemoryState::Io: case KMemoryState::Shared: case KMemoryState::AliasCode: @@ -1913,31 +2882,32 @@ VAddr KPageTable::GetRegionAddress(KMemoryState state) const { case KMemoryState::GeneratedCode: case KMemoryState::CodeOut: case KMemoryState::Coverage: - return alias_code_region_start; + case KMemoryState::Insecure: + return m_alias_code_region_start; case KMemoryState::Code: case KMemoryState::CodeData: - return code_region_start; + return m_code_region_start; default: UNREACHABLE(); } } -std::size_t KPageTable::GetRegionSize(KMemoryState state) const { +size_t KPageTable::GetRegionSize(KMemoryState state) const { switch (state) { case KMemoryState::Free: case KMemoryState::Kernel: - return address_space_end - address_space_start; + return m_address_space_end - m_address_space_start; case KMemoryState::Normal: - return heap_region_end - heap_region_start; + return m_heap_region_end - m_heap_region_start; case KMemoryState::Ipc: case KMemoryState::NonSecureIpc: case KMemoryState::NonDeviceIpc: - return alias_region_end - alias_region_start; + return m_alias_region_end - m_alias_region_start; case KMemoryState::Stack: - return stack_region_end - stack_region_start; + return m_stack_region_end - m_stack_region_start; case KMemoryState::Static: case KMemoryState::ThreadLocal: - return kernel_map_region_end - kernel_map_region_start; + return m_kernel_map_region_end - m_kernel_map_region_start; case KMemoryState::Io: case KMemoryState::Shared: case KMemoryState::AliasCode: @@ -1948,16 +2918,17 @@ std::size_t KPageTable::GetRegionSize(KMemoryState state) const { case KMemoryState::GeneratedCode: case KMemoryState::CodeOut: case KMemoryState::Coverage: - return alias_code_region_end - alias_code_region_start; + case KMemoryState::Insecure: + return m_alias_code_region_end - m_alias_code_region_start; case KMemoryState::Code: case KMemoryState::CodeData: - return code_region_end - code_region_start; + return m_code_region_end - m_code_region_start; default: UNREACHABLE(); } } -bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const { +bool KPageTable::CanContain(VAddr addr, size_t size, KMemoryState state) const { const VAddr end = addr + size; const VAddr last = end - 1; @@ -1966,10 +2937,10 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co const bool is_in_region = region_start <= addr && addr < end && last <= region_start + region_size - 1; - const bool is_in_heap = !(end <= heap_region_start || heap_region_end <= addr || - heap_region_start == heap_region_end); - const bool is_in_alias = !(end <= alias_region_start || alias_region_end <= addr || - alias_region_start == alias_region_end); + const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr || + m_heap_region_start == m_heap_region_end); + const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr || + m_alias_region_start == m_alias_region_end); switch (state) { case KMemoryState::Free: case KMemoryState::Kernel: @@ -1989,6 +2960,7 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co case KMemoryState::GeneratedCode: case KMemoryState::CodeOut: case KMemoryState::Coverage: + case KMemoryState::Insecure: return is_in_region && !is_in_heap && !is_in_alias; case KMemoryState::Normal: ASSERT(is_in_heap); @@ -2008,23 +2980,23 @@ Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_ KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr) const { // Validate the states match expectation. - R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory); - R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory); - R_UNLESS((info.attribute & attr_mask) == attr, ResultInvalidCurrentMemory); + R_UNLESS((info.m_state & state_mask) == state, ResultInvalidCurrentMemory); + R_UNLESS((info.m_permission & perm_mask) == perm, ResultInvalidCurrentMemory); + R_UNLESS((info.m_attribute & attr_mask) == attr, ResultInvalidCurrentMemory); - return ResultSuccess; + R_SUCCEED(); } -Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, - std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, +Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr addr, size_t size, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr) const { ASSERT(this->IsLockedByCurrentThread()); // Get information about the first block. const VAddr last_addr = addr + size - 1; - KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr); + KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr); KMemoryInfo info = it->GetMemoryInfo(); // If the start address isn't aligned, we need a block. @@ -2042,7 +3014,7 @@ Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VA // Advance our iterator. it++; - ASSERT(it != block_manager->cend()); + ASSERT(it != m_memory_block_manager.cend()); info = it->GetMemoryInfo(); } @@ -2054,12 +3026,12 @@ Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VA *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; } - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, - KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, - VAddr addr, std::size_t size, KMemoryState state_mask, + KMemoryAttribute* out_attr, size_t* out_blocks_needed, + VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { @@ -2067,7 +3039,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* // Get information about the first block. const VAddr last_addr = addr + size - 1; - KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr); + KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr); KMemoryInfo info = it->GetMemoryInfo(); // If the start address isn't aligned, we need a block. @@ -2075,14 +3047,14 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0; // Validate all blocks in the range have correct state. - const KMemoryState first_state = info.state; - const KMemoryPermission first_perm = info.perm; - const KMemoryAttribute first_attr = info.attribute; + const KMemoryState first_state = info.m_state; + const KMemoryPermission first_perm = info.m_permission; + const KMemoryAttribute first_attr = info.m_attribute; while (true) { // Validate the current block. - R_UNLESS(info.state == first_state, ResultInvalidCurrentMemory); - R_UNLESS(info.perm == first_perm, ResultInvalidCurrentMemory); - R_UNLESS((info.attribute | ignore_attr) == (first_attr | ignore_attr), + R_UNLESS(info.m_state == first_state, ResultInvalidCurrentMemory); + R_UNLESS(info.m_permission == first_perm, ResultInvalidCurrentMemory); + R_UNLESS((info.m_attribute | ignore_attr) == (first_attr | ignore_attr), ResultInvalidCurrentMemory); // Validate against the provided masks. @@ -2095,7 +3067,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* // Advance our iterator. it++; - ASSERT(it != block_manager->cend()); + ASSERT(it != m_memory_block_manager.cend()); info = it->GetMemoryInfo(); } @@ -2116,7 +3088,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* if (out_blocks_needed != nullptr) { *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; } - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size, @@ -2134,7 +3106,7 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Check that the output page group is empty, if it exists. if (out_pg) { @@ -2162,6 +3134,12 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr R_TRY(this->MakePageGroup(*out_pg, addr, num_pages)); } + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Decide on new perm and attr. new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; KMemoryAttribute new_attr = static_cast(old_attr | lock_attr); @@ -2172,9 +3150,11 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr } // Apply the memory block updates. - block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, + new_attr, KMemoryBlockDisableMergeAttribute::Locked, + KMemoryBlockDisableMergeAttribute::None); - return ResultSuccess; + R_SUCCEED(); } Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, @@ -2191,7 +3171,7 @@ Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); // Lock the table. - KScopedLightLock lk(general_lock); + KScopedLightLock lk(m_general_lock); // Check the state. KMemoryState old_state{}; @@ -2213,15 +3193,23 @@ Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; KMemoryAttribute new_attr = static_cast(old_attr & ~lock_attr); + // Create an update allocator. + Result allocator_result{ResultSuccess}; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + // Update permission, if we need to. if (new_perm != old_perm) { R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); } // Apply the memory block updates. - block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, + new_attr, KMemoryBlockDisableMergeAttribute::None, + KMemoryBlockDisableMergeAttribute::Locked); - return ResultSuccess; + R_SUCCEED(); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 25774f2..f1ca785 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -9,11 +9,14 @@ #include "common/common_types.h" #include "common/page_table.h" #include "core/file_sys/program_metadata.h" +#include "core/hle/kernel/k_dynamic_resource_manager.h" #include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_memory_block.h" +#include "core/hle/kernel/k_memory_block_manager.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/result.h" +#include "core/memory.h" namespace Core { class System; @@ -21,7 +24,10 @@ class System; namespace Kernel { +class KBlockInfoManager; class KMemoryBlockManager; +class KResourceLimit; +class KSystemResource; class KPageTable final { public: @@ -34,129 +40,185 @@ public: ~KPageTable(); Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, - VAddr code_addr, std::size_t code_size, KMemoryManager::Pool pool); - Result MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, + bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, + VAddr code_addr, size_t code_size, KSystemResource* system_resource, + KResourceLimit* resource_limit); + + void Finalize(); + + Result MapProcessCode(VAddr addr, size_t pages_count, KMemoryState state, KMemoryPermission perm); - Result MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); - Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, + Result MapCodeMemory(VAddr dst_address, VAddr src_address, size_t size); + Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t size, ICacheInvalidationStrategy icache_invalidation_strategy); - Result UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, + Result UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& src_page_table, VAddr src_addr); - Result MapPhysicalMemory(VAddr addr, std::size_t size); - Result UnmapPhysicalMemory(VAddr addr, std::size_t size); - Result MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); - Result UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); + Result MapPhysicalMemory(VAddr addr, size_t size); + Result UnmapPhysicalMemory(VAddr addr, size_t size); + Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size); + Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size); Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state, KMemoryPermission perm); - Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr, + Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, KMemoryState state, KMemoryPermission perm) { - return this->MapPages(out_addr, num_pages, alignment, phys_addr, true, - this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize, - state, perm); + R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, + this->GetRegionAddress(state), + this->GetRegionSize(state) / PageSize, state, perm)); } Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state); - Result UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state); - Result SetProcessMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission svc_perm); + Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state); + Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm); KMemoryInfo QueryInfo(VAddr addr); - Result ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); - Result ResetTransferMemory(VAddr addr, std::size_t size); - Result SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm); - Result SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr); - Result SetMaxHeapSize(std::size_t size); - Result SetHeapSize(VAddr* out, std::size_t size); - ResultVal AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, - bool is_map_only, VAddr region_start, - std::size_t region_num_pages, KMemoryState state, - KMemoryPermission perm, PAddr map_addr = 0); - Result LockForDeviceAddressSpace(VAddr addr, std::size_t size); - Result UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); - Result LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size); - Result UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg); + Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm); + Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr); + Result SetMaxHeapSize(size_t size); + Result SetHeapSize(VAddr* out, size_t size); + ResultVal AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only, + VAddr region_start, size_t region_num_pages, + KMemoryState state, KMemoryPermission perm, + PAddr map_addr = 0); + + Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, + KMemoryPermission perm, bool is_aligned, bool check_heap); + Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap); + + Result UnlockForDeviceAddressSpace(VAddr addr, size_t size); + + Result LockForIpcUserBuffer(PAddr* out, VAddr address, size_t size); + Result UnlockForIpcUserBuffer(VAddr address, size_t size); + + Result SetupForIpc(VAddr* out_dst_addr, size_t size, VAddr src_addr, KPageTable& src_page_table, + KMemoryPermission test_perm, KMemoryState dst_state, bool send); + Result CleanupForIpcServer(VAddr address, size_t size, KMemoryState dst_state); + Result CleanupForIpcClient(VAddr address, size_t size, KMemoryState dst_state); + + Result LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size); + Result UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg); Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr); Common::PageTable& PageTableImpl() { - return page_table_impl; + return *m_page_table_impl; } const Common::PageTable& PageTableImpl() const { - return page_table_impl; + return *m_page_table_impl; } - bool CanContain(VAddr addr, std::size_t size, KMemoryState state) const; + bool CanContain(VAddr addr, size_t size, KMemoryState state) const; + +protected: + struct PageLinkedList { + private: + struct Node { + Node* m_next; + std::array m_buffer; + }; + + public: + constexpr PageLinkedList() = default; + + void Push(Node* n) { + ASSERT(Common::IsAligned(reinterpret_cast(n), PageSize)); + n->m_next = m_root; + m_root = n; + } + + void Push(Core::Memory::Memory& memory, VAddr addr) { + this->Push(memory.GetPointer(addr)); + } + + Node* Peek() const { + return m_root; + } + + Node* Pop() { + Node* const r = m_root; + + m_root = r->m_next; + r->m_next = nullptr; + + return r; + } + + private: + Node* m_root{}; + }; + static_assert(std::is_trivially_destructible::value); private: enum class OperationType : u32 { - Map, - MapGroup, - Unmap, - ChangePermissions, - ChangePermissionsAndRefresh, + Map = 0, + MapFirst = 1, + MapGroup = 2, + Unmap = 3, + ChangePermissions = 4, + ChangePermissionsAndRefresh = 5, + Separate = 6, }; - static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = KMemoryAttribute::DontCareMask | - KMemoryAttribute::IpcLocked | - KMemoryAttribute::DeviceShared; + static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = + KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; - Result InitializeMemoryLayout(VAddr start, VAddr end); Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); - Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr, - bool is_pa_valid, VAddr region_start, std::size_t region_num_pages, + Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, + bool is_pa_valid, VAddr region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm); Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list); - bool IsRegionMapped(VAddr address, u64 size); bool IsRegionContiguous(VAddr addr, u64 size) const; - void AddRegionToPages(VAddr start, std::size_t num_pages, KPageGroup& page_linked_list); + void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list); KMemoryInfo QueryInfoImpl(VAddr addr); - VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages, - std::size_t align); - Result Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group, + VAddr AllocateVirtualMemory(VAddr start, size_t region_num_pages, u64 needed_num_pages, + size_t align); + Result Operate(VAddr addr, size_t num_pages, const KPageGroup& page_group, OperationType operation); - Result Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, - OperationType operation, PAddr map_addr = 0); + Result Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, OperationType operation, + PAddr map_addr = 0); + void FinalizeUpdate(PageLinkedList* page_list); VAddr GetRegionAddress(KMemoryState state) const; - std::size_t GetRegionSize(KMemoryState state) const; + size_t GetRegionSize(KMemoryState state) const; - VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages, - std::size_t alignment, std::size_t offset, std::size_t guard_pages); + VAddr FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages, + size_t alignment, size_t offset, size_t guard_pages); - Result CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, + Result CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr) const; - Result CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask, + Result CheckMemoryStateContiguous(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr) const { - return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, - perm, attr_mask, attr); + R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, + perm, attr_mask, attr)); } Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr) const; Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, - KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, VAddr addr, - std::size_t size, KMemoryState state_mask, KMemoryState state, + KMemoryAttribute* out_attr, size_t* out_blocks_needed, VAddr addr, + size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; - Result CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, + Result CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { - return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, - state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); + R_RETURN(CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, + state_mask, state, perm_mask, perm, attr_mask, attr, + ignore_attr)); } - Result CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, + Result CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { - return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, - attr_mask, attr, ignore_attr); + R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, + attr_mask, attr, ignore_attr)); } Result LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size, @@ -174,13 +236,13 @@ private: bool IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages); bool IsLockedByCurrentThread() const { - return general_lock.IsLockedByCurrentThread(); + return m_general_lock.IsLockedByCurrentThread(); } bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) { ASSERT(this->IsLockedByCurrentThread()); - return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr); + return layout.IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr); } bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const { @@ -191,95 +253,108 @@ private: return *out != 0; } - mutable KLightLock general_lock; - mutable KLightLock map_physical_memory_lock; + Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed, VAddr address, + size_t size, KMemoryPermission test_perm, KMemoryState dst_state); + Result SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_addr, + KMemoryPermission test_perm, KMemoryState dst_state, + KPageTable& src_page_table, bool send); + void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address, + size_t size, KMemoryPermission prot_perm); - std::unique_ptr block_manager; + // HACK: These will be removed once we automatically manage page reference counts. + void HACK_OpenPages(PAddr phys_addr, size_t num_pages); + void HACK_ClosePages(VAddr virt_addr, size_t num_pages); + + mutable KLightLock m_general_lock; + mutable KLightLock m_map_physical_memory_lock; public: constexpr VAddr GetAddressSpaceStart() const { - return address_space_start; + return m_address_space_start; } constexpr VAddr GetAddressSpaceEnd() const { - return address_space_end; + return m_address_space_end; } - constexpr std::size_t GetAddressSpaceSize() const { - return address_space_end - address_space_start; + constexpr size_t GetAddressSpaceSize() const { + return m_address_space_end - m_address_space_start; } constexpr VAddr GetHeapRegionStart() const { - return heap_region_start; + return m_heap_region_start; } constexpr VAddr GetHeapRegionEnd() const { - return heap_region_end; + return m_heap_region_end; } - constexpr std::size_t GetHeapRegionSize() const { - return heap_region_end - heap_region_start; + constexpr size_t GetHeapRegionSize() const { + return m_heap_region_end - m_heap_region_start; } constexpr VAddr GetAliasRegionStart() const { - return alias_region_start; + return m_alias_region_start; } constexpr VAddr GetAliasRegionEnd() const { - return alias_region_end; + return m_alias_region_end; } - constexpr std::size_t GetAliasRegionSize() const { - return alias_region_end - alias_region_start; + constexpr size_t GetAliasRegionSize() const { + return m_alias_region_end - m_alias_region_start; } constexpr VAddr GetStackRegionStart() const { - return stack_region_start; + return m_stack_region_start; } constexpr VAddr GetStackRegionEnd() const { - return stack_region_end; + return m_stack_region_end; } - constexpr std::size_t GetStackRegionSize() const { - return stack_region_end - stack_region_start; + constexpr size_t GetStackRegionSize() const { + return m_stack_region_end - m_stack_region_start; } constexpr VAddr GetKernelMapRegionStart() const { - return kernel_map_region_start; + return m_kernel_map_region_start; } constexpr VAddr GetKernelMapRegionEnd() const { - return kernel_map_region_end; + return m_kernel_map_region_end; } constexpr VAddr GetCodeRegionStart() const { - return code_region_start; + return m_code_region_start; } constexpr VAddr GetCodeRegionEnd() const { - return code_region_end; + return m_code_region_end; } constexpr VAddr GetAliasCodeRegionStart() const { - return alias_code_region_start; + return m_alias_code_region_start; + } + constexpr VAddr GetAliasCodeRegionEnd() const { + return m_alias_code_region_end; } constexpr VAddr GetAliasCodeRegionSize() const { - return alias_code_region_end - alias_code_region_start; + return m_alias_code_region_end - m_alias_code_region_start; } - std::size_t GetNormalMemorySize() { - KScopedLightLock lk(general_lock); - return GetHeapSize() + mapped_physical_memory_size; + size_t GetNormalMemorySize() { + KScopedLightLock lk(m_general_lock); + return GetHeapSize() + m_mapped_physical_memory_size; } - constexpr std::size_t GetAddressSpaceWidth() const { - return address_space_width; + constexpr size_t GetAddressSpaceWidth() const { + return m_address_space_width; } - constexpr std::size_t GetHeapSize() const { - return current_heap_end - heap_region_start; + constexpr size_t GetHeapSize() const { + return m_current_heap_end - m_heap_region_start; } - constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const { - return address_space_start <= address && address + size - 1 <= address_space_end - 1; + constexpr bool IsInsideAddressSpace(VAddr address, size_t size) const { + return m_address_space_start <= address && address + size - 1 <= m_address_space_end - 1; } - constexpr bool IsOutsideAliasRegion(VAddr address, std::size_t size) const { - return alias_region_start > address || address + size - 1 > alias_region_end - 1; + constexpr bool IsOutsideAliasRegion(VAddr address, size_t size) const { + return m_alias_region_start > address || address + size - 1 > m_alias_region_end - 1; } - constexpr bool IsOutsideStackRegion(VAddr address, std::size_t size) const { - return stack_region_start > address || address + size - 1 > stack_region_end - 1; + constexpr bool IsOutsideStackRegion(VAddr address, size_t size) const { + return m_stack_region_start > address || address + size - 1 > m_stack_region_end - 1; } - constexpr bool IsInvalidRegion(VAddr address, std::size_t size) const { + constexpr bool IsInvalidRegion(VAddr address, size_t size) const { return address + size - 1 > GetAliasCodeRegionStart() + GetAliasCodeRegionSize() - 1; } - constexpr bool IsInsideHeapRegion(VAddr address, std::size_t size) const { - return address + size > heap_region_start && heap_region_end > address; + constexpr bool IsInsideHeapRegion(VAddr address, size_t size) const { + return address + size > m_heap_region_start && m_heap_region_end > address; } - constexpr bool IsInsideAliasRegion(VAddr address, std::size_t size) const { - return address + size > alias_region_start && alias_region_end > address; + constexpr bool IsInsideAliasRegion(VAddr address, size_t size) const { + return address + size > m_alias_region_start && m_alias_region_end > address; } - constexpr bool IsOutsideASLRRegion(VAddr address, std::size_t size) const { + constexpr bool IsOutsideASLRRegion(VAddr address, size_t size) const { if (IsInvalidRegion(address, size)) { return true; } @@ -291,73 +366,128 @@ public: } return {}; } - constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { + constexpr bool IsInsideASLRRegion(VAddr address, size_t size) const { return !IsOutsideASLRRegion(address, size); } - constexpr std::size_t GetNumGuardPages() const { + constexpr size_t GetNumGuardPages() const { return IsKernel() ? 1 : 4; } PAddr GetPhysicalAddr(VAddr addr) const { - const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; + const auto backing_addr = m_page_table_impl->backing_addr[addr >> PageBits]; ASSERT(backing_addr); return backing_addr + addr; } constexpr bool Contains(VAddr addr) const { - return address_space_start <= addr && addr <= address_space_end - 1; + return m_address_space_start <= addr && addr <= m_address_space_end - 1; } - constexpr bool Contains(VAddr addr, std::size_t size) const { - return address_space_start <= addr && addr < addr + size && - addr + size - 1 <= address_space_end - 1; + constexpr bool Contains(VAddr addr, size_t size) const { + return m_address_space_start <= addr && addr < addr + size && + addr + size - 1 <= m_address_space_end - 1; + } + +public: + static VAddr GetLinearMappedVirtualAddress(const KMemoryLayout& layout, PAddr addr) { + return layout.GetLinearVirtualAddress(addr); + } + + static PAddr GetLinearMappedPhysicalAddress(const KMemoryLayout& layout, VAddr addr) { + return layout.GetLinearPhysicalAddress(addr); + } + + static VAddr GetHeapVirtualAddress(const KMemoryLayout& layout, PAddr addr) { + return GetLinearMappedVirtualAddress(layout, addr); + } + + static PAddr GetHeapPhysicalAddress(const KMemoryLayout& layout, VAddr addr) { + return GetLinearMappedPhysicalAddress(layout, addr); + } + + static VAddr GetPageTableVirtualAddress(const KMemoryLayout& layout, PAddr addr) { + return GetLinearMappedVirtualAddress(layout, addr); + } + + static PAddr GetPageTablePhysicalAddress(const KMemoryLayout& layout, VAddr addr) { + return GetLinearMappedPhysicalAddress(layout, addr); } private: constexpr bool IsKernel() const { - return is_kernel; + return m_is_kernel; } constexpr bool IsAslrEnabled() const { - return is_aslr_enabled; + return m_enable_aslr; } - constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { - return (address_space_start <= addr) && - (num_pages <= (address_space_end - address_space_start) / PageSize) && - (addr + num_pages * PageSize - 1 <= address_space_end - 1); + constexpr bool ContainsPages(VAddr addr, size_t num_pages) const { + return (m_address_space_start <= addr) && + (num_pages <= (m_address_space_end - m_address_space_start) / PageSize) && + (addr + num_pages * PageSize - 1 <= m_address_space_end - 1); } private: - VAddr address_space_start{}; - VAddr address_space_end{}; - VAddr heap_region_start{}; - VAddr heap_region_end{}; - VAddr current_heap_end{}; - VAddr alias_region_start{}; - VAddr alias_region_end{}; - VAddr stack_region_start{}; - VAddr stack_region_end{}; - VAddr kernel_map_region_start{}; - VAddr kernel_map_region_end{}; - VAddr code_region_start{}; - VAddr code_region_end{}; - VAddr alias_code_region_start{}; - VAddr alias_code_region_end{}; + class KScopedPageTableUpdater { + private: + KPageTable* m_pt{}; + PageLinkedList m_ll; - std::size_t mapped_physical_memory_size{}; - std::size_t max_heap_size{}; - std::size_t max_physical_memory_size{}; - std::size_t address_space_width{}; + public: + explicit KScopedPageTableUpdater(KPageTable* pt) : m_pt(pt) {} + explicit KScopedPageTableUpdater(KPageTable& pt) : KScopedPageTableUpdater(&pt) {} + ~KScopedPageTableUpdater() { + m_pt->FinalizeUpdate(this->GetPageList()); + } - bool is_kernel{}; - bool is_aslr_enabled{}; + PageLinkedList* GetPageList() { + return &m_ll; + } + }; - u32 heap_fill_value{}; - const KMemoryRegion* cached_physical_heap_region{}; +private: + VAddr m_address_space_start{}; + VAddr m_address_space_end{}; + VAddr m_heap_region_start{}; + VAddr m_heap_region_end{}; + VAddr m_current_heap_end{}; + VAddr m_alias_region_start{}; + VAddr m_alias_region_end{}; + VAddr m_stack_region_start{}; + VAddr m_stack_region_end{}; + VAddr m_kernel_map_region_start{}; + VAddr m_kernel_map_region_end{}; + VAddr m_code_region_start{}; + VAddr m_code_region_end{}; + VAddr m_alias_code_region_start{}; + VAddr m_alias_code_region_end{}; - KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; - KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; + size_t m_max_heap_size{}; + size_t m_mapped_physical_memory_size{}; + size_t m_mapped_unsafe_physical_memory{}; + size_t m_mapped_insecure_memory{}; + size_t m_mapped_ipc_server_memory{}; + size_t m_address_space_width{}; - Common::PageTable page_table_impl; + KMemoryBlockManager m_memory_block_manager; + u32 m_allocate_option{}; - Core::System& system; + bool m_is_kernel{}; + bool m_enable_aslr{}; + bool m_enable_device_address_space_merge{}; + + KMemoryBlockSlabManager* m_memory_block_slab_manager{}; + KBlockInfoManager* m_block_info_manager{}; + KResourceLimit* m_resource_limit{}; + + u32 m_heap_fill_value{}; + u32 m_ipc_fill_value{}; + u32 m_stack_fill_value{}; + const KMemoryRegion* m_cached_physical_heap_region{}; + + KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application}; + KMemoryManager::Direction m_allocation_option{KMemoryManager::Direction::FromFront}; + + std::unique_ptr m_page_table_impl; + + Core::System& m_system; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_table_manager.h b/src/core/hle/kernel/k_page_table_manager.h new file mode 100644 index 0000000..91a45cd --- /dev/null +++ b/src/core/hle/kernel/k_page_table_manager.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/kernel/k_dynamic_resource_manager.h" +#include "core/hle/kernel/k_page_table_slab_heap.h" + +namespace Kernel { + +class KPageTableManager : public KDynamicResourceManager { +public: + using RefCount = KPageTableSlabHeap::RefCount; + static constexpr size_t PageTableSize = KPageTableSlabHeap::PageTableSize; + +public: + KPageTableManager() = default; + + void Initialize(KDynamicPageManager* page_allocator, KPageTableSlabHeap* pt_heap) { + m_pt_heap = pt_heap; + + static_assert(std::derived_from); + BaseHeap::Initialize(page_allocator, pt_heap); + } + + VAddr Allocate() { + return VAddr(BaseHeap::Allocate()); + } + + RefCount GetRefCount(VAddr addr) const { + return m_pt_heap->GetRefCount(addr); + } + + void Open(VAddr addr, int count) { + return m_pt_heap->Open(addr, count); + } + + bool Close(VAddr addr, int count) { + return m_pt_heap->Close(addr, count); + } + + bool IsInPageTableHeap(VAddr addr) const { + return m_pt_heap->IsInRange(addr); + } + +private: + using BaseHeap = KDynamicResourceManager; + + KPageTableSlabHeap* m_pt_heap{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_page_table_slab_heap.h b/src/core/hle/kernel/k_page_table_slab_heap.h new file mode 100644 index 0000000..a9543cb --- /dev/null +++ b/src/core/hle/kernel/k_page_table_slab_heap.h @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/kernel/k_dynamic_slab_heap.h" +#include "core/hle/kernel/slab_helpers.h" + +namespace Kernel { + +namespace impl { + +class PageTablePage { +public: + // Do not initialize anything. + PageTablePage() = default; + +private: + std::array m_buffer{}; +}; +static_assert(sizeof(PageTablePage) == PageSize); + +} // namespace impl + +class KPageTableSlabHeap : public KDynamicSlabHeap { +public: + using RefCount = u16; + static constexpr size_t PageTableSize = sizeof(impl::PageTablePage); + static_assert(PageTableSize == PageSize); + +public: + KPageTableSlabHeap() = default; + + static constexpr size_t CalculateReferenceCountSize(size_t size) { + return (size / PageSize) * sizeof(RefCount); + } + + void Initialize(KDynamicPageManager* page_allocator, size_t object_count, RefCount* rc) { + BaseHeap::Initialize(page_allocator, object_count); + this->Initialize(rc); + } + + RefCount GetRefCount(VAddr addr) { + ASSERT(this->IsInRange(addr)); + return *this->GetRefCountPointer(addr); + } + + void Open(VAddr addr, int count) { + ASSERT(this->IsInRange(addr)); + + *this->GetRefCountPointer(addr) += static_cast(count); + + ASSERT(this->GetRefCount(addr) > 0); + } + + bool Close(VAddr addr, int count) { + ASSERT(this->IsInRange(addr)); + ASSERT(this->GetRefCount(addr) >= count); + + *this->GetRefCountPointer(addr) -= static_cast(count); + return this->GetRefCount(addr) == 0; + } + + bool IsInPageTableHeap(VAddr addr) const { + return this->IsInRange(addr); + } + +private: + void Initialize([[maybe_unused]] RefCount* rc) { + // TODO(bunnei): Use rc once we support kernel virtual memory allocations. + const auto count = this->GetSize() / PageSize; + m_ref_counts.resize(count); + + for (size_t i = 0; i < count; i++) { + m_ref_counts[i] = 0; + } + } + + RefCount* GetRefCountPointer(VAddr addr) { + return m_ref_counts.data() + ((addr - this->GetAddress()) / PageSize); + } + +private: + using BaseHeap = KDynamicSlabHeap; + + std::vector m_ref_counts; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp index 7a5a9dc..77d00ae 100644 --- a/src/core/hle/kernel/k_port.cpp +++ b/src/core/hle/kernel/k_port.cpp @@ -57,12 +57,6 @@ Result KPort::EnqueueSession(KServerSession* session) { server.EnqueueSession(session); - if (auto session_ptr = server.GetSessionRequestHandler().lock()) { - session_ptr->ClientConnected(server.AcceptSession()); - } else { - ASSERT(false); - } - return ResultSuccess; } diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index d3e9966..a1abf5d 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -38,7 +38,7 @@ namespace { */ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) { const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); - ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); + ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1)); KThread* thread = KThread::Create(system.Kernel()); SCOPE_EXIT({ thread->Close(); }); @@ -72,7 +72,8 @@ Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process->name = std::move(process_name); process->resource_limit = res_limit; - process->status = ProcessStatus::Created; + process->system_resource_address = 0; + process->state = State::Created; process->program_id = 0; process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() : kernel.CreateNewUserProcessID(); @@ -92,11 +93,12 @@ Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process->exception_thread = nullptr; process->is_suspended = false; process->schedule_count = 0; + process->is_handle_table_initialized = false; // Open a reference to the resource limit. process->resource_limit->Open(); - return ResultSuccess; + R_SUCCEED(); } void KProcess::DoWorkerTaskImpl() { @@ -121,9 +123,9 @@ void KProcess::DecrementRunningThreadCount() { } } -u64 KProcess::GetTotalPhysicalMemoryAvailable() const { - const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + - page_table->GetNormalMemorySize() + GetSystemResourceSize() + image_size + +u64 KProcess::GetTotalPhysicalMemoryAvailable() { + const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemoryMax) + + page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size + main_thread_stack_size}; if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); capacity != pool_size) { @@ -135,16 +137,16 @@ u64 KProcess::GetTotalPhysicalMemoryAvailable() const { return memory_usage_capacity; } -u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const { +u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() { return GetTotalPhysicalMemoryAvailable() - GetSystemResourceSize(); } -u64 KProcess::GetTotalPhysicalMemoryUsed() const { - return image_size + main_thread_stack_size + page_table->GetNormalMemorySize() + +u64 KProcess::GetTotalPhysicalMemoryUsed() { + return image_size + main_thread_stack_size + page_table.GetNormalMemorySize() + GetSystemResourceSize(); } -u64 KProcess::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { +u64 KProcess::GetTotalPhysicalMemoryUsedWithoutSystemResource() { return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); } @@ -244,7 +246,7 @@ Result KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr ad shmem->Open(); shemen_info->Open(); - return ResultSuccess; + R_SUCCEED(); } void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address, @@ -283,18 +285,29 @@ void KProcess::UnregisterThread(KThread* thread) { thread_list.remove(thread); } +u64 KProcess::GetFreeThreadCount() const { + if (resource_limit != nullptr) { + const auto current_value = + resource_limit->GetCurrentValue(LimitableResource::ThreadCountMax); + const auto limit_value = resource_limit->GetLimitValue(LimitableResource::ThreadCountMax); + return limit_value - current_value; + } else { + return 0; + } +} + Result KProcess::Reset() { // Lock the process and the scheduler. KScopedLightLock lk(state_lock); KScopedSchedulerLock sl{kernel}; // Validate that we're in a state that we can reset. - R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState); + R_UNLESS(state != State::Terminated, ResultInvalidState); R_UNLESS(is_signaled, ResultInvalidState); // Clear signaled. is_signaled = false; - return ResultSuccess; + R_SUCCEED(); } Result KProcess::SetActivity(ProcessActivity activity) { @@ -304,15 +317,13 @@ Result KProcess::SetActivity(ProcessActivity activity) { KScopedSchedulerLock sl{kernel}; // Validate our state. - R_UNLESS(status != ProcessStatus::Exiting, ResultInvalidState); - R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState); + R_UNLESS(state != State::Terminating, ResultInvalidState); + R_UNLESS(state != State::Terminated, ResultInvalidState); // Either pause or resume. if (activity == ProcessActivity::Paused) { // Verify that we're not suspended. - if (is_suspended) { - return ResultInvalidState; - } + R_UNLESS(!is_suspended, ResultInvalidState); // Suspend all threads. for (auto* thread : GetThreadList()) { @@ -325,9 +336,7 @@ Result KProcess::SetActivity(ProcessActivity activity) { ASSERT(activity == ProcessActivity::Runnable); // Verify that we're suspended. - if (!is_suspended) { - return ResultInvalidState; - } + R_UNLESS(is_suspended, ResultInvalidState); // Resume all threads. for (auto* thread : GetThreadList()) { @@ -338,7 +347,7 @@ Result KProcess::SetActivity(ProcessActivity activity) { SetSuspended(false); } - return ResultSuccess; + R_SUCCEED(); } Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size) { @@ -348,35 +357,38 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: system_resource_size = metadata.GetSystemResourceSize(); image_size = code_size; - KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, - code_size + system_resource_size); + // We currently do not support process-specific system resource + UNIMPLEMENTED_IF(system_resource_size != 0); + + KScopedResourceReservation memory_reservation( + resource_limit, LimitableResource::PhysicalMemoryMax, code_size + system_resource_size); if (!memory_reservation.Succeeded()) { LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", code_size + system_resource_size); - return ResultLimitReached; + R_RETURN(ResultLimitReached); } // Initialize proces address space - if (const Result result{page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, - 0x8000000, code_size, - KMemoryManager::Pool::Application)}; + if (const Result result{page_table.InitializeForProcess( + metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application, + 0x8000000, code_size, &kernel.GetSystemSystemResource(), resource_limit)}; result.IsError()) { - return result; + R_RETURN(result); } // Map process code region - if (const Result result{page_table->MapProcessCode(page_table->GetCodeRegionStart(), - code_size / PageSize, KMemoryState::Code, - KMemoryPermission::None)}; + if (const Result result{page_table.MapProcessCode(page_table.GetCodeRegionStart(), + code_size / PageSize, KMemoryState::Code, + KMemoryPermission::None)}; result.IsError()) { - return result; + R_RETURN(result); } // Initialize process capabilities const auto& caps{metadata.GetKernelCapabilities()}; if (const Result result{ - capabilities.InitializeForUserProcess(caps.data(), caps.size(), *page_table)}; + capabilities.InitializeForUserProcess(caps.data(), caps.size(), page_table)}; result.IsError()) { - return result; + R_RETURN(result); } // Set memory usage capacity @@ -384,40 +396,41 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: case FileSys::ProgramAddressSpaceType::Is32Bit: case FileSys::ProgramAddressSpaceType::Is36Bit: case FileSys::ProgramAddressSpaceType::Is39Bit: - memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart(); + memory_usage_capacity = page_table.GetHeapRegionEnd() - page_table.GetHeapRegionStart(); break; case FileSys::ProgramAddressSpaceType::Is32BitNoMap: - memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart() + - page_table->GetAliasRegionEnd() - page_table->GetAliasRegionStart(); + memory_usage_capacity = page_table.GetHeapRegionEnd() - page_table.GetHeapRegionStart() + + page_table.GetAliasRegionEnd() - page_table.GetAliasRegionStart(); break; default: ASSERT(false); + break; } // Create TLS region - R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address))); + R_TRY(this->CreateThreadLocalRegion(std::addressof(plr_address))); memory_reservation.Commit(); - return handle_table.Initialize(capabilities.GetHandleTableSize()); + R_RETURN(handle_table.Initialize(capabilities.GetHandleTableSize())); } void KProcess::Run(s32 main_thread_priority, u64 stack_size) { AllocateMainThreadStack(stack_size); - resource_limit->Reserve(LimitableResource::Threads, 1); - resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); + resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); + resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size); const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; - ASSERT(!page_table->SetMaxHeapSize(heap_capacity).IsError()); + ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); - ChangeStatus(ProcessStatus::Running); + ChangeState(State::Running); SetupMainThread(kernel.System(), *this, main_thread_priority, main_thread_stack_top); } void KProcess::PrepareForTermination() { - ChangeStatus(ProcessStatus::Exiting); + ChangeState(State::Terminating); const auto stop_threads = [this](const std::vector& in_thread_list) { for (auto* thread : in_thread_list) { @@ -437,15 +450,15 @@ void KProcess::PrepareForTermination() { stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); - this->DeleteThreadLocalRegion(tls_region_address); - tls_region_address = 0; + this->DeleteThreadLocalRegion(plr_address); + plr_address = 0; if (resource_limit) { - resource_limit->Release(LimitableResource::PhysicalMemory, + resource_limit->Release(LimitableResource::PhysicalMemoryMax, main_thread_stack_size + image_size); } - ChangeStatus(ProcessStatus::Exited); + ChangeState(State::Terminated); } void KProcess::Finalize() { @@ -474,7 +487,7 @@ void KProcess::Finalize() { } // Finalize the page table. - page_table.reset(); + page_table.Finalize(); // Perform inherited finalization. KAutoObjectWithSlabHeapAndContainer::Finalize(); @@ -499,7 +512,7 @@ Result KProcess::CreateThreadLocalRegion(VAddr* out) { } *out = tlr; - return ResultSuccess; + R_SUCCEED(); } } @@ -528,7 +541,7 @@ Result KProcess::CreateThreadLocalRegion(VAddr* out) { // We succeeded! tlp_guard.Cancel(); *out = tlr; - return ResultSuccess; + R_SUCCEED(); } Result KProcess::DeleteThreadLocalRegion(VAddr addr) { @@ -576,7 +589,7 @@ Result KProcess::DeleteThreadLocalRegion(VAddr addr) { KThreadLocalPage::Free(kernel, page_to_free); } - return ResultSuccess; + R_SUCCEED(); } bool KProcess::InsertWatchpoint(Core::System& system, VAddr addr, u64 size, @@ -628,7 +641,7 @@ bool KProcess::RemoveWatchpoint(Core::System& system, VAddr addr, u64 size, void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { const auto ReprotectSegment = [&](const CodeSet::Segment& segment, Svc::MemoryPermission permission) { - page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); + page_table.SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); }; kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(), @@ -645,19 +658,18 @@ bool KProcess::IsSignaled() const { } KProcess::KProcess(KernelCore& kernel_) - : KAutoObjectWithSlabHeapAndContainer{kernel_}, page_table{std::make_unique( - kernel_.System())}, + : KAutoObjectWithSlabHeapAndContainer{kernel_}, page_table{kernel_.System()}, handle_table{kernel_}, address_arbiter{kernel_.System()}, condition_var{kernel_.System()}, state_lock{kernel_}, list_lock{kernel_} {} KProcess::~KProcess() = default; -void KProcess::ChangeStatus(ProcessStatus new_status) { - if (status == new_status) { +void KProcess::ChangeState(State new_state) { + if (state == new_state) { return; } - status = new_status; + state = new_state; is_signaled = true; NotifyAvailable(); } @@ -668,17 +680,17 @@ Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { // The kernel always ensures that the given stack size is page aligned. main_thread_stack_size = Common::AlignUp(stack_size, PageSize); - const VAddr start{page_table->GetStackRegionStart()}; - const std::size_t size{page_table->GetStackRegionEnd() - start}; + const VAddr start{page_table.GetStackRegionStart()}; + const std::size_t size{page_table.GetStackRegionEnd() - start}; CASCADE_RESULT(main_thread_stack_top, - page_table->AllocateAndMapMemory( + page_table.AllocateAndMapMemory( main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize, KMemoryState::Stack, KMemoryPermission::UserReadWrite)); main_thread_stack_top += main_thread_stack_size; - return ResultSuccess; + R_SUCCEED(); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index d56d73b..09bf2f1 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -13,6 +13,7 @@ #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/k_handle_table.h" +#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_thread_local_page.h" #include "core/hle/kernel/k_worker_task.h" @@ -31,7 +32,6 @@ class ProgramMetadata; namespace Kernel { class KernelCore; -class KPageTable; class KResourceLimit; class KThread; class KSharedMemoryInfo; @@ -45,24 +45,6 @@ enum class MemoryRegion : u16 { BASE = 3, }; -/** - * Indicates the status of a Process instance. - * - * @note These match the values as used by kernel, - * so new entries should only be added if RE - * shows that a new value has been introduced. - */ -enum class ProcessStatus { - Created, - CreatedWithDebuggerAttached, - Running, - WaitingForDebuggerToAttach, - DebuggerAttached, - Exiting, - Exited, - DebugBreak, -}; - enum class ProcessActivity : u32 { Runnable, Paused, @@ -89,6 +71,17 @@ public: explicit KProcess(KernelCore& kernel_); ~KProcess() override; + enum class State { + Created = static_cast(Svc::ProcessState::Created), + CreatedAttached = static_cast(Svc::ProcessState::CreatedAttached), + Running = static_cast(Svc::ProcessState::Running), + Crashed = static_cast(Svc::ProcessState::Crashed), + RunningAttached = static_cast(Svc::ProcessState::RunningAttached), + Terminating = static_cast(Svc::ProcessState::Terminating), + Terminated = static_cast(Svc::ProcessState::Terminated), + DebugBreak = static_cast(Svc::ProcessState::DebugBreak), + }; + enum : u64 { /// Lowest allowed process ID for a kernel initial process. InitialKIPIDMin = 1, @@ -114,12 +107,12 @@ public: /// Gets a reference to the process' page table. KPageTable& PageTable() { - return *page_table; + return page_table; } /// Gets const a reference to the process' page table. const KPageTable& PageTable() const { - return *page_table; + return page_table; } /// Gets a reference to the process' handle table. @@ -145,26 +138,25 @@ public: } Result WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) { - return condition_var.Wait(address, cv_key, tag, ns); + R_RETURN(condition_var.Wait(address, cv_key, tag, ns)); } Result SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, s32 count) { - return address_arbiter.SignalToAddress(address, signal_type, value, count); + R_RETURN(address_arbiter.SignalToAddress(address, signal_type, value, count)); } Result WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value, s64 timeout) { - return address_arbiter.WaitForAddress(address, arb_type, value, timeout); + R_RETURN(address_arbiter.WaitForAddress(address, arb_type, value, timeout)); } - /// Gets the address to the process' dedicated TLS region. - VAddr GetTLSRegionAddress() const { - return tls_region_address; + VAddr GetProcessLocalRegionAddress() const { + return plr_address; } /// Gets the current status of the process - ProcessStatus GetStatus() const { - return status; + State GetState() const { + return state; } /// Gets the unique ID that identifies this particular process. @@ -286,18 +278,18 @@ public: } /// Retrieves the total physical memory available to this process in bytes. - u64 GetTotalPhysicalMemoryAvailable() const; + u64 GetTotalPhysicalMemoryAvailable(); /// Retrieves the total physical memory available to this process in bytes, /// without the size of the personal system resource heap added to it. - u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource() const; + u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource(); /// Retrieves the total physical memory used by this process in bytes. - u64 GetTotalPhysicalMemoryUsed() const; + u64 GetTotalPhysicalMemoryUsed(); /// Retrieves the total physical memory used by this process in bytes, /// without the size of the personal system resource heap added to it. - u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const; + u64 GetTotalPhysicalMemoryUsedWithoutSystemResource(); /// Gets the list of all threads created with this process as their owner. std::list& GetThreadList() { @@ -312,6 +304,9 @@ public: /// from this process' thread list. void UnregisterThread(KThread* thread); + /// Retrieves the number of available threads for this process. + u64 GetFreeThreadCount() const; + /// Clears the signaled state of the process if and only if it's signaled. /// /// @pre The process must not be already terminated. If this is called on a @@ -415,19 +410,24 @@ private: pinned_threads[core_id] = nullptr; } - /// Changes the process status. If the status is different - /// from the current process status, then this will trigger - /// a process signal. - void ChangeStatus(ProcessStatus new_status); + void FinalizeHandleTable() { + // Finalize the table. + handle_table.Finalize(); + + // Note that the table is finalized. + is_handle_table_initialized = false; + } + + void ChangeState(State new_state); /// Allocates the main thread stack for the process, given the stack size in bytes. Result AllocateMainThreadStack(std::size_t stack_size); /// Memory manager for this process - std::unique_ptr page_table; + KPageTable page_table; /// Current status of the process - ProcessStatus status{}; + State state{}; /// The ID of this process u64 process_id = 0; @@ -443,6 +443,8 @@ private: /// Resource limit descriptor for this process KResourceLimit* resource_limit{}; + VAddr system_resource_address{}; + /// The ideal CPU core for this process, threads are scheduled on this core by default. u8 ideal_core = 0; @@ -469,7 +471,7 @@ private: KConditionVariable condition_var; /// Address indicating the location of the process' dedicated TLS region. - VAddr tls_region_address = 0; + VAddr plr_address = 0; /// Random values for svcGetInfo RandomEntropy std::array random_entropy{}; @@ -495,8 +497,12 @@ private: /// Schedule count of this process s64 schedule_count{}; + size_t memory_release_hint{}; + bool is_signaled{}; bool is_suspended{}; + bool is_immortal{}; + bool is_handle_table_initialized{}; bool is_initialized{}; std::atomic num_running_threads{}; diff --git a/src/core/hle/kernel/k_readable_event.cpp b/src/core/hle/kernel/k_readable_event.cpp index 94c5464..5c942d4 100644 --- a/src/core/hle/kernel/k_readable_event.cpp +++ b/src/core/hle/kernel/k_readable_event.cpp @@ -15,31 +15,44 @@ KReadableEvent::KReadableEvent(KernelCore& kernel_) : KSynchronizationObject{ker KReadableEvent::~KReadableEvent() = default; -bool KReadableEvent::IsSignaled() const { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); +void KReadableEvent::Initialize(KEvent* parent) { + m_is_signaled = false; + m_parent = parent; - return is_signaled; + if (m_parent != nullptr) { + m_parent->Open(); + } +} + +bool KReadableEvent::IsSignaled() const { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); + + return m_is_signaled; } void KReadableEvent::Destroy() { - if (parent) { - parent->Close(); + if (m_parent) { + { + KScopedSchedulerLock sl{kernel}; + m_parent->OnReadableEventDestroyed(); + } + m_parent->Close(); } } Result KReadableEvent::Signal() { KScopedSchedulerLock lk{kernel}; - if (!is_signaled) { - is_signaled = true; - NotifyAvailable(); + if (!m_is_signaled) { + m_is_signaled = true; + this->NotifyAvailable(); } return ResultSuccess; } Result KReadableEvent::Clear() { - Reset(); + this->Reset(); return ResultSuccess; } @@ -47,11 +60,11 @@ Result KReadableEvent::Clear() { Result KReadableEvent::Reset() { KScopedSchedulerLock lk{kernel}; - if (!is_signaled) { + if (!m_is_signaled) { return ResultInvalidState; } - is_signaled = false; + m_is_signaled = false; return ResultSuccess; } diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h index 18dcad2..743f96b 100644 --- a/src/core/hle/kernel/k_readable_event.h +++ b/src/core/hle/kernel/k_readable_event.h @@ -20,26 +20,23 @@ public: explicit KReadableEvent(KernelCore& kernel_); ~KReadableEvent() override; - void Initialize(KEvent* parent_event_, std::string&& name_) { - is_signaled = false; - parent = parent_event_; - name = std::move(name_); - } + void Initialize(KEvent* parent); KEvent* GetParent() const { - return parent; + return m_parent; } + Result Signal(); + Result Clear(); + bool IsSignaled() const override; void Destroy() override; - Result Signal(); - Result Clear(); Result Reset(); private: - bool is_signaled{}; - KEvent* parent{}; + bool m_is_signaled{}; + KEvent* m_parent{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp index 010dcf9..b9d22b4 100644 --- a/src/core/hle/kernel/k_resource_limit.cpp +++ b/src/core/hle/kernel/k_resource_limit.cpp @@ -159,12 +159,13 @@ KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical // TODO(bunnei): These values are the system defaults, the limits for service processes are // lower. These should use the correct limit values. - ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size) + ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, physical_memory_size) .IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900).IsSuccess()); + ASSERT( + resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133).IsSuccess()); return resource_limit; } diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h index 65c98c9..2573d1b 100644 --- a/src/core/hle/kernel/k_resource_limit.h +++ b/src/core/hle/kernel/k_resource_limit.h @@ -16,15 +16,8 @@ class CoreTiming; namespace Kernel { class KernelCore; -enum class LimitableResource : u32 { - PhysicalMemory = 0, - Threads = 1, - Events = 2, - TransferMemory = 3, - Sessions = 4, - Count, -}; +using LimitableResource = Svc::LimitableResource; constexpr bool IsValidResourceType(LimitableResource type) { return type < LimitableResource::Count; diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index c34ce7a..d667690 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -81,8 +81,8 @@ void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) { // HACK: we cannot schedule from this thread, it is not a core thread ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); - // Special case to ensure dummy threads that are waiting block - GetCurrentThread(kernel).IfDummyThreadTryWait(); + // Ensure dummy threads that are waiting block. + GetCurrentThread(kernel).DummyThreadBeginWait(); ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting); GetCurrentThread(kernel).EnableDispatch(); @@ -314,6 +314,16 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { idle_cores &= ~(1ULL << core_id); } + // HACK: any waiting dummy threads can wake up now. + kernel.GlobalSchedulerContext().WakeupWaitingDummyThreads(); + + // HACK: if we are a dummy thread, and we need to go sleep, indicate + // that for when the lock is released. + KThread* const cur_thread = GetCurrentThreadPointer(kernel); + if (cur_thread->IsDummyThread() && cur_thread->GetState() != ThreadState::Runnable) { + cur_thread->RequestDummyThreadWait(); + } + return cores_needing_scheduling; } @@ -374,7 +384,8 @@ void KScheduler::SwitchThread(KThread* next_thread) { void KScheduler::ScheduleImpl() { // First, clear the needs scheduling bool. - m_state.needs_scheduling.store(false, std::memory_order_seq_cst); + m_state.needs_scheduling.store(false, std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_seq_cst); // Load the appropriate thread pointers for scheduling. KThread* const cur_thread{GetCurrentThreadPointer(kernel)}; @@ -390,7 +401,8 @@ void KScheduler::ScheduleImpl() { // If there aren't, we want to check if the highest priority thread is the same as the current // thread. if (highest_priority_thread == cur_thread) { - // If they're the same, then we can just return. + // If they're the same, then we can just issue a memory barrier and return. + std::atomic_thread_fence(std::memory_order_seq_cst); return; } @@ -466,7 +478,8 @@ void KScheduler::ScheduleImplFiber() { // We failed to successfully do the context switch, and need to retry. // Clear needs_scheduling. - m_state.needs_scheduling.store(false, std::memory_order_seq_cst); + m_state.needs_scheduling.store(false, std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_seq_cst); // Refresh the highest priority thread. highest_priority_thread = m_state.highest_priority_thread; @@ -531,11 +544,23 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa GetPriorityQueue(kernel).Remove(thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(kernel); + + if (thread->IsDummyThread()) { + // HACK: if this is a dummy thread, it should no longer wake up when the + // scheduler lock is released. + kernel.GlobalSchedulerContext().UnregisterDummyThreadForWakeup(thread); + } } else if (cur_state == ThreadState::Runnable) { // If we're now runnable, then we weren't previously, and we should add. GetPriorityQueue(kernel).PushBack(thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(kernel); + + if (thread->IsDummyThread()) { + // HACK: if this is a dummy thread, it should wake up when the scheduler + // lock is released. + kernel.GlobalSchedulerContext().RegisterDummyThreadForWakeup(thread); + } } } diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 73314b4..129d604 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h @@ -60,6 +60,9 @@ public: // Release an instance of the lock. if ((--lock_count) == 0) { + // Perform a memory barrier here. + std::atomic_thread_fence(std::memory_order_seq_cst); + // We're no longer going to hold the lock. Take note of what cores need scheduling. const u64 cores_needing_scheduling = SchedulerType::UpdateHighestPriorityThreads(kernel); diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index e968f26..16968ba 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp @@ -61,12 +61,6 @@ void KServerPort::Destroy() { // Close our reference to our parent. parent->Close(); - - // Release host emulation members. - session_handler.reset(); - - // Ensure that the global list tracking server objects does not hold on to a reference. - kernel.UnregisterServerObject(this); } bool KServerPort::IsSignaled() const { diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h index fd4f4bd..5fc7ee6 100644 --- a/src/core/hle/kernel/k_server_port.h +++ b/src/core/hle/kernel/k_server_port.h @@ -27,24 +27,6 @@ public: void Initialize(KPort* parent_port_, std::string&& name_); - /// Whether or not this server port has an HLE handler available. - bool HasSessionRequestHandler() const { - return !session_handler.expired(); - } - - /// Gets the HLE handler for this port. - SessionRequestHandlerWeakPtr GetSessionRequestHandler() const { - return session_handler; - } - - /** - * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port - * will inherit a reference to this handler. - */ - void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) { - session_handler = std::move(handler); - } - void EnqueueSession(KServerSession* pending_session); KServerSession* AcceptSession(); @@ -65,7 +47,6 @@ private: void CleanupSessions(); SessionList session_list; - SessionRequestHandlerWeakPtr session_handler; KPort* parent{}; }; diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 802c646..aa1941f 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -7,6 +7,8 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/scope_exit.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" @@ -18,49 +20,128 @@ #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/service_thread.h" #include "core/memory.h" namespace Kernel { -KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} +using ThreadQueueImplForKServerSessionRequest = KThreadQueue; + +KServerSession::KServerSession(KernelCore& kernel_) + : KSynchronizationObject{kernel_}, m_lock{kernel_} {} KServerSession::~KServerSession() = default; -void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, - std::shared_ptr manager_) { +void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) { // Set member variables. parent = parent_session_; name = std::move(name_); - - if (manager_) { - manager = manager_; - } else { - manager = std::make_shared(kernel); - } } void KServerSession::Destroy() { parent->OnServerClosed(); + this->CleanupRequests(); + parent->Close(); - - // Release host emulation members. - manager.reset(); - - // Ensure that the global list tracking server objects does not hold on to a reference. - kernel.UnregisterServerObject(this); } void KServerSession::OnClientClosed() { - if (manager->HasSessionHandler()) { - manager->SessionHandler().ClientDisconnected(this); + KScopedLightLock lk{m_lock}; + + // Handle any pending requests. + KSessionRequest* prev_request = nullptr; + while (true) { + // Declare variables for processing the request. + KSessionRequest* request = nullptr; + KEvent* event = nullptr; + KThread* thread = nullptr; + bool cur_request = false; + bool terminate = false; + + // Get the next request. + { + KScopedSchedulerLock sl{kernel}; + + if (m_current_request != nullptr && m_current_request != prev_request) { + // Set the request, open a reference as we process it. + request = m_current_request; + request->Open(); + cur_request = true; + + // Get thread and event for the request. + thread = request->GetThread(); + event = request->GetEvent(); + + // If the thread is terminating, handle that. + if (thread->IsTerminationRequested()) { + request->ClearThread(); + request->ClearEvent(); + terminate = true; + } + + prev_request = request; + } else if (!m_request_list.empty()) { + // Pop the request from the front of the list. + request = std::addressof(m_request_list.front()); + m_request_list.pop_front(); + + // Get thread and event for the request. + thread = request->GetThread(); + event = request->GetEvent(); + } + } + + // If there are no requests, we're done. + if (request == nullptr) { + break; + } + + // All requests must have threads. + ASSERT(thread != nullptr); + + // Ensure that we close the request when done. + SCOPE_EXIT({ request->Close(); }); + + // If we're terminating, close a reference to the thread and event. + if (terminate) { + thread->Close(); + if (event != nullptr) { + event->Close(); + } + } + + // If we need to, reply. + if (event != nullptr && !cur_request) { + // There must be no mappings. + ASSERT(request->GetSendCount() == 0); + ASSERT(request->GetReceiveCount() == 0); + ASSERT(request->GetExchangeCount() == 0); + + // // Get the process and page table. + // KProcess *client_process = thread->GetOwnerProcess(); + // auto &client_pt = client_process->GetPageTable(); + + // // Reply to the request. + // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(), + // ResultSessionClosed); + + // // Unlock the buffer. + // // NOTE: Nintendo does not check the result of this. + // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize()); + + // Signal the event. + event->Signal(); + } } + + // Notify. + this->NotifyAvailable(ResultSessionClosed); } bool KServerSession::IsSignaled() const { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); // If the client is closed, we're always signaled. if (parent->IsClientClosed()) { @@ -68,114 +149,271 @@ bool KServerSession::IsSignaled() const { } // Otherwise, we're signaled if we have a request and aren't handling one. - return false; + return !m_request_list.empty() && m_current_request == nullptr; } -void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) { - manager->AppendDomainHandler(std::move(handler)); -} +Result KServerSession::OnRequest(KSessionRequest* request) { + // Create the wait queue. + ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; -std::size_t KServerSession::NumDomainRequestHandlers() const { - return manager->DomainHandlerCount(); -} + { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; -Result KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { - if (!context.HasDomainMessageHeader()) { - return ResultSuccess; - } + // Ensure that we can handle new requests. + R_UNLESS(!parent->IsServerClosed(), ResultSessionClosed); - // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs - context.SetSessionRequestManager(manager); + // Check that we're not terminating. + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); - // If there is a DomainMessageHeader, then this is CommandType "Request" - const auto& domain_message_header = context.GetDomainMessageHeader(); - const u32 object_id{domain_message_header.object_id}; - switch (domain_message_header.command) { - case IPC::DomainMessageHeader::CommandType::SendMessage: - if (object_id > manager->DomainHandlerCount()) { - LOG_CRITICAL(IPC, - "object_id {} is too big! This probably means a recent service call " - "to {} needed to return a new interface!", - object_id, name); - ASSERT(false); - return ResultSuccess; // Ignore error if asserts are off - } - if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) { - return strong_ptr->HandleSyncRequest(*this, context); - } else { - ASSERT(false); - return ResultSuccess; + // Get whether we're empty. + const bool was_empty = m_request_list.empty(); + + // Add the request to the list. + request->Open(); + m_request_list.push_back(*request); + + // If we were empty, signal. + if (was_empty) { + this->NotifyAvailable(); } - case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { - LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); + // If we have a request event, this is asynchronous, and we don't need to wait. + R_SUCCEED_IF(request->GetEvent() != nullptr); - manager->CloseDomainHandler(object_id - 1); - - IPC::ResponseBuilder rb{context, 2}; - rb.Push(ResultSuccess); - return ResultSuccess; - } + // This is a synchronous request, so we should wait for our request to complete. + GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); + GetCurrentThread(kernel).BeginWait(&wait_queue); } - LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value()); - ASSERT(false); - return ResultSuccess; + return GetCurrentThread(kernel).GetWaitResult(); } -Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { - u32* cmd_buf{reinterpret_cast(memory.GetPointer(thread->GetTLSAddress()))}; - auto context = std::make_shared(kernel, memory, this, thread); +Result KServerSession::SendReply(bool is_hle) { + // Lock the session. + KScopedLightLock lk{m_lock}; - context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); + // Get the request. + KSessionRequest* request; + { + KScopedSchedulerLock sl{kernel}; - // Ensure we have a session request handler - if (manager->HasSessionRequestHandler(*context)) { - if (auto strong_ptr = manager->GetServiceThread().lock()) { - strong_ptr->QueueSyncRequest(*parent, std::move(context)); - } else { - ASSERT_MSG(false, "strong_ptr is nullptr!"); + // Get the current request. + request = m_current_request; + R_UNLESS(request != nullptr, ResultInvalidState); + + // Clear the current request, since we're processing it. + m_current_request = nullptr; + if (!m_request_list.empty()) { + this->NotifyAvailable(); } - } else { - ASSERT_MSG(false, "handler is invalid!"); } - return ResultSuccess; -} + // Close reference to the request once we're done processing it. + SCOPE_EXIT({ request->Close(); }); + + // Extract relevant information from the request. + const uintptr_t client_message = request->GetAddress(); + const size_t client_buffer_size = request->GetSize(); + KThread* client_thread = request->GetThread(); + KEvent* event = request->GetEvent(); + + // Check whether we're closed. + const bool closed = (client_thread == nullptr || parent->IsClientClosed()); -Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { Result result = ResultSuccess; + if (!closed) { + // If we're not closed, send the reply. + if (is_hle) { + // HLE servers write directly to a pointer to the thread command buffer. Therefore + // the reply has already been written in this case. + } else { + Core::Memory::Memory& memory{kernel.System().Memory()}; + KThread* server_thread{GetCurrentThreadPointer(kernel)}; + UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); - // If the session has been converted to a domain, handle the domain request - if (manager->HasSessionRequestHandler(context)) { - if (IsDomain() && context.HasDomainMessageHeader()) { - result = HandleDomainSyncRequest(context); - // If there is no domain header, the regular session handler is used - } else if (manager->HasSessionHandler()) { - // If this ServerSession has an associated HLE handler, forward the request to it. - result = manager->SessionHandler().HandleSyncRequest(*this, context); + auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); + auto* dst_msg_buffer = memory.GetPointer(client_message); + std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); } } else { - ASSERT_MSG(false, "Session handler is invalid, stubbing response!"); - IPC::ResponseBuilder rb(context, 2); - rb.Push(ResultSuccess); + result = ResultSessionClosed; } - if (convert_to_domain) { - ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance."); - manager->ConvertToDomain(); - convert_to_domain = false; + // Select a result for the client. + Result client_result = result; + if (closed && R_SUCCEEDED(result)) { + result = ResultSessionClosed; + client_result = ResultSessionClosed; + } else { + result = ResultSuccess; } - // The calling thread is waiting for this request to complete, so wake it up. - context.GetThread().EndWait(result); + // If there's a client thread, update it. + if (client_thread != nullptr) { + if (event != nullptr) { + // // Get the client process/page table. + // KProcess *client_process = client_thread->GetOwnerProcess(); + // KPageTable *client_page_table = &client_process->PageTable(); + + // // If we need to, reply with an async error. + // if (R_FAILED(client_result)) { + // ReplyAsyncError(client_process, client_message, client_buffer_size, + // client_result); + // } + + // // Unlock the client buffer. + // // NOTE: Nintendo does not check the result of this. + // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); + + // Signal the event. + event->Signal(); + } else { + // End the client thread's wait. + KScopedSchedulerLock sl{kernel}; + + if (!client_thread->IsTerminationRequested()) { + client_thread->EndWait(client_result); + } + } + } return result; } -Result KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing) { - return QueueSyncRequest(thread, memory); +Result KServerSession::ReceiveRequest(std::shared_ptr* out_context, + std::weak_ptr manager) { + // Lock the session. + KScopedLightLock lk{m_lock}; + + // Get the request and client thread. + KSessionRequest* request; + KThread* client_thread; + + { + KScopedSchedulerLock sl{kernel}; + + // Ensure that we can service the request. + R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed); + + // Ensure we aren't already servicing a request. + R_UNLESS(m_current_request == nullptr, ResultNotFound); + + // Ensure we have a request to service. + R_UNLESS(!m_request_list.empty(), ResultNotFound); + + // Pop the first request from the list. + request = &m_request_list.front(); + m_request_list.pop_front(); + + // Get the thread for the request. + client_thread = request->GetThread(); + R_UNLESS(client_thread != nullptr, ResultSessionClosed); + + // Open the client thread. + client_thread->Open(); + } + + SCOPE_EXIT({ client_thread->Close(); }); + + // Set the request as our current. + m_current_request = request; + + // Get the client address. + uintptr_t client_message = request->GetAddress(); + size_t client_buffer_size = request->GetSize(); + // bool recv_list_broken = false; + + // Receive the message. + Core::Memory::Memory& memory{kernel.System().Memory()}; + if (out_context != nullptr) { + // HLE request. + u32* cmd_buf{reinterpret_cast(memory.GetPointer(client_message))}; + *out_context = std::make_shared(kernel, memory, this, client_thread); + (*out_context)->SetSessionRequestManager(manager); + (*out_context) + ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(), + cmd_buf); + } else { + KThread* server_thread{GetCurrentThreadPointer(kernel)}; + UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); + + auto* src_msg_buffer = memory.GetPointer(client_message); + auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); + std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + } + + // We succeeded. + return ResultSuccess; +} + +void KServerSession::CleanupRequests() { + KScopedLightLock lk(m_lock); + + // Clean up any pending requests. + while (true) { + // Get the next request. + KSessionRequest* request = nullptr; + { + KScopedSchedulerLock sl{kernel}; + + if (m_current_request) { + // Choose the current request if we have one. + request = m_current_request; + m_current_request = nullptr; + } else if (!m_request_list.empty()) { + // Pop the request from the front of the list. + request = &m_request_list.front(); + m_request_list.pop_front(); + } + } + + // If there's no request, we're done. + if (request == nullptr) { + break; + } + + // Close a reference to the request once it's cleaned up. + SCOPE_EXIT({ request->Close(); }); + + // Extract relevant information from the request. + // const uintptr_t client_message = request->GetAddress(); + // const size_t client_buffer_size = request->GetSize(); + KThread* client_thread = request->GetThread(); + KEvent* event = request->GetEvent(); + + // KProcess *server_process = request->GetServerProcess(); + // KProcess *client_process = (client_thread != nullptr) ? + // client_thread->GetOwnerProcess() : nullptr; + // KProcessPageTable *client_page_table = (client_process != nullptr) ? + // &client_process->GetPageTable() : nullptr; + + // Cleanup the mappings. + // Result result = CleanupMap(request, server_process, client_page_table); + + // If there's a client thread, update it. + if (client_thread != nullptr) { + if (event != nullptr) { + // // We need to reply async. + // ReplyAsyncError(client_process, client_message, client_buffer_size, + // (R_SUCCEEDED(result) ? ResultSessionClosed : result)); + + // // Unlock the client buffer. + // NOTE: Nintendo does not check the result of this. + // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); + + // Signal the event. + event->Signal(); + } else { + // End the client thread's wait. + KScopedSchedulerLock sl{kernel}; + + if (!client_thread->IsTerminationRequested()) { + client_thread->EndWait(ResultSessionClosed); + } + } + } + } } } // namespace Kernel diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 6d08219..6e189af 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -1,8 +1,9 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include #include #include #include @@ -10,24 +11,16 @@ #include #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_light_lock.h" +#include "core/hle/kernel/k_session_request.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/result.h" -namespace Core::Memory { -class Memory; -} - -namespace Core::Timing { -class CoreTiming; -struct EventType; -} // namespace Core::Timing - namespace Kernel { class HLERequestContext; class KernelCore; class KSession; -class SessionRequestHandler; class SessionRequestManager; class KThread; @@ -43,8 +36,7 @@ public: void Destroy() override; - void Initialize(KSession* parent_session_, std::string&& name_, - std::shared_ptr manager_); + void Initialize(KSession* parent_session_, std::string&& name_); KSession* GetParent() { return parent; @@ -55,71 +47,30 @@ public: } bool IsSignaled() const override; - void OnClientClosed(); - void ClientConnected(SessionRequestHandlerPtr handler) { - manager->SetSessionHandler(std::move(handler)); - } + /// TODO: flesh these out to match the real kernel + Result OnRequest(KSessionRequest* request); + Result SendReply(bool is_hle = false); + Result ReceiveRequest(std::shared_ptr* out_context = nullptr, + std::weak_ptr manager = {}); - void ClientDisconnected() { - manager = nullptr; - } - - /** - * Handle a sync request from the emulated application. - * - * @param thread Thread that initiated the request. - * @param memory Memory context to handle the sync request under. - * @param core_timing Core timing context to schedule the request event under. - * - * @returns Result from the operation. - */ - Result HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing); - - /// Adds a new domain request handler to the collection of request handlers within - /// this ServerSession instance. - void AppendDomainHandler(SessionRequestHandlerPtr handler); - - /// Retrieves the total number of domain request handlers that have been - /// appended to this ServerSession instance. - std::size_t NumDomainRequestHandlers() const; - - /// Returns true if the session has been converted to a domain, otherwise False - bool IsDomain() const { - return manager->IsDomain(); - } - - /// Converts the session to a domain at the end of the current command - void ConvertToDomain() { - convert_to_domain = true; - } - - /// Gets the session request manager, which forwards requests to the underlying service - std::shared_ptr& GetSessionRequestManager() { - return manager; + Result SendReplyHLE() { + return SendReply(true); } private: - /// Queues a sync request from the emulated application. - Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); - - /// Completes a sync request from the emulated application. - Result CompleteSyncRequest(HLERequestContext& context); - - /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an - /// object handle. - Result HandleDomainSyncRequest(Kernel::HLERequestContext& context); - - /// This session's HLE request handlers - std::shared_ptr manager; - - /// When set to True, converts the session to a domain at the end of the command - bool convert_to_domain{}; + /// Frees up waiting client sessions when this server session is about to die + void CleanupRequests(); /// KSession that owns this KServerSession KSession* parent{}; + + /// List of threads which are pending a reply. + boost::intrusive::list m_request_list; + KSessionRequest* m_current_request{}; + + KLightLock m_lock; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index ee05aa2..b6f6fe9 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp @@ -13,8 +13,7 @@ KSession::KSession(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {} KSession::~KSession() = default; -void KSession::Initialize(KClientPort* port_, const std::string& name_, - std::shared_ptr manager_) { +void KSession::Initialize(KClientPort* port_, const std::string& name_) { // Increment reference count. // Because reference count is one on creation, this will result // in a reference count of two. Thus, when both server and client are closed @@ -26,7 +25,7 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_, KAutoObject::Create(std::addressof(client)); // Initialize our sub sessions. - server.Initialize(this, name_ + ":Server", manager_); + server.Initialize(this, name_ + ":Server"); client.Initialize(this, name_ + ":Client"); // Set state and name. @@ -77,7 +76,7 @@ void KSession::OnClientClosed() { void KSession::PostDestroy(uintptr_t arg) { // Release the session count resource the owner process holds. KProcess* owner = reinterpret_cast(arg); - owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1); + owner->GetResourceLimit()->Release(LimitableResource::SessionCountMax, 1); owner->Close(); } diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h index c6ead40..93e5e6f 100644 --- a/src/core/hle/kernel/k_session.h +++ b/src/core/hle/kernel/k_session.h @@ -21,8 +21,7 @@ public: explicit KSession(KernelCore& kernel_); ~KSession() override; - void Initialize(KClientPort* port_, const std::string& name_, - std::shared_ptr manager_ = nullptr); + void Initialize(KClientPort* port_, const std::string& name_); void Finalize() override; diff --git a/src/core/hle/kernel/k_session_request.cpp b/src/core/hle/kernel/k_session_request.cpp new file mode 100644 index 0000000..520da6a --- /dev/null +++ b/src/core/hle/kernel/k_session_request.cpp @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/k_page_buffer.h" +#include "core/hle/kernel/k_session_request.h" + +namespace Kernel { + +Result KSessionRequest::SessionMappings::PushMap(VAddr client, VAddr server, size_t size, + KMemoryState state, size_t index) { + // At most 15 buffers of each type (4-bit descriptor counts). + ASSERT(index < ((1ul << 4) - 1) * 3); + + // Get the mapping. + Mapping* mapping; + if (index < NumStaticMappings) { + mapping = &m_static_mappings[index]; + } else { + // Allocate a page for the extra mappings. + if (m_mappings == nullptr) { + KPageBuffer* page_buffer = KPageBuffer::Allocate(kernel); + R_UNLESS(page_buffer != nullptr, ResultOutOfMemory); + + m_mappings = reinterpret_cast(page_buffer); + } + + mapping = &m_mappings[index - NumStaticMappings]; + } + + // Set the mapping. + mapping->Set(client, server, size, state); + + return ResultSuccess; +} + +Result KSessionRequest::SessionMappings::PushSend(VAddr client, VAddr server, size_t size, + KMemoryState state) { + ASSERT(m_num_recv == 0); + ASSERT(m_num_exch == 0); + return this->PushMap(client, server, size, state, m_num_send++); +} + +Result KSessionRequest::SessionMappings::PushReceive(VAddr client, VAddr server, size_t size, + KMemoryState state) { + ASSERT(m_num_exch == 0); + return this->PushMap(client, server, size, state, m_num_send + m_num_recv++); +} + +Result KSessionRequest::SessionMappings::PushExchange(VAddr client, VAddr server, size_t size, + KMemoryState state) { + return this->PushMap(client, server, size, state, m_num_send + m_num_recv + m_num_exch++); +} + +void KSessionRequest::SessionMappings::Finalize() { + if (m_mappings) { + KPageBuffer::Free(kernel, reinterpret_cast(m_mappings)); + m_mappings = nullptr; + } +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_session_request.h b/src/core/hle/kernel/k_session_request.h new file mode 100644 index 0000000..e5558bc --- /dev/null +++ b/src/core/hle/kernel/k_session_request.h @@ -0,0 +1,306 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/hle/kernel/k_auto_object.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_memory_block.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/slab_helpers.h" + +namespace Kernel { + +class KSessionRequest final : public KSlabAllocated, + public KAutoObject, + public boost::intrusive::list_base_hook<> { + KERNEL_AUTOOBJECT_TRAITS(KSessionRequest, KAutoObject); + +public: + class SessionMappings { + private: + static constexpr size_t NumStaticMappings = 8; + + class Mapping { + public: + constexpr void Set(VAddr c, VAddr s, size_t sz, KMemoryState st) { + m_client_address = c; + m_server_address = s; + m_size = sz; + m_state = st; + } + + constexpr VAddr GetClientAddress() const { + return m_client_address; + } + constexpr VAddr GetServerAddress() const { + return m_server_address; + } + constexpr size_t GetSize() const { + return m_size; + } + constexpr KMemoryState GetMemoryState() const { + return m_state; + } + + private: + VAddr m_client_address; + VAddr m_server_address; + size_t m_size; + KMemoryState m_state; + }; + + public: + explicit SessionMappings(KernelCore& kernel_) : kernel(kernel_) {} + + void Initialize() {} + void Finalize(); + + size_t GetSendCount() const { + return m_num_send; + } + size_t GetReceiveCount() const { + return m_num_recv; + } + size_t GetExchangeCount() const { + return m_num_exch; + } + + Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state); + Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state); + Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state); + + VAddr GetSendClientAddress(size_t i) const { + return GetSendMapping(i).GetClientAddress(); + } + VAddr GetSendServerAddress(size_t i) const { + return GetSendMapping(i).GetServerAddress(); + } + size_t GetSendSize(size_t i) const { + return GetSendMapping(i).GetSize(); + } + KMemoryState GetSendMemoryState(size_t i) const { + return GetSendMapping(i).GetMemoryState(); + } + + VAddr GetReceiveClientAddress(size_t i) const { + return GetReceiveMapping(i).GetClientAddress(); + } + VAddr GetReceiveServerAddress(size_t i) const { + return GetReceiveMapping(i).GetServerAddress(); + } + size_t GetReceiveSize(size_t i) const { + return GetReceiveMapping(i).GetSize(); + } + KMemoryState GetReceiveMemoryState(size_t i) const { + return GetReceiveMapping(i).GetMemoryState(); + } + + VAddr GetExchangeClientAddress(size_t i) const { + return GetExchangeMapping(i).GetClientAddress(); + } + VAddr GetExchangeServerAddress(size_t i) const { + return GetExchangeMapping(i).GetServerAddress(); + } + size_t GetExchangeSize(size_t i) const { + return GetExchangeMapping(i).GetSize(); + } + KMemoryState GetExchangeMemoryState(size_t i) const { + return GetExchangeMapping(i).GetMemoryState(); + } + + private: + Result PushMap(VAddr client, VAddr server, size_t size, KMemoryState state, size_t index); + + const Mapping& GetSendMapping(size_t i) const { + ASSERT(i < m_num_send); + + const size_t index = i; + if (index < NumStaticMappings) { + return m_static_mappings[index]; + } else { + return m_mappings[index - NumStaticMappings]; + } + } + + const Mapping& GetReceiveMapping(size_t i) const { + ASSERT(i < m_num_recv); + + const size_t index = m_num_send + i; + if (index < NumStaticMappings) { + return m_static_mappings[index]; + } else { + return m_mappings[index - NumStaticMappings]; + } + } + + const Mapping& GetExchangeMapping(size_t i) const { + ASSERT(i < m_num_exch); + + const size_t index = m_num_send + m_num_recv + i; + if (index < NumStaticMappings) { + return m_static_mappings[index]; + } else { + return m_mappings[index - NumStaticMappings]; + } + } + + private: + KernelCore& kernel; + std::array m_static_mappings; + Mapping* m_mappings{}; + u8 m_num_send{}; + u8 m_num_recv{}; + u8 m_num_exch{}; + }; + +public: + explicit KSessionRequest(KernelCore& kernel_) : KAutoObject(kernel_), m_mappings(kernel_) {} + + static KSessionRequest* Create(KernelCore& kernel) { + KSessionRequest* req = KSessionRequest::Allocate(kernel); + if (req != nullptr) [[likely]] { + KAutoObject::Create(req); + } + return req; + } + + void Destroy() override { + this->Finalize(); + KSessionRequest::Free(kernel, this); + } + + void Initialize(KEvent* event, uintptr_t address, size_t size) { + m_mappings.Initialize(); + + m_thread = GetCurrentThreadPointer(kernel); + m_event = event; + m_address = address; + m_size = size; + + m_thread->Open(); + if (m_event != nullptr) { + m_event->Open(); + } + } + + static void PostDestroy(uintptr_t arg) {} + + KThread* GetThread() const { + return m_thread; + } + KEvent* GetEvent() const { + return m_event; + } + uintptr_t GetAddress() const { + return m_address; + } + size_t GetSize() const { + return m_size; + } + KProcess* GetServerProcess() const { + return m_server; + } + + void SetServerProcess(KProcess* process) { + m_server = process; + m_server->Open(); + } + + void ClearThread() { + m_thread = nullptr; + } + void ClearEvent() { + m_event = nullptr; + } + + size_t GetSendCount() const { + return m_mappings.GetSendCount(); + } + size_t GetReceiveCount() const { + return m_mappings.GetReceiveCount(); + } + size_t GetExchangeCount() const { + return m_mappings.GetExchangeCount(); + } + + Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state) { + return m_mappings.PushSend(client, server, size, state); + } + + Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state) { + return m_mappings.PushReceive(client, server, size, state); + } + + Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state) { + return m_mappings.PushExchange(client, server, size, state); + } + + VAddr GetSendClientAddress(size_t i) const { + return m_mappings.GetSendClientAddress(i); + } + VAddr GetSendServerAddress(size_t i) const { + return m_mappings.GetSendServerAddress(i); + } + size_t GetSendSize(size_t i) const { + return m_mappings.GetSendSize(i); + } + KMemoryState GetSendMemoryState(size_t i) const { + return m_mappings.GetSendMemoryState(i); + } + + VAddr GetReceiveClientAddress(size_t i) const { + return m_mappings.GetReceiveClientAddress(i); + } + VAddr GetReceiveServerAddress(size_t i) const { + return m_mappings.GetReceiveServerAddress(i); + } + size_t GetReceiveSize(size_t i) const { + return m_mappings.GetReceiveSize(i); + } + KMemoryState GetReceiveMemoryState(size_t i) const { + return m_mappings.GetReceiveMemoryState(i); + } + + VAddr GetExchangeClientAddress(size_t i) const { + return m_mappings.GetExchangeClientAddress(i); + } + VAddr GetExchangeServerAddress(size_t i) const { + return m_mappings.GetExchangeServerAddress(i); + } + size_t GetExchangeSize(size_t i) const { + return m_mappings.GetExchangeSize(i); + } + KMemoryState GetExchangeMemoryState(size_t i) const { + return m_mappings.GetExchangeMemoryState(i); + } + +private: + // NOTE: This is public and virtual in Nintendo's kernel. + void Finalize() override { + m_mappings.Finalize(); + + if (m_thread) { + m_thread->Close(); + } + if (m_event) { + m_event->Close(); + } + if (m_server) { + m_server->Close(); + } + } + +private: + SessionMappings m_mappings; + KThread* m_thread{}; + KProcess* m_server{}; + KEvent* m_event{}; + uintptr_t m_address{}; + size_t m_size{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index 8ff1545..10cd4c4 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp @@ -14,7 +14,7 @@ namespace Kernel { KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} KSharedMemory::~KSharedMemory() { - kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size); + kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size); } Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, @@ -35,7 +35,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o KResourceLimit* reslimit = kernel.GetSystemResourceLimit(); // Reserve memory for ourselves. - KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory, + KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemoryMax, size_); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); @@ -50,14 +50,14 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o is_initialized = true; // Clear all pages in the memory. - std::memset(device_memory_.GetPointer(physical_address_), 0, size_); + std::memset(device_memory_.GetPointer(physical_address_), 0, size_); return ResultSuccess; } void KSharedMemory::Finalize() { // Release the memory reservation. - resource_limit->Release(LimitableResource::PhysicalMemory, size); + resource_limit->Release(LimitableResource::PhysicalMemoryMax, size); resource_limit->Close(); // Perform inherited finalization. diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h index 34cb984..a96c55a 100644 --- a/src/core/hle/kernel/k_shared_memory.h +++ b/src/core/hle/kernel/k_shared_memory.h @@ -54,7 +54,7 @@ public: * @return A pointer to the shared memory block from the specified offset */ u8* GetPointer(std::size_t offset = 0) { - return device_memory->GetPointer(physical_address + offset); + return device_memory->GetPointer(physical_address + offset); } /** @@ -63,7 +63,7 @@ public: * @return A pointer to the shared memory block from the specified offset */ const u8* GetPointer(std::size_t offset = 0) const { - return device_memory->GetPointer(physical_address + offset); + return device_memory->GetPointer(physical_address + offset); } void Finalize() override; @@ -74,7 +74,7 @@ public: static void PostDestroy([[maybe_unused]] uintptr_t arg) {} private: - Core::DeviceMemory* device_memory; + Core::DeviceMemory* device_memory{}; KProcess* owner_process{}; KPageGroup page_list; Svc::MemoryPermission owner_permission{}; diff --git a/src/core/hle/kernel/k_shared_memory_info.h b/src/core/hle/kernel/k_shared_memory_info.h index e43db85..2bb6b6d 100644 --- a/src/core/hle/kernel/k_shared_memory_info.h +++ b/src/core/hle/kernel/k_shared_memory_info.h @@ -15,7 +15,8 @@ class KSharedMemoryInfo final : public KSlabAllocated, public boost::intrusive::list_base_hook<> { public: - explicit KSharedMemoryInfo() = default; + explicit KSharedMemoryInfo(KernelCore&) {} + KSharedMemoryInfo() = default; constexpr void Initialize(KSharedMemory* shmem) { shared_memory = shmem; diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h index 2b30353..68469b0 100644 --- a/src/core/hle/kernel/k_slab_heap.h +++ b/src/core/hle/kernel/k_slab_heap.h @@ -6,8 +6,10 @@ #include #include "common/assert.h" +#include "common/atomic_ops.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/spin_lock.h" namespace Kernel { @@ -36,28 +38,34 @@ public: } void* Allocate() { - Node* ret = m_head.load(); + // KScopedInterruptDisable di; - do { - if (ret == nullptr) { - break; - } - } while (!m_head.compare_exchange_weak(ret, ret->next)); + m_lock.lock(); + Node* ret = m_head; + if (ret != nullptr) [[likely]] { + m_head = ret->next; + } + + m_lock.unlock(); return ret; } void Free(void* obj) { - Node* node = static_cast(obj); + // KScopedInterruptDisable di; - Node* cur_head = m_head.load(); - do { - node->next = cur_head; - } while (!m_head.compare_exchange_weak(cur_head, node)); + m_lock.lock(); + + Node* node = static_cast(obj); + node->next = m_head; + m_head = node; + + m_lock.unlock(); } private: std::atomic m_head{}; + Common::SpinLock m_lock; }; } // namespace impl @@ -75,16 +83,13 @@ private: private: void UpdatePeakImpl(uintptr_t obj) { - static_assert(std::atomic_ref::is_always_lock_free); - std::atomic_ref peak_ref(m_peak); - const uintptr_t alloc_peak = obj + this->GetObjectSize(); uintptr_t cur_peak = m_peak; do { if (alloc_peak <= cur_peak) { break; } - } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak)); + } while (!Common::AtomicCompareAndSwap(&m_peak, alloc_peak, cur_peak, cur_peak)); } public: diff --git a/src/core/hle/kernel/k_system_resource.cpp b/src/core/hle/kernel/k_system_resource.cpp new file mode 100644 index 0000000..4cc377a --- /dev/null +++ b/src/core/hle/kernel/k_system_resource.cpp @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/k_system_resource.h" + +namespace Kernel { + +Result KSecureSystemResource::Initialize([[maybe_unused]] size_t size, + [[maybe_unused]] KResourceLimit* resource_limit, + [[maybe_unused]] KMemoryManager::Pool pool) { + // Unimplemented + UNREACHABLE(); +} + +void KSecureSystemResource::Finalize() { + // Unimplemented + UNREACHABLE(); +} + +size_t KSecureSystemResource::CalculateRequiredSecureMemorySize( + [[maybe_unused]] size_t size, [[maybe_unused]] KMemoryManager::Pool pool) { + // Unimplemented + UNREACHABLE(); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_system_resource.h b/src/core/hle/kernel/k_system_resource.h new file mode 100644 index 0000000..9a991f7 --- /dev/null +++ b/src/core/hle/kernel/k_system_resource.h @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/assert.h" +#include "common/common_types.h" +#include "core/hle/kernel/k_auto_object.h" +#include "core/hle/kernel/k_dynamic_resource_manager.h" +#include "core/hle/kernel/k_memory_manager.h" +#include "core/hle/kernel/k_page_table_manager.h" +#include "core/hle/kernel/k_resource_limit.h" +#include "core/hle/kernel/slab_helpers.h" + +namespace Kernel { + +// NOTE: Nintendo's implementation does not have the "is_secure_resource" field, and instead uses +// virtual IsSecureResource(). + +class KSystemResource : public KAutoObject { + KERNEL_AUTOOBJECT_TRAITS(KSystemResource, KAutoObject); + +public: + explicit KSystemResource(KernelCore& kernel_) : KAutoObject(kernel_) {} + +protected: + void SetSecureResource() { + m_is_secure_resource = true; + } + +public: + virtual void Destroy() override { + UNREACHABLE_MSG("KSystemResource::Destroy() was called"); + } + + bool IsSecureResource() const { + return m_is_secure_resource; + } + + void SetManagers(KMemoryBlockSlabManager& mb, KBlockInfoManager& bi, KPageTableManager& pt) { + ASSERT(m_p_memory_block_slab_manager == nullptr); + ASSERT(m_p_block_info_manager == nullptr); + ASSERT(m_p_page_table_manager == nullptr); + + m_p_memory_block_slab_manager = std::addressof(mb); + m_p_block_info_manager = std::addressof(bi); + m_p_page_table_manager = std::addressof(pt); + } + + const KMemoryBlockSlabManager& GetMemoryBlockSlabManager() const { + return *m_p_memory_block_slab_manager; + } + const KBlockInfoManager& GetBlockInfoManager() const { + return *m_p_block_info_manager; + } + const KPageTableManager& GetPageTableManager() const { + return *m_p_page_table_manager; + } + + KMemoryBlockSlabManager& GetMemoryBlockSlabManager() { + return *m_p_memory_block_slab_manager; + } + KBlockInfoManager& GetBlockInfoManager() { + return *m_p_block_info_manager; + } + KPageTableManager& GetPageTableManager() { + return *m_p_page_table_manager; + } + + KMemoryBlockSlabManager* GetMemoryBlockSlabManagerPointer() { + return m_p_memory_block_slab_manager; + } + KBlockInfoManager* GetBlockInfoManagerPointer() { + return m_p_block_info_manager; + } + KPageTableManager* GetPageTableManagerPointer() { + return m_p_page_table_manager; + } + +private: + KMemoryBlockSlabManager* m_p_memory_block_slab_manager{}; + KBlockInfoManager* m_p_block_info_manager{}; + KPageTableManager* m_p_page_table_manager{}; + bool m_is_secure_resource{false}; +}; + +class KSecureSystemResource final + : public KAutoObjectWithSlabHeap { +public: + explicit KSecureSystemResource(KernelCore& kernel_) + : KAutoObjectWithSlabHeap(kernel_) { + // Mark ourselves as being a secure resource. + this->SetSecureResource(); + } + + Result Initialize(size_t size, KResourceLimit* resource_limit, KMemoryManager::Pool pool); + void Finalize(); + + bool IsInitialized() const { + return m_is_initialized; + } + static void PostDestroy([[maybe_unused]] uintptr_t arg) {} + + size_t CalculateRequiredSecureMemorySize() const { + return CalculateRequiredSecureMemorySize(m_resource_size, m_resource_pool); + } + + size_t GetSize() const { + return m_resource_size; + } + size_t GetUsedSize() const { + return m_dynamic_page_manager.GetUsed() * PageSize; + } + + const KDynamicPageManager& GetDynamicPageManager() const { + return m_dynamic_page_manager; + } + +public: + static size_t CalculateRequiredSecureMemorySize(size_t size, KMemoryManager::Pool pool); + +private: + bool m_is_initialized{}; + KMemoryManager::Pool m_resource_pool{}; + KDynamicPageManager m_dynamic_page_manager; + KMemoryBlockSlabManager m_memory_block_slab_manager; + KBlockInfoManager m_block_info_manager; + KPageTableManager m_page_table_manager; + KMemoryBlockSlabHeap m_memory_block_heap; + KBlockInfoSlabHeap m_block_info_heap; + KPageTableSlabHeap m_page_table_heap; + KResourceLimit* m_resource_limit{}; + VAddr m_resource_address{}; + size_t m_resource_size{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 174afc8..21207fe 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -30,6 +30,7 @@ #include "core/hle/kernel/k_worker_task_manager.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/svc_types.h" #include "core/hle/result.h" #include "core/memory.h" @@ -38,6 +39,9 @@ #endif namespace { + +constexpr inline s32 TerminatingThreadPriority = Kernel::Svc::SystemThreadPriorityHighest - 1; + static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, u32 entry_point, u32 arg) { context = {}; @@ -144,7 +148,9 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack physical_affinity_mask.SetAffinity(phys_core, true); // Set the thread state. - thread_state = (type == ThreadType::Main) ? ThreadState::Runnable : ThreadState::Initialized; + thread_state = (type == ThreadType::Main || type == ThreadType::Dummy) + ? ThreadState::Runnable + : ThreadState::Initialized; // Set TLS address. tls_address = 0; @@ -241,7 +247,7 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack } } - return ResultSuccess; + R_SUCCEED(); } Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, @@ -254,41 +260,42 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_ thread->host_context = std::make_shared(std::move(init_func)); thread->is_single_core = !Settings::values.use_multi_core.GetValue(); - return ResultSuccess; + R_SUCCEED(); } -Result KThread::InitializeDummyThread(KThread* thread) { +Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) { // Initialize the thread. - R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy)); + R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy)); // Initialize emulation parameters. thread->stack_parameters.disable_count = 0; - return ResultSuccess; + R_SUCCEED(); } Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) { - return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, - system.GetCpuManager().GetGuestActivateFunc()); + R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, + ThreadType::Main, system.GetCpuManager().GetGuestActivateFunc())); } Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { - return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, - system.GetCpuManager().GetIdleThreadStartFunc()); + R_RETURN(InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, + ThreadType::Main, system.GetCpuManager().GetIdleThreadStartFunc())); } Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, KThreadFunction func, uintptr_t arg, s32 virt_core) { - return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority, - system.GetCpuManager().GetShutdownThreadStartFunc()); + R_RETURN(InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, + ThreadType::HighPriority, + system.GetCpuManager().GetShutdownThreadStartFunc())); } Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, s32 virt_core, KProcess* owner) { system.Kernel().GlobalSchedulerContext().AddThread(thread); - return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, - ThreadType::User, system.GetCpuManager().GetGuestThreadFunc()); + R_RETURN(InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, + ThreadType::User, system.GetCpuManager().GetGuestThreadFunc())); } void KThread::PostDestroy(uintptr_t arg) { @@ -296,7 +303,7 @@ void KThread::PostDestroy(uintptr_t arg) { const bool resource_limit_release_hint = (arg & 1); const s64 hint_value = (resource_limit_release_hint ? 0 : 1); if (owner != nullptr) { - owner->GetResourceLimit()->Release(LimitableResource::Threads, 1, hint_value); + owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value); owner->Close(); } } @@ -538,7 +545,7 @@ Result KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { *out_ideal_core = virtual_ideal_core_id; *out_affinity_mask = virtual_affinity_mask; - return ResultSuccess; + R_SUCCEED(); } Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { @@ -554,7 +561,7 @@ Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) *out_affinity_mask = original_physical_affinity_mask.GetAffinityMask(); } - return ResultSuccess; + R_SUCCEED(); } Result KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { @@ -666,7 +673,7 @@ Result KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { } while (retry_update); } - return ResultSuccess; + R_SUCCEED(); } void KThread::SetBasePriority(s32 value) { @@ -839,7 +846,7 @@ Result KThread::SetActivity(Svc::ThreadActivity activity) { } while (thread_is_current); } - return ResultSuccess; + R_SUCCEED(); } Result KThread::GetThreadContext3(std::vector& out) { @@ -874,7 +881,7 @@ Result KThread::GetThreadContext3(std::vector& out) { } } - return ResultSuccess; + R_SUCCEED(); } void KThread::AddWaiterImpl(KThread* thread) { @@ -1038,7 +1045,7 @@ Result KThread::Run() { // Set our state and finish. SetState(ThreadState::Runnable); - return ResultSuccess; + R_SUCCEED(); } } @@ -1047,7 +1054,7 @@ void KThread::Exit() { // Release the thread resource hint, running thread count from parent. if (parent != nullptr) { - parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1); + parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1); resource_limit_release_hint = true; parent->DecrementRunningThreadCount(); } @@ -1073,6 +1080,78 @@ void KThread::Exit() { UNREACHABLE_MSG("KThread::Exit() would return"); } +Result KThread::Terminate() { + ASSERT(this != GetCurrentThreadPointer(kernel)); + + // Request the thread terminate if it hasn't already. + if (const auto new_state = this->RequestTerminate(); new_state != ThreadState::Terminated) { + // If the thread isn't terminated, wait for it to terminate. + s32 index; + KSynchronizationObject* objects[] = {this}; + R_TRY(KSynchronizationObject::Wait(kernel, std::addressof(index), objects, 1, + Svc::WaitInfinite)); + } + + R_SUCCEED(); +} + +ThreadState KThread::RequestTerminate() { + ASSERT(this != GetCurrentThreadPointer(kernel)); + + KScopedSchedulerLock sl{kernel}; + + // Determine if this is the first termination request. + const bool first_request = [&]() -> bool { + // Perform an atomic compare-and-swap from false to true. + bool expected = false; + return termination_requested.compare_exchange_strong(expected, true); + }(); + + // If this is the first request, start termination procedure. + if (first_request) { + // If the thread is in initialized state, just change state to terminated. + if (this->GetState() == ThreadState::Initialized) { + thread_state = ThreadState::Terminated; + return ThreadState::Terminated; + } + + // Register the terminating dpc. + this->RegisterDpc(DpcFlag::Terminating); + + // If the thread is pinned, unpin it. + if (this->GetStackParameters().is_pinned) { + this->GetOwnerProcess()->UnpinThread(this); + } + + // If the thread is suspended, continue it. + if (this->IsSuspended()) { + suspend_allowed_flags = 0; + this->UpdateState(); + } + + // Change the thread's priority to be higher than any system thread's. + if (this->GetBasePriority() >= Svc::SystemThreadPriorityHighest) { + this->SetBasePriority(TerminatingThreadPriority); + } + + // If the thread is runnable, send a termination interrupt to other cores. + if (this->GetState() == ThreadState::Runnable) { + if (const u64 core_mask = + physical_affinity_mask.GetAffinityMask() & ~(1ULL << GetCurrentCoreId(kernel)); + core_mask != 0) { + Kernel::KInterruptManager::SendInterProcessorInterrupt(kernel, core_mask); + } + } + + // Wake up the thread. + if (this->GetState() == ThreadState::Waiting) { + wait_queue->CancelWait(this, ResultTerminationRequested, true); + } + } + + return this->GetState(); +} + Result KThread::Sleep(s64 timeout) { ASSERT(!kernel.GlobalSchedulerContext().IsLocked()); ASSERT(this == GetCurrentThreadPointer(kernel)); @@ -1086,7 +1165,7 @@ Result KThread::Sleep(s64 timeout) { // Check if the thread should terminate. if (this->IsTerminationRequested()) { slp.CancelSleep(); - return ResultTerminationRequested; + R_THROW(ResultTerminationRequested); } // Wait for the sleep to end. @@ -1094,33 +1173,34 @@ Result KThread::Sleep(s64 timeout) { SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); } - return ResultSuccess; + R_SUCCEED(); } -void KThread::IfDummyThreadTryWait() { - if (!IsDummyThread()) { - return; - } +void KThread::RequestDummyThreadWait() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); + ASSERT(this->IsDummyThread()); - if (GetState() != ThreadState::Waiting) { - return; - } - - ASSERT(!kernel.IsPhantomModeForSingleCore()); - - // Block until we are no longer waiting. - std::unique_lock lk(dummy_wait_lock); - dummy_wait_cv.wait( - lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); }); + // We will block when the scheduler lock is released. + dummy_thread_runnable.store(false); } -void KThread::IfDummyThreadEndWait() { - if (!IsDummyThread()) { +void KThread::DummyThreadBeginWait() { + if (!this->IsDummyThread() || kernel.IsPhantomModeForSingleCore()) { + // Occurs in single core mode. return; } + // Block until runnable is no longer false. + dummy_thread_runnable.wait(false); +} + +void KThread::DummyThreadEndWait() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); + ASSERT(this->IsDummyThread()); + // Wake up the waiting thread. - dummy_wait_cv.notify_one(); + dummy_thread_runnable.store(true); + dummy_thread_runnable.notify_one(); } void KThread::BeginWait(KThreadQueue* queue) { @@ -1154,9 +1234,6 @@ void KThread::EndWait(Result wait_result_) { } wait_queue->EndWait(this, wait_result_); - - // Special case for dummy threads to wakeup if necessary. - IfDummyThreadEndWait(); } } diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 9ee2020..dc52b4e 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -180,6 +180,10 @@ public: void Exit(); + Result Terminate(); + + ThreadState RequestTerminate(); + [[nodiscard]] u32 GetSuspendFlags() const { return suspend_allowed_flags & suspend_request_flags; } @@ -411,7 +415,7 @@ public: static void PostDestroy(uintptr_t arg); - [[nodiscard]] static Result InitializeDummyThread(KThread* thread); + [[nodiscard]] static Result InitializeDummyThread(KThread* thread, KProcess* owner); [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core); @@ -639,8 +643,9 @@ public: // therefore will not block on guest kernel synchronization primitives. These methods handle // blocking as needed. - void IfDummyThreadTryWait(); - void IfDummyThreadEndWait(); + void RequestDummyThreadWait(); + void DummyThreadBeginWait(); + void DummyThreadEndWait(); [[nodiscard]] uintptr_t GetArgument() const { return argument; @@ -773,15 +778,14 @@ private: bool is_single_core{}; ThreadType thread_type{}; StepState step_state{}; - std::mutex dummy_wait_lock; - std::condition_variable dummy_wait_cv; + std::atomic dummy_thread_runnable{true}; // For debugging std::vector wait_objects_for_debugging; VAddr mutex_wait_address_for_debugging{}; ThreadWaitReasonForDebugging wait_reason_for_debugging{}; - uintptr_t argument; - VAddr stack_top; + uintptr_t argument{}; + VAddr stack_top{}; public: using ConditionVariableThreadTreeType = ConditionVariableThreadTree; diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h index 0a7f226..fe0cff0 100644 --- a/src/core/hle/kernel/k_thread_local_page.h +++ b/src/core/hle/kernel/k_thread_local_page.h @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/intrusive_red_black_tree.h" +#include "common/polyfill_ranges.h" #include "core/hle/kernel/memory_types.h" #include "core/hle/kernel/slab_helpers.h" #include "core/hle/result.h" @@ -26,7 +27,7 @@ public: static_assert(RegionsPerPage > 0); public: - constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) { + constexpr explicit KThreadLocalPage(KernelCore&, VAddr addr = {}) : m_virt_addr(addr) { m_is_region_free.fill(true); } diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index b0320eb..9f34c2d 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp @@ -37,7 +37,7 @@ void KTransferMemory::Finalize() { void KTransferMemory::PostDestroy(uintptr_t arg) { KProcess* owner = reinterpret_cast(arg); - owner->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1); + owner->GetResourceLimit()->Release(LimitableResource::TransferMemoryCountMax, 1); owner->Close(); } diff --git a/src/core/hle/kernel/k_worker_task_manager.cpp b/src/core/hle/kernel/k_worker_task_manager.cpp index 221f341..04042bf 100644 --- a/src/core/hle/kernel/k_worker_task_manager.cpp +++ b/src/core/hle/kernel/k_worker_task_manager.cpp @@ -23,7 +23,7 @@ void KWorkerTask::DoWorkerTask() { } } -KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "yuzu:KWorkerTaskManager") {} +KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {} void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { ASSERT(type <= WorkerType::Count); diff --git a/src/core/hle/kernel/k_writable_event.cpp b/src/core/hle/kernel/k_writable_event.cpp deleted file mode 100644 index ff88c5a..0000000 --- a/src/core/hle/kernel/k_writable_event.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" - -namespace Kernel { - -KWritableEvent::KWritableEvent(KernelCore& kernel_) - : KAutoObjectWithSlabHeapAndContainer{kernel_} {} - -KWritableEvent::~KWritableEvent() = default; - -void KWritableEvent::Initialize(KEvent* parent_event_, std::string&& name_) { - parent = parent_event_; - name = std::move(name_); - parent->GetReadableEvent().Open(); -} - -Result KWritableEvent::Signal() { - return parent->GetReadableEvent().Signal(); -} - -Result KWritableEvent::Clear() { - return parent->GetReadableEvent().Clear(); -} - -void KWritableEvent::Destroy() { - // Close our references. - parent->GetReadableEvent().Close(); - parent->Close(); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/k_writable_event.h b/src/core/hle/kernel/k_writable_event.h deleted file mode 100644 index 3fd0c7d..0000000 --- a/src/core/hle/kernel/k_writable_event.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/kernel/k_auto_object.h" -#include "core/hle/kernel/slab_helpers.h" -#include "core/hle/result.h" - -namespace Kernel { - -class KernelCore; -class KEvent; - -class KWritableEvent final - : public KAutoObjectWithSlabHeapAndContainer { - KERNEL_AUTOOBJECT_TRAITS(KWritableEvent, KAutoObject); - -public: - explicit KWritableEvent(KernelCore& kernel_); - ~KWritableEvent() override; - - void Destroy() override; - - static void PostDestroy([[maybe_unused]] uintptr_t arg) {} - - void Initialize(KEvent* parent_, std::string&& name_); - Result Signal(); - Result Clear(); - - KEvent* GetParent() const { - return parent; - } - -private: - KEvent* parent{}; -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index ce7fa82..0eb74a4 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -24,13 +24,16 @@ #include "core/hardware_properties.h" #include "core/hle/kernel/init/init_slab_setup.h" #include "core/hle/kernel/k_client_port.h" +#include "core/hle/kernel/k_dynamic_resource_manager.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" +#include "core/hle/kernel/k_page_buffer.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_system_resource.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_worker_task_manager.h" #include "core/hle/kernel/kernel.h" @@ -46,9 +49,14 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); namespace Kernel { struct KernelCore::Impl { + static constexpr size_t ApplicationMemoryBlockSlabHeapSize = 20000; + static constexpr size_t SystemMemoryBlockSlabHeapSize = 10000; + static constexpr size_t BlockInfoSlabHeapSize = 4000; + static constexpr size_t ReservedDynamicPageCount = 64; + explicit Impl(Core::System& system_, KernelCore& kernel_) - : time_manager{system_}, - service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {} + : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"}, + service_thread_barrier{2}, system{system_} {} void SetMulticore(bool is_multi) { is_multicore = is_multi; @@ -59,7 +67,6 @@ struct KernelCore::Impl { global_scheduler_context = std::make_unique(kernel); global_handle_table = std::make_unique(kernel); global_handle_table->Initialize(KHandleTable::MaxTableSize); - default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread"); is_phantom_mode_for_singlecore = false; @@ -71,12 +78,22 @@ struct KernelCore::Impl { // Initialize kernel memory and resources. InitializeSystemResourceLimit(kernel, system.CoreTiming()); InitializeMemoryLayout(); - Init::InitializeKPageBufferSlabHeap(system); InitializeShutdownThreads(); - InitializePreemption(kernel); InitializePhysicalCores(); + InitializePreemption(kernel); - RegisterHostThread(); + // Initialize the Dynamic Slab Heaps. + { + const auto& pt_heap_region = memory_layout->GetPageTableHeapRegion(); + ASSERT(pt_heap_region.GetEndAddress() != 0); + + InitializeResourceManagers(kernel, pt_heap_region.GetAddress(), + pt_heap_region.GetSize()); + } + + RegisterHostThread(nullptr); + + default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread"); } void InitializeCores() { @@ -86,6 +103,19 @@ struct KernelCore::Impl { } } + void CloseCurrentProcess() { + KProcess* old_process = current_process.exchange(nullptr); + if (old_process == nullptr) { + return; + } + + // old_process->Close(); + // TODO: The process should be destroyed based on accurate ref counting after + // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. + old_process->Finalize(); + old_process->Destroy(); + } + void Shutdown() { is_shutting_down.store(true, std::memory_order_relaxed); SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); }); @@ -99,10 +129,6 @@ struct KernelCore::Impl { next_user_process_id = KProcess::ProcessIDMin; next_thread_id = 1; - for (auto& core : cores) { - core = nullptr; - } - global_handle_table->Finalize(); global_handle_table.reset(); @@ -152,15 +178,7 @@ struct KernelCore::Impl { } } - // Shutdown all processes. - if (current_process) { - (*current_process).Finalize(); - // current_process->Close(); - // TODO: The current process should be destroyed based on accurate ref counting after - // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. - (*current_process).Destroy(); - current_process = nullptr; - } + CloseCurrentProcess(); // Track kernel objects that were not freed on shutdown { @@ -178,17 +196,6 @@ struct KernelCore::Impl { } void CloseServices() { - // Close all open server sessions and ports. - std::unordered_set server_objects_; - { - std::scoped_lock lk(server_objects_lock); - server_objects_ = server_objects; - server_objects.clear(); - } - for (auto* server_object : server_objects_) { - server_object->Close(); - } - // Ensures all service threads gracefully shutdown. ClearServiceThreads(); } @@ -226,18 +233,22 @@ struct KernelCore::Impl { const auto kernel_size{sizes.second}; // If setting the default system values fails, then something seriously wrong has occurred. - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) + ASSERT( + system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, total_size) + .IsSuccess()); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800) .IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200) + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900) .IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); - system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200) + .IsSuccess()); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133) + .IsSuccess()); + system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, kernel_size); // Reserve secure applet memory, introduced in firmware 5.0.0 constexpr u64 secure_applet_memory_size{4_MiB}; - ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, + ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, secure_applet_memory_size)); } @@ -257,6 +268,84 @@ struct KernelCore::Impl { system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event); } + void InitializeResourceManagers(KernelCore& kernel, VAddr address, size_t size) { + // Ensure that the buffer is suitable for our use. + ASSERT(Common::IsAligned(address, PageSize)); + ASSERT(Common::IsAligned(size, PageSize)); + + // Ensure that we have space for our reference counts. + const size_t rc_size = + Common::AlignUp(KPageTableSlabHeap::CalculateReferenceCountSize(size), PageSize); + ASSERT(rc_size < size); + size -= rc_size; + + // Initialize the resource managers' shared page manager. + resource_manager_page_manager = std::make_unique(); + resource_manager_page_manager->Initialize( + address, size, std::max(PageSize, KPageBufferSlabHeap::BufferSize)); + + // Initialize the KPageBuffer slab heap. + page_buffer_slab_heap.Initialize(system); + + // Initialize the fixed-size slabheaps. + app_memory_block_heap = std::make_unique(); + sys_memory_block_heap = std::make_unique(); + block_info_heap = std::make_unique(); + app_memory_block_heap->Initialize(resource_manager_page_manager.get(), + ApplicationMemoryBlockSlabHeapSize); + sys_memory_block_heap->Initialize(resource_manager_page_manager.get(), + SystemMemoryBlockSlabHeapSize); + block_info_heap->Initialize(resource_manager_page_manager.get(), BlockInfoSlabHeapSize); + + // Reserve all but a fixed number of remaining pages for the page table heap. + const size_t num_pt_pages = resource_manager_page_manager->GetCount() - + resource_manager_page_manager->GetUsed() - + ReservedDynamicPageCount; + page_table_heap = std::make_unique(); + + // TODO(bunnei): Pass in address once we support kernel virtual memory allocations. + page_table_heap->Initialize( + resource_manager_page_manager.get(), num_pt_pages, + /*GetPointer(address + size)*/ nullptr); + + // Setup the slab managers. + KDynamicPageManager* const app_dynamic_page_manager = nullptr; + KDynamicPageManager* const sys_dynamic_page_manager = + /*KTargetSystem::IsDynamicResourceLimitsEnabled()*/ true + ? resource_manager_page_manager.get() + : nullptr; + app_memory_block_manager = std::make_unique(); + sys_memory_block_manager = std::make_unique(); + app_block_info_manager = std::make_unique(); + sys_block_info_manager = std::make_unique(); + app_page_table_manager = std::make_unique(); + sys_page_table_manager = std::make_unique(); + + app_memory_block_manager->Initialize(app_dynamic_page_manager, app_memory_block_heap.get()); + sys_memory_block_manager->Initialize(sys_dynamic_page_manager, sys_memory_block_heap.get()); + + app_block_info_manager->Initialize(app_dynamic_page_manager, block_info_heap.get()); + sys_block_info_manager->Initialize(sys_dynamic_page_manager, block_info_heap.get()); + + app_page_table_manager->Initialize(app_dynamic_page_manager, page_table_heap.get()); + sys_page_table_manager->Initialize(sys_dynamic_page_manager, page_table_heap.get()); + + // Check that we have the correct number of dynamic pages available. + ASSERT(resource_manager_page_manager->GetCount() - + resource_manager_page_manager->GetUsed() == + ReservedDynamicPageCount); + + // Create the system page table managers. + app_system_resource = std::make_unique(kernel); + sys_system_resource = std::make_unique(kernel); + + // Set the managers for the system resources. + app_system_resource->SetManagers(*app_memory_block_manager, *app_block_info_manager, + *app_page_table_manager); + sys_system_resource->SetManagers(*sys_memory_block_manager, *sys_block_info_manager, + *sys_page_table_manager); + } + void InitializeShutdownThreads() { for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { shutdown_threads[core_id] = KThread::Create(system.Kernel()); @@ -292,15 +381,18 @@ struct KernelCore::Impl { } // Gets the dummy KThread for the caller, allocating a new one if this is the first time - KThread* GetHostDummyThread() { + KThread* GetHostDummyThread(KThread* existing_thread) { auto initialize = [this](KThread* thread) { - ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); + ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); return thread; }; - thread_local auto raw_thread = KThread(system.Kernel()); - thread_local auto thread = initialize(&raw_thread); + thread_local KThread raw_thread{system.Kernel()}; + thread_local KThread* thread = nullptr; + if (thread == nullptr) { + thread = (existing_thread == nullptr) ? initialize(&raw_thread) : existing_thread; + } return thread; } @@ -315,9 +407,9 @@ struct KernelCore::Impl { } /// Registers a new host thread by allocating a host thread ID for it - void RegisterHostThread() { + void RegisterHostThread(KThread* existing_thread) { [[maybe_unused]] const auto this_id = GetHostThreadId(); - [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(); + [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(existing_thread); } [[nodiscard]] u32 GetCurrentHostThreadID() { @@ -328,6 +420,8 @@ struct KernelCore::Impl { return this_id; } + static inline thread_local bool is_phantom_mode_for_singlecore{false}; + bool IsPhantomModeForSingleCore() const { return is_phantom_mode_for_singlecore; } @@ -344,14 +438,9 @@ struct KernelCore::Impl { static inline thread_local KThread* current_thread{nullptr}; KThread* GetCurrentEmuThread() { - // If we are shutting down the kernel, none of this is relevant anymore. - if (IsShuttingDown()) { - return {}; - } - const auto thread_id = GetCurrentHostThreadID(); if (thread_id >= Core::Hardware::NUM_CPU_CORES) { - return GetHostDummyThread(); + return GetHostDummyThread(nullptr); } return current_thread; @@ -441,6 +530,9 @@ struct KernelCore::Impl { ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc)); + // Determine if we'll use extra thread resources. + const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit(); + // Setup the stack region. constexpr size_t StackRegionSize = 14_MiB; constexpr size_t StackRegionAlign = KernelAslrAlignment; @@ -451,7 +543,8 @@ struct KernelCore::Impl { stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack)); // Determine the size of the resource region. - const size_t resource_region_size = memory_layout->GetResourceRegionSizeForInit(); + const size_t resource_region_size = + memory_layout->GetResourceRegionSizeForInit(use_extra_resources); // Determine the size of the slab region. const size_t slab_region_size = @@ -685,49 +778,48 @@ struct KernelCore::Impl { return {}; } - KClientPort* port = &search->second(system.ServiceManager(), system); - RegisterServerObject(&port->GetParent()->GetServerPort()); - return port; + return &search->second(system.ServiceManager(), system); } - void RegisterServerObject(KAutoObject* server_object) { - std::scoped_lock lk(server_objects_lock); - server_objects.insert(server_object); + void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) { + auto search = service_interface_handlers.find(name); + if (search == service_interface_handlers.end()) { + return; + } + + search->second(system.ServiceManager(), server_port); } - void UnregisterServerObject(KAutoObject* server_object) { - std::scoped_lock lk(server_objects_lock); - server_objects.erase(server_object); - } - - std::weak_ptr CreateServiceThread(KernelCore& kernel, - const std::string& name) { - auto service_thread = std::make_shared(kernel, 1, name); + Kernel::ServiceThread& CreateServiceThread(KernelCore& kernel, const std::string& name) { + auto* ptr = new ServiceThread(kernel, name); service_threads_manager.QueueWork( - [this, service_thread]() { service_threads.emplace(service_thread); }); + [this, ptr]() { service_threads.emplace(ptr, std::unique_ptr(ptr)); }); - return service_thread; + return *ptr; } - void ReleaseServiceThread(std::weak_ptr service_thread) { - if (auto strong_ptr = service_thread.lock()) { - if (strong_ptr == default_service_thread.lock()) { - // Nothing to do here, the service is using default_service_thread, which will be - // released on shutdown. - return; - } + void ReleaseServiceThread(Kernel::ServiceThread& service_thread) { + auto* ptr = &service_thread; - service_threads_manager.QueueWork( - [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); + if (ptr == default_service_thread) { + // Nothing to do here, the service is using default_service_thread, which will be + // released on shutdown. + return; } + + service_threads_manager.QueueWork([this, ptr]() { service_threads.erase(ptr); }); } void ClearServiceThreads() { - service_threads_manager.QueueWork([this]() { service_threads.clear(); }); + service_threads_manager.QueueWork([this] { + service_threads.clear(); + default_service_thread = nullptr; + service_thread_barrier.Sync(); + }); + service_thread_barrier.Sync(); } - std::mutex server_objects_lock; std::mutex registered_objects_lock; std::mutex registered_in_use_objects_lock; @@ -745,6 +837,8 @@ struct KernelCore::Impl { Init::KSlabResourceCounts slab_resource_counts{}; KResourceLimit* system_resource_limit{}; + KPageBufferSlabHeap page_buffer_slab_heap; + std::shared_ptr preemption_event; // This is the kernel's handle table or supervisor handle table which @@ -756,8 +850,8 @@ struct KernelCore::Impl { /// Map of named ports managed by the kernel, which can be retrieved using /// the ConnectToPort SVC. std::unordered_map service_interface_factory; + std::unordered_map service_interface_handlers; NamedPortTable named_ports; - std::unordered_set server_objects; std::unordered_set registered_objects; std::unordered_set registered_in_use_objects; @@ -770,6 +864,21 @@ struct KernelCore::Impl { // Kernel memory management std::unique_ptr memory_manager; + // Resource managers + std::unique_ptr resource_manager_page_manager; + std::unique_ptr page_table_heap; + std::unique_ptr app_memory_block_heap; + std::unique_ptr sys_memory_block_heap; + std::unique_ptr block_info_heap; + std::unique_ptr app_page_table_manager; + std::unique_ptr sys_page_table_manager; + std::unique_ptr app_memory_block_manager; + std::unique_ptr sys_memory_block_manager; + std::unique_ptr app_block_info_manager; + std::unique_ptr sys_block_info_manager; + std::unique_ptr app_system_resource; + std::unique_ptr sys_system_resource; + // Shared memory for services Kernel::KSharedMemory* hid_shared_mem{}; Kernel::KSharedMemory* font_shared_mem{}; @@ -781,16 +890,16 @@ struct KernelCore::Impl { std::unique_ptr memory_layout; // Threads used for services - std::unordered_set> service_threads; - std::weak_ptr default_service_thread; + std::unordered_map> service_threads; + ServiceThread* default_service_thread{}; Common::ThreadWorker service_threads_manager; + Common::Barrier service_thread_barrier; - std::array shutdown_threads; + std::array shutdown_threads{}; std::array, Core::Hardware::NUM_CPU_CORES> schedulers{}; bool is_multicore{}; std::atomic_bool is_shutting_down{}; - bool is_phantom_mode_for_singlecore{}; u32 single_core_thread_id{}; std::array svc_ticks{}; @@ -853,6 +962,10 @@ const KProcess* KernelCore::CurrentProcess() const { return impl->current_process; } +void KernelCore::CloseCurrentProcess() { + impl->CloseCurrentProcess(); +} + const std::vector& KernelCore::GetProcessList() const { return impl->process_list; } @@ -953,16 +1066,17 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory& impl->service_interface_factory.emplace(std::move(name), factory); } +void KernelCore::RegisterInterfaceForNamedService(std::string name, + ServiceInterfaceHandlerFn&& handler) { + impl->service_interface_handlers.emplace(std::move(name), handler); +} + KClientPort* KernelCore::CreateNamedServicePort(std::string name) { return impl->CreateNamedServicePort(std::move(name)); } -void KernelCore::RegisterServerObject(KAutoObject* server_object) { - impl->RegisterServerObject(server_object); -} - -void KernelCore::UnregisterServerObject(KAutoObject* server_object) { - impl->UnregisterServerObject(server_object); +void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* server_port) { + impl->RegisterNamedServiceHandler(std::move(name), server_port); } void KernelCore::RegisterKernelObject(KAutoObject* object) { @@ -1017,8 +1131,12 @@ void KernelCore::RegisterCoreThread(std::size_t core_id) { impl->RegisterCoreThread(core_id); } -void KernelCore::RegisterHostThread() { - impl->RegisterHostThread(); +void KernelCore::RegisterHostThread(KThread* existing_thread) { + impl->RegisterHostThread(existing_thread); + + if (existing_thread != nullptr) { + ASSERT(GetCurrentEmuThread() == existing_thread); + } } u32 KernelCore::GetCurrentHostThreadID() const { @@ -1041,6 +1159,14 @@ const KMemoryManager& KernelCore::MemoryManager() const { return *impl->memory_manager; } +KSystemResource& KernelCore::GetSystemSystemResource() { + return *impl->sys_system_resource; +} + +const KSystemResource& KernelCore::GetSystemSystemResource() const { + return *impl->sys_system_resource; +} + Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { return *impl->hid_shared_mem; } @@ -1085,16 +1211,28 @@ void KernelCore::Suspend(bool suspended) { const bool should_suspend{exception_exited || suspended}; const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; - for (auto* process : GetProcessList()) { - process->SetActivity(activity); + std::vector> process_threads; + { + KScopedSchedulerLock sl{*this}; + + if (auto* process = CurrentProcess(); process != nullptr) { + process->SetActivity(activity); + + if (!should_suspend) { + // Runnable now; no need to wait. + return; + } - if (should_suspend) { - // Wait for execution to stop for (auto* thread : process->GetThreadList()) { - thread->WaitUntilSuspended(); + process_threads.emplace_back(thread); } } } + + // Wait for execution to stop. + for (auto& thread : process_threads) { + thread->WaitUntilSuspended(); + } } void KernelCore::ShutdownCores() { @@ -1126,15 +1264,15 @@ void KernelCore::ExitSVCProfile() { MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]); } -std::weak_ptr KernelCore::CreateServiceThread(const std::string& name) { +Kernel::ServiceThread& KernelCore::CreateServiceThread(const std::string& name) { return impl->CreateServiceThread(*this, name); } -std::weak_ptr KernelCore::GetDefaultServiceThread() const { - return impl->default_service_thread; +Kernel::ServiceThread& KernelCore::GetDefaultServiceThread() const { + return *impl->default_service_thread; } -void KernelCore::ReleaseServiceThread(std::weak_ptr service_thread) { +void KernelCore::ReleaseServiceThread(Kernel::ServiceThread& service_thread) { impl->ReleaseServiceThread(service_thread); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index bcf016a..2e22fe0 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -34,25 +34,31 @@ class KClientPort; class GlobalSchedulerContext; class KAutoObjectWithListContainer; class KClientSession; +class KDebug; +class KDynamicPageManager; class KEvent; +class KEventInfo; class KHandleTable; class KLinkedListNode; class KMemoryLayout; class KMemoryManager; class KPageBuffer; +class KPageBufferSlabHeap; class KPort; class KProcess; class KResourceLimit; class KScheduler; +class KServerPort; class KServerSession; class KSession; +class KSessionRequest; class KSharedMemory; class KSharedMemoryInfo; +class KSecureSystemResource; class KThread; class KThreadLocalPage; class KTransferMemory; class KWorkerTaskManager; -class KWritableEvent; class KCodeMemory; class PhysicalCore; class ServiceThread; @@ -62,6 +68,8 @@ class TimeManager; using ServiceInterfaceFactory = std::function; +using ServiceInterfaceHandlerFn = std::function; + namespace Init { struct KSlabResourceCounts; } @@ -131,6 +139,9 @@ public: /// Retrieves a const pointer to the current process. const KProcess* CurrentProcess() const; + /// Closes the current process. + void CloseCurrentProcess(); + /// Retrieves the list of processes. const std::vector& GetProcessList() const; @@ -188,16 +199,14 @@ public: /// Registers a named HLE service, passing a factory used to open a port to that service. void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory); + /// Registers a setup function for the named HLE service. + void RegisterInterfaceForNamedService(std::string name, ServiceInterfaceHandlerFn&& handler); + /// Opens a port to a service previously registered with RegisterNamedService. KClientPort* CreateNamedServicePort(std::string name); - /// Registers a server session or port with the gobal emulation state, to be freed on shutdown. - /// This is necessary because we do not emulate processes for HLE sessions and ports. - void RegisterServerObject(KAutoObject* server_object); - - /// Unregisters a server session or port previously registered with RegisterServerSession when - /// it was destroyed during the current emulation session. - void UnregisterServerObject(KAutoObject* server_object); + /// Accepts a session on a port created by CreateNamedServicePort. + void RegisterNamedServiceHandler(std::string name, KServerPort* server_port); /// Registers all kernel objects with the global emulation state, this is purely for tracking /// leaks after emulation has been shutdown. @@ -231,7 +240,7 @@ public: void RegisterCoreThread(std::size_t core_id); /// Register the current thread as a non CPU core thread. - void RegisterHostThread(); + void RegisterHostThread(KThread* existing_thread = nullptr); /// Gets the virtual memory manager for the kernel. KMemoryManager& MemoryManager(); @@ -239,6 +248,12 @@ public: /// Gets the virtual memory manager for the kernel. const KMemoryManager& MemoryManager() const; + /// Gets the system resource manager. + KSystemResource& GetSystemSystemResource(); + + /// Gets the system resource manager. + const KSystemResource& GetSystemSystemResource() const; + /// Gets the shared memory object for HID services. Kernel::KSharedMemory& GetHidSharedMem(); @@ -294,24 +309,24 @@ public: * See GetDefaultServiceThread. * @param name String name for the ServerSession creating this thread, used for debug * purposes. - * @returns The a weak pointer newly created service thread. + * @returns A reference to the newly created service thread. */ - std::weak_ptr CreateServiceThread(const std::string& name); + Kernel::ServiceThread& CreateServiceThread(const std::string& name); /** * Gets the default host service thread, which executes HLE service requests. Unless service * requests need to block on the host, the default service thread should be used in favor of * creating a new service thread. - * @returns The a weak pointer for the default service thread. + * @returns A reference to the default service thread. */ - std::weak_ptr GetDefaultServiceThread() const; + Kernel::ServiceThread& GetDefaultServiceThread() const; /** * Releases a HLE service thread, instructing KernelCore to free it. This should be called when * the ServerSession associated with the thread is destroyed. * @param service_thread Service thread to release. */ - void ReleaseServiceThread(std::weak_ptr service_thread); + void ReleaseServiceThread(Kernel::ServiceThread& service_thread); /// Workaround for single-core mode when preempting threads while idle. bool IsPhantomModeForSingleCore() const; @@ -345,14 +360,20 @@ public: return slab_heap_container->thread; } else if constexpr (std::is_same_v) { return slab_heap_container->transfer_memory; - } else if constexpr (std::is_same_v) { - return slab_heap_container->writeable_event; } else if constexpr (std::is_same_v) { return slab_heap_container->code_memory; } else if constexpr (std::is_same_v) { return slab_heap_container->page_buffer; } else if constexpr (std::is_same_v) { return slab_heap_container->thread_local_page; + } else if constexpr (std::is_same_v) { + return slab_heap_container->session_request; + } else if constexpr (std::is_same_v) { + return slab_heap_container->secure_system_resource; + } else if constexpr (std::is_same_v) { + return slab_heap_container->event_info; + } else if constexpr (std::is_same_v) { + return slab_heap_container->debug; } } @@ -412,10 +433,13 @@ private: KSlabHeap shared_memory_info; KSlabHeap thread; KSlabHeap transfer_memory; - KSlabHeap writeable_event; KSlabHeap code_memory; KSlabHeap page_buffer; KSlabHeap thread_local_page; + KSlabHeap session_request; + KSlabHeap secure_system_resource; + KSlabHeap event_info; + KSlabHeap debug; }; std::unique_ptr slab_heap_container; diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index d437596..3044922 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -12,7 +12,7 @@ namespace Kernel { PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_) : core_index{core_index_}, system{system_}, scheduler{scheduler_} { -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) // TODO(bunnei): Initialization relies on a core being available. We may later replace this with // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. auto& kernel = system.Kernel(); @@ -26,7 +26,7 @@ PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KSche PhysicalCore::~PhysicalCore() = default; void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) auto& kernel = system.Kernel(); if (!is_64_bit) { // We already initialized a 64-bit core, replace with a 32-bit one. diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 2fc8d4b..fb2ba4c 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -85,7 +85,7 @@ private: std::mutex guard; std::condition_variable on_interrupt; std::unique_ptr arm_interface; - bool is_interrupted; + bool is_interrupted{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 2e87b4e..38afa72 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -1,15 +1,19 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include +#include #include #include #include -#include +#include "common/polyfill_thread.h" #include "common/scope_exit.h" #include "common/thread.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -19,101 +23,184 @@ namespace Kernel { class ServiceThread::Impl final { public: - explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name); + explicit Impl(KernelCore& kernel, const std::string& service_name); ~Impl(); - void QueueSyncRequest(KSession& session, std::shared_ptr&& context); + void WaitAndProcessImpl(); + void SessionClosed(KServerSession* server_session, + std::shared_ptr manager); + void LoopProcess(); + + void RegisterServerSession(KServerSession* session, + std::shared_ptr manager); private: - std::vector threads; - std::queue> requests; - std::mutex queue_mutex; - std::condition_variable_any condition; - const std::string service_name; + KernelCore& kernel; + const std::string m_service_name; + + std::jthread m_host_thread{}; + std::mutex m_session_mutex{}; + std::map> m_sessions{}; + KEvent* m_wakeup_event{}; + KThread* m_thread{}; + std::atomic m_shutdown_requested{}; }; -ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) - : service_name{name} { - for (std::size_t i = 0; i < num_threads; ++i) { - threads.emplace_back([this, &kernel](std::stop_token stop_token) { - Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str()); +void ServiceThread::Impl::WaitAndProcessImpl() { + // Create local list of waitable sessions. + std::vector objs; + std::vector> managers; - // Wait for first request before trying to acquire a render context - { - std::unique_lock lock{queue_mutex}; - condition.wait(lock, stop_token, [this] { return !requests.empty(); }); - } + { + // Lock to get the set. + std::scoped_lock lk{m_session_mutex}; - if (stop_token.stop_requested()) { - return; - } + // Reserve the needed quantity. + objs.reserve(m_sessions.size() + 1); + managers.reserve(m_sessions.size()); - // Allocate a dummy guest thread for this host thread. - kernel.RegisterHostThread(); + // Copy to our local list. + for (const auto& [session, manager] : m_sessions) { + objs.push_back(session); + managers.push_back(manager); + } - while (true) { - std::function task; + // Insert the wakeup event at the end. + objs.push_back(&m_wakeup_event->GetReadableEvent()); + } - { - std::unique_lock lock{queue_mutex}; - condition.wait(lock, stop_token, [this] { return !requests.empty(); }); + // Wait on the list of sessions. + s32 index{-1}; + Result rc = KSynchronizationObject::Wait(kernel, &index, objs.data(), + static_cast(objs.size()), -1); + ASSERT(!rc.IsFailure()); - if (stop_token.stop_requested()) { - return; - } + // If this was the wakeup event, clear it and finish. + if (index >= static_cast(objs.size() - 1)) { + m_wakeup_event->Clear(); + return; + } - if (requests.empty()) { - continue; - } + // This event is from a server session. + auto* server_session = static_cast(objs[index]); + auto& manager = managers[index]; - task = std::move(requests.front()); - requests.pop(); - } + // Fetch the HLE request context. + std::shared_ptr context; + rc = server_session->ReceiveRequest(&context, manager); - task(); - } - }); + // If the session was closed, handle that. + if (rc == ResultSessionClosed) { + SessionClosed(server_session, manager); + + // Finish. + return; + } + + // TODO: handle other cases + ASSERT(rc == ResultSuccess); + + // Perform the request. + Result service_rc = manager->CompleteSyncRequest(server_session, *context); + + // Reply to the client. + rc = server_session->SendReplyHLE(); + + if (rc == ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) { + SessionClosed(server_session, manager); + return; + } + + // TODO: handle other cases + ASSERT(rc == ResultSuccess); + ASSERT(service_rc == ResultSuccess); +} + +void ServiceThread::Impl::SessionClosed(KServerSession* server_session, + std::shared_ptr manager) { + { + // Lock to get the set. + std::scoped_lock lk{m_session_mutex}; + + // Erase the session. + ASSERT(m_sessions.erase(server_session) == 1); + } + + // Close our reference to the server session. + server_session->Close(); +} + +void ServiceThread::Impl::LoopProcess() { + Common::SetCurrentThreadName(m_service_name.c_str()); + + kernel.RegisterHostThread(m_thread); + + while (!m_shutdown_requested.load()) { + WaitAndProcessImpl(); } } -void ServiceThread::Impl::QueueSyncRequest(KSession& session, - std::shared_ptr&& context) { +void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session, + std::shared_ptr manager) { + // Open the server session. + server_session->Open(); + { - std::unique_lock lock{queue_mutex}; + // Lock to get the set. + std::scoped_lock lk{m_session_mutex}; - auto* server_session{&session.GetServerSession()}; - - // Open a reference to the session to ensure it is not closes while the service request - // completes asynchronously. - server_session->Open(); - - requests.emplace([server_session, context{std::move(context)}]() { - // Close the reference. - SCOPE_EXIT({ server_session->Close(); }); - - // Complete the service request. - server_session->CompleteSyncRequest(*context); - }); + // Insert the session and manager. + m_sessions[server_session] = manager; } - condition.notify_one(); + + // Signal the wakeup event. + m_wakeup_event->Signal(); } ServiceThread::Impl::~Impl() { - condition.notify_all(); - for (auto& thread : threads) { - thread.request_stop(); - thread.join(); + // Shut down the processing thread. + m_shutdown_requested.store(true); + m_wakeup_event->Signal(); + m_host_thread.join(); + + // Close all remaining sessions. + for (const auto& [server_session, manager] : m_sessions) { + server_session->Close(); } + + // Destroy remaining managers. + m_sessions.clear(); + + // Close event. + m_wakeup_event->GetReadableEvent().Close(); + m_wakeup_event->Close(); + + // Close thread. + m_thread->Close(); } -ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name) - : impl{std::make_unique(kernel, num_threads, name)} {} +ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name) + : kernel{kernel_}, m_service_name{service_name} { + // Initialize event. + m_wakeup_event = KEvent::Create(kernel); + m_wakeup_event->Initialize(nullptr); + + // Initialize thread. + m_thread = KThread::Create(kernel); + ASSERT(KThread::InitializeDummyThread(m_thread, nullptr).IsSuccess()); + + // Start thread. + m_host_thread = std::jthread([this] { LoopProcess(); }); +} + +ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name) + : impl{std::make_unique(kernel, name)} {} ServiceThread::~ServiceThread() = default; -void ServiceThread::QueueSyncRequest(KSession& session, - std::shared_ptr&& context) { - impl->QueueSyncRequest(session, std::move(context)); +void ServiceThread::RegisterServerSession(KServerSession* session, + std::shared_ptr manager) { + impl->RegisterServerSession(session, manager); } } // namespace Kernel diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h index c5896f2..fb43255 100644 --- a/src/core/hle/kernel/service_thread.h +++ b/src/core/hle/kernel/service_thread.h @@ -11,13 +11,15 @@ namespace Kernel { class HLERequestContext; class KernelCore; class KSession; +class SessionRequestManager; class ServiceThread final { public: - explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name); + explicit ServiceThread(KernelCore& kernel, const std::string& name); ~ServiceThread(); - void QueueSyncRequest(KSession& session, std::shared_ptr&& context); + void RegisterServerSession(KServerSession* session, + std::shared_ptr manager); private: class Impl; diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h index 299a981..0228ce1 100644 --- a/src/core/hle/kernel/slab_helpers.h +++ b/src/core/hle/kernel/slab_helpers.h @@ -24,7 +24,7 @@ public: } static Derived* Allocate(KernelCore& kernel) { - return kernel.SlabHeap().Allocate(); + return kernel.SlabHeap().Allocate(kernel); } static void Free(KernelCore& kernel, Derived* obj) { @@ -52,6 +52,84 @@ public: } }; +template +class KAutoObjectWithSlabHeap : public Base { + static_assert(std::is_base_of::value); + +private: + static Derived* Allocate(KernelCore& kernel) { + return kernel.SlabHeap().Allocate(kernel); + } + + static void Free(KernelCore& kernel, Derived* obj) { + kernel.SlabHeap().Free(obj); + } + +public: + explicit KAutoObjectWithSlabHeap(KernelCore& kernel_) : Base(kernel_), kernel(kernel_) {} + virtual ~KAutoObjectWithSlabHeap() = default; + + virtual void Destroy() override { + const bool is_initialized = this->IsInitialized(); + uintptr_t arg = 0; + if (is_initialized) { + arg = this->GetPostDestroyArgument(); + this->Finalize(); + } + Free(kernel, static_cast(this)); + if (is_initialized) { + Derived::PostDestroy(arg); + } + } + + virtual bool IsInitialized() const { + return true; + } + virtual uintptr_t GetPostDestroyArgument() const { + return 0; + } + + size_t GetSlabIndex() const { + return SlabHeap(kernel).GetObjectIndex(static_cast(this)); + } + +public: + static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) { + kernel.SlabHeap().Initialize(memory, memory_size); + } + + static Derived* Create(KernelCore& kernel) { + Derived* obj = Allocate(kernel); + if (obj != nullptr) { + KAutoObject::Create(obj); + } + return obj; + } + + static size_t GetObjectSize(KernelCore& kernel) { + return kernel.SlabHeap().GetObjectSize(); + } + + static size_t GetSlabHeapSize(KernelCore& kernel) { + return kernel.SlabHeap().GetSlabHeapSize(); + } + + static size_t GetPeakIndex(KernelCore& kernel) { + return kernel.SlabHeap().GetPeakIndex(); + } + + static uintptr_t GetSlabHeapAddress(KernelCore& kernel) { + return kernel.SlabHeap().GetSlabHeapAddress(); + } + + static size_t GetNumRemaining(KernelCore& kernel) { + return kernel.SlabHeap().GetNumRemaining(); + } + +protected: + KernelCore& kernel; +}; + template class KAutoObjectWithSlabHeapAndContainer : public Base { static_assert(std::is_base_of::value); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 27e5a80..788ee21 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -24,17 +24,18 @@ #include "core/hle/kernel/k_memory_block.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_page_table.h" +#include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_resource_reservation.h" +#include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/svc.h" @@ -256,6 +257,93 @@ static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u3 return UnmapMemory(system, dst_addr, src_addr, size); } +template +Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) { + auto& process = *system.CurrentProcess(); + auto& handle_table = process.GetHandleTable(); + + // Declare the session we're going to allocate. + T* session; + + // Reserve a new session from the process resource limit. + // FIXME: LimitableResource_SessionCountMax + KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax); + if (session_reservation.Succeeded()) { + session = T::Create(system.Kernel()); + } else { + return ResultLimitReached; + + // // We couldn't reserve a session. Check that we support dynamically expanding the + // // resource limit. + // R_UNLESS(process.GetResourceLimit() == + // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached); + // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached()); + + // // Try to allocate a session from unused slab memory. + // session = T::CreateFromUnusedSlabMemory(); + // R_UNLESS(session != nullptr, ResultLimitReached); + // ON_RESULT_FAILURE { session->Close(); }; + + // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to + // // prevent request exhaustion. + // // NOTE: Nintendo checks if session->DynamicCast() != nullptr, but there's + // // no reason to not do this statically. + // if constexpr (std::same_as) { + // for (size_t i = 0; i < 2; i++) { + // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory(); + // R_UNLESS(request != nullptr, ResultLimitReached); + // request->Close(); + // } + // } + + // We successfully allocated a session, so add the object we allocated to the resource + // limit. + // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1); + } + + // Check that we successfully created a session. + R_UNLESS(session != nullptr, ResultOutOfResource); + + // Initialize the session. + session->Initialize(nullptr, fmt::format("{}", name)); + + // Commit the session reservation. + session_reservation.Commit(); + + // Ensure that we clean up the session (and its only references are handle table) on function + // end. + SCOPE_EXIT({ + session->GetClientSession().Close(); + session->GetServerSession().Close(); + }); + + // Register the session. + T::Register(system.Kernel(), session); + + // Add the server session to the handle table. + R_TRY(handle_table.Add(out_server, &session->GetServerSession())); + + // Add the client session to the handle table. + const auto result = handle_table.Add(out_client, &session->GetClientSession()); + + if (!R_SUCCEEDED(result)) { + // Ensure that we maintaing a clean handle state on exit. + handle_table.Remove(*out_server); + } + + return result; +} + +static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, + u32 is_light, u64 name) { + if (is_light) { + // return CreateSession(system, out_server, out_client, name); + return ResultUnknown; + } else { + return CreateSession(system, out_server, out_client, name); + } +} + /// Connect to an OS service given the port name, returns the handle to the port to out static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { auto& memory = system.Memory(); @@ -296,7 +384,8 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n // Create a session. KClientSession* session{}; R_TRY(port->CreateSession(std::addressof(session))); - port->Close(); + + kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); // Register the session in the table, close the extra reference. handle_table.Register(*out, session); @@ -313,7 +402,7 @@ static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, return ConnectToNamedPort(system, out_handle, port_name_address); } -/// Makes a blocking IPC call to an OS service. +/// Makes a blocking IPC call to a service. static Result SendSyncRequest(Core::System& system, Handle handle) { auto& kernel = system.Kernel(); @@ -327,22 +416,75 @@ static Result SendSyncRequest(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); - { - KScopedSchedulerLock lock(kernel); - - // This is a synchronous request, so we should wait for our request to complete. - GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue)); - GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); - session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming()); - } - - return GetCurrentThread(kernel).GetWaitResult(); + return session->SendSyncRequest(); } static Result SendSyncRequest32(Core::System& system, Handle handle) { return SendSyncRequest(system, handle); } +static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, + s32 num_handles, Handle reply_target, s64 timeout_ns) { + auto& kernel = system.Kernel(); + auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable(); + + // Convert handle list to object table. + std::vector objs(num_handles); + R_UNLESS( + handle_table.GetMultipleObjects(objs.data(), handles, num_handles), + ResultInvalidHandle); + + // Ensure handles are closed when we're done. + SCOPE_EXIT({ + for (auto i = 0; i < num_handles; ++i) { + objs[i]->Close(); + } + }); + + // Reply to the target, if one is specified. + if (reply_target != InvalidHandle) { + KScopedAutoObject session = handle_table.GetObject(reply_target); + R_UNLESS(session.IsNotNull(), ResultInvalidHandle); + + // If we fail to reply, we want to set the output index to -1. + // ON_RESULT_FAILURE { *out_index = -1; }; + + // Send the reply. + // R_TRY(session->SendReply()); + + Result rc = session->SendReply(); + if (!R_SUCCEEDED(rc)) { + *out_index = -1; + return rc; + } + } + + // Wait for a message. + while (true) { + // Wait for an object. + s32 index; + Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(), + static_cast(objs.size()), timeout_ns); + if (result == ResultTimedOut) { + return result; + } + + // Receive the request. + if (R_SUCCEEDED(result)) { + KServerSession* session = objs[index]->DynamicCast(); + if (session != nullptr) { + result = session->ReceiveRequest(); + if (result == ResultNotFound) { + continue; + } + } + } + + *out_index = index; + return result; + } +} + /// Get the ID for the specified thread. static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { // Get the thread from its handle. @@ -514,27 +656,12 @@ static Result ArbitrateUnlock32(Core::System& system, u32 address) { return ArbitrateUnlock(system, address); } -enum class BreakType : u32 { - Panic = 0, - AssertionFailed = 1, - PreNROLoad = 3, - PostNROLoad = 4, - PreNROUnload = 5, - PostNROUnload = 6, - CppException = 7, -}; - -struct BreakReason { - union { - u32 raw; - BitField<0, 30, BreakType> break_type; - BitField<31, 1, u32> signal_debugger; - }; -}; - /// Break program execution static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { - BreakReason break_reason{reason}; + BreakReason break_reason = + static_cast(reason & ~static_cast(BreakReason::NotificationOnlyFlag)); + bool notification_only = (reason & static_cast(BreakReason::NotificationOnlyFlag)) != 0; + bool has_dumped_buffer{}; std::vector debug_buffer; @@ -563,57 +690,56 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { } has_dumped_buffer = true; }; - switch (break_reason.break_type) { - case BreakType::Panic: - LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}", + switch (break_reason) { + case BreakReason::Panic: + LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1, + info2); + handle_debug_buffer(info1, info2); + break; + case BreakReason::Assert: + LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}", info1, info2); handle_debug_buffer(info1, info2); break; - case BreakType::AssertionFailed: - LOG_CRITICAL(Debug_Emulated, - "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}", - info1, info2); + case BreakReason::User: + LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2); handle_debug_buffer(info1, info2); break; - case BreakType::PreNROLoad: - LOG_WARNING( - Debug_Emulated, - "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", - info1, info2); + case BreakReason::PreLoadDll: + LOG_INFO(Debug_Emulated, + "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); break; - case BreakType::PostNROLoad: - LOG_WARNING(Debug_Emulated, - "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, - info2); + case BreakReason::PostLoadDll: + LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); break; - case BreakType::PreNROUnload: - LOG_WARNING( - Debug_Emulated, - "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", - info1, info2); + case BreakReason::PreUnloadDll: + LOG_INFO(Debug_Emulated, + "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); break; - case BreakType::PostNROUnload: - LOG_WARNING(Debug_Emulated, - "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1, - info2); + case BreakReason::PostUnloadDll: + LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}", + info1, info2); break; - case BreakType::CppException: + case BreakReason::CppException: LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); break; default: LOG_WARNING( Debug_Emulated, - "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}", - static_cast(break_reason.break_type.Value()), info1, info2); + "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}", + reason, info1, info2); handle_debug_buffer(info1, info2); break; } - system.GetReporter().SaveSvcBreakReport( - static_cast(break_reason.break_type.Value()), break_reason.signal_debugger, info1, - info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); + system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2, + has_dumped_buffer ? std::make_optional(debug_buffer) + : std::nullopt); - if (!break_reason.signal_debugger) { + if (!notification_only) { LOG_CRITICAL( Debug_Emulated, "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", @@ -658,63 +784,29 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, info_sub_id, handle); - enum class GetInfoType : u64 { - // 1.0.0+ - AllowedCPUCoreMask = 0, - AllowedThreadPriorityMask = 1, - MapRegionBaseAddr = 2, - MapRegionSize = 3, - HeapRegionBaseAddr = 4, - HeapRegionSize = 5, - TotalPhysicalMemoryAvailable = 6, - TotalPhysicalMemoryUsed = 7, - IsCurrentProcessBeingDebugged = 8, - RegisterResourceLimit = 9, - IdleTickCount = 10, - RandomEntropy = 11, - ThreadTickCount = 0xF0000002, - // 2.0.0+ - ASLRRegionBaseAddr = 12, - ASLRRegionSize = 13, - StackRegionBaseAddr = 14, - StackRegionSize = 15, - // 3.0.0+ - SystemResourceSize = 16, - SystemResourceUsage = 17, - TitleId = 18, - // 4.0.0+ - PrivilegedProcessId = 19, - // 5.0.0+ - UserExceptionContextAddr = 20, - // 6.0.0+ - TotalPhysicalMemoryAvailableWithoutSystemResource = 21, - TotalPhysicalMemoryUsedWithoutSystemResource = 22, - - // Homebrew only - MesosphereCurrentProcess = 65001, - }; - - const auto info_id_type = static_cast(info_id); + const auto info_id_type = static_cast(info_id); switch (info_id_type) { - case GetInfoType::AllowedCPUCoreMask: - case GetInfoType::AllowedThreadPriorityMask: - case GetInfoType::MapRegionBaseAddr: - case GetInfoType::MapRegionSize: - case GetInfoType::HeapRegionBaseAddr: - case GetInfoType::HeapRegionSize: - case GetInfoType::ASLRRegionBaseAddr: - case GetInfoType::ASLRRegionSize: - case GetInfoType::StackRegionBaseAddr: - case GetInfoType::StackRegionSize: - case GetInfoType::TotalPhysicalMemoryAvailable: - case GetInfoType::TotalPhysicalMemoryUsed: - case GetInfoType::SystemResourceSize: - case GetInfoType::SystemResourceUsage: - case GetInfoType::TitleId: - case GetInfoType::UserExceptionContextAddr: - case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: - case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: { + case InfoType::CoreMask: + case InfoType::PriorityMask: + case InfoType::AliasRegionAddress: + case InfoType::AliasRegionSize: + case InfoType::HeapRegionAddress: + case InfoType::HeapRegionSize: + case InfoType::AslrRegionAddress: + case InfoType::AslrRegionSize: + case InfoType::StackRegionAddress: + case InfoType::StackRegionSize: + case InfoType::TotalMemorySize: + case InfoType::UsedMemorySize: + case InfoType::SystemResourceSizeTotal: + case InfoType::SystemResourceSizeUsed: + case InfoType::ProgramId: + case InfoType::UserExceptionContextAddress: + case InfoType::TotalNonSystemMemorySize: + case InfoType::UsedNonSystemMemorySize: + case InfoType::IsApplication: + case InfoType::FreeThreadCount: { if (info_sub_id != 0) { LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, info_sub_id); @@ -730,79 +822,83 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han } switch (info_id_type) { - case GetInfoType::AllowedCPUCoreMask: + case InfoType::CoreMask: *result = process->GetCoreMask(); return ResultSuccess; - case GetInfoType::AllowedThreadPriorityMask: + case InfoType::PriorityMask: *result = process->GetPriorityMask(); return ResultSuccess; - case GetInfoType::MapRegionBaseAddr: + case InfoType::AliasRegionAddress: *result = process->PageTable().GetAliasRegionStart(); return ResultSuccess; - case GetInfoType::MapRegionSize: + case InfoType::AliasRegionSize: *result = process->PageTable().GetAliasRegionSize(); return ResultSuccess; - case GetInfoType::HeapRegionBaseAddr: + case InfoType::HeapRegionAddress: *result = process->PageTable().GetHeapRegionStart(); return ResultSuccess; - case GetInfoType::HeapRegionSize: + case InfoType::HeapRegionSize: *result = process->PageTable().GetHeapRegionSize(); return ResultSuccess; - case GetInfoType::ASLRRegionBaseAddr: + case InfoType::AslrRegionAddress: *result = process->PageTable().GetAliasCodeRegionStart(); return ResultSuccess; - case GetInfoType::ASLRRegionSize: + case InfoType::AslrRegionSize: *result = process->PageTable().GetAliasCodeRegionSize(); return ResultSuccess; - case GetInfoType::StackRegionBaseAddr: + case InfoType::StackRegionAddress: *result = process->PageTable().GetStackRegionStart(); return ResultSuccess; - case GetInfoType::StackRegionSize: + case InfoType::StackRegionSize: *result = process->PageTable().GetStackRegionSize(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryAvailable: + case InfoType::TotalMemorySize: *result = process->GetTotalPhysicalMemoryAvailable(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryUsed: + case InfoType::UsedMemorySize: *result = process->GetTotalPhysicalMemoryUsed(); return ResultSuccess; - case GetInfoType::SystemResourceSize: + case InfoType::SystemResourceSizeTotal: *result = process->GetSystemResourceSize(); return ResultSuccess; - case GetInfoType::SystemResourceUsage: + case InfoType::SystemResourceSizeUsed: LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); *result = process->GetSystemResourceUsage(); return ResultSuccess; - case GetInfoType::TitleId: + case InfoType::ProgramId: *result = process->GetProgramID(); return ResultSuccess; - case GetInfoType::UserExceptionContextAddr: - *result = process->GetTLSRegionAddress(); + case InfoType::UserExceptionContextAddress: + *result = process->GetProcessLocalRegionAddress(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: + case InfoType::TotalNonSystemMemorySize: *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: + case InfoType::UsedNonSystemMemorySize: *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); return ResultSuccess; + case InfoType::FreeThreadCount: + *result = process->GetFreeThreadCount(); + return ResultSuccess; + default: break; } @@ -811,11 +907,11 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han return ResultInvalidEnumValue; } - case GetInfoType::IsCurrentProcessBeingDebugged: + case InfoType::DebuggerAttached: *result = 0; return ResultSuccess; - case GetInfoType::RegisterResourceLimit: { + case InfoType::ResourceLimit: { if (handle != 0) { LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle); return ResultInvalidHandle; @@ -843,7 +939,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han return ResultSuccess; } - case GetInfoType::RandomEntropy: + case InfoType::RandomEntropy: if (handle != 0) { LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", handle); @@ -859,13 +955,13 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); return ResultSuccess; - case GetInfoType::PrivilegedProcessId: + case InfoType::InitialProcessIdRange: LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query privileged process id bounds, returned 0"); *result = 0; return ResultSuccess; - case GetInfoType::ThreadTickCount: { + case InfoType::ThreadTickCount: { constexpr u64 num_cpus = 4; if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, @@ -900,7 +996,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han *result = out_ticks; return ResultSuccess; } - case GetInfoType::IdleTickCount: { + case InfoType::IdleTickCount: { // Verify the input handle is invalid. R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); @@ -914,7 +1010,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); return ResultSuccess; } - case GetInfoType::MesosphereCurrentProcess: { + case InfoType::MesosphereCurrentProcess: { // Verify the input handle is invalid. R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); @@ -1574,13 +1670,13 @@ static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address auto& memory{system.Memory()}; const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; - memory.Write64(memory_info_address + 0x00, memory_info.addr); + memory.Write64(memory_info_address + 0x00, memory_info.base_address); memory.Write64(memory_info_address + 0x08, memory_info.size); memory.Write32(memory_info_address + 0x10, static_cast(memory_info.state) & 0xff); - memory.Write32(memory_info_address + 0x14, static_cast(memory_info.attr)); - memory.Write32(memory_info_address + 0x18, static_cast(memory_info.perm)); - memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount); - memory.Write32(memory_info_address + 0x20, memory_info.device_refcount); + memory.Write32(memory_info_address + 0x14, static_cast(memory_info.attribute)); + memory.Write32(memory_info_address + 0x18, static_cast(memory_info.permission)); + memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count); + memory.Write32(memory_info_address + 0x20, memory_info.device_count); memory.Write32(memory_info_address + 0x24, 0); // Page info appears to be currently unused by the kernel and is always set to zero. @@ -1747,7 +1843,7 @@ static void ExitProcess(Core::System& system) { auto* current_process = system.Kernel().CurrentProcess(); LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); - ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, + ASSERT_MSG(current_process->GetState() == KProcess::State::Running, "Process has already exited"); system.Exit(); @@ -1801,7 +1897,7 @@ static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry // Reserve a new thread from the process resource limit (waiting up to 100ms). KScopedResourceReservation thread_reservation( - kernel.CurrentProcess(), LimitableResource::Threads, 1, + kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1, system.CoreTiming().GetGlobalTimeNs().count() + 100000000); if (!thread_reservation.Succeeded()) { LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); @@ -2105,7 +2201,7 @@ static u64 GetSystemTick(Core::System& system) { auto& core_timing = system.CoreTiming(); // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) - const u64 result{system.CoreTiming().GetClockTicks()}; + const u64 result{core_timing.GetClockTicks()}; if (!system.Kernel().IsMulticore()) { core_timing.AddTicks(400U); @@ -2202,7 +2298,7 @@ static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr addr // Reserve a new transfer memory from the process resource limit. KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), - LimitableResource::TransferMemory); + LimitableResource::TransferMemoryCountMax); R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached); // Create the transfer memory. @@ -2303,11 +2399,11 @@ static Result SignalEvent(Core::System& system, Handle event_handle) { // Get the current handle table. const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - // Get the writable event. - KScopedAutoObject writable_event = handle_table.GetObject(event_handle); - R_UNLESS(writable_event.IsNotNull(), ResultInvalidHandle); + // Get the event. + KScopedAutoObject event = handle_table.GetObject(event_handle); + R_UNLESS(event.IsNotNull(), ResultInvalidHandle); - return writable_event->Signal(); + return event->Signal(); } static Result SignalEvent32(Core::System& system, Handle event_handle) { @@ -2322,9 +2418,9 @@ static Result ClearEvent(Core::System& system, Handle event_handle) { // Try to clear the writable event. { - KScopedAutoObject writable_event = handle_table.GetObject(event_handle); - if (writable_event.IsNotNull()) { - return writable_event->Clear(); + KScopedAutoObject event = handle_table.GetObject(event_handle); + if (event.IsNotNull()) { + return event->Clear(); } } @@ -2354,7 +2450,7 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r // Reserve a new event from the process resource limit KScopedResourceReservation event_reservation(kernel.CurrentProcess(), - LimitableResource::Events); + LimitableResource::EventCountMax); R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); // Create a new event. @@ -2362,24 +2458,24 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r R_UNLESS(event != nullptr, ResultOutOfResource); // Initialize the event. - event->Initialize("CreateEvent", kernel.CurrentProcess()); + event->Initialize(kernel.CurrentProcess()); // Commit the thread reservation. event_reservation.Commit(); // Ensure that we clean up the event (and its only references are handle table) on function end. SCOPE_EXIT({ - event->GetWritableEvent().Close(); event->GetReadableEvent().Close(); + event->Close(); }); // Register the event. KEvent::Register(kernel, event); - // Add the writable event to the handle table. - R_TRY(handle_table.Add(out_write, std::addressof(event->GetWritableEvent()))); + // Add the event to the handle table. + R_TRY(handle_table.Add(out_write, event)); - // Add the writable event to the handle table. + // Ensure that we maintaing a clean handle state on exit. auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); }); // Add the readable event to the handle table. @@ -2397,11 +2493,6 @@ static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); - // This function currently only allows retrieving a process' status. - enum class InfoType { - Status, - }; - const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); KScopedAutoObject process = handle_table.GetObject(process_handle); if (process.IsNull()) { @@ -2410,13 +2501,13 @@ static Result GetProcessInfo(Core::System& system, u64* out, Handle process_hand return ResultInvalidHandle; } - const auto info_type = static_cast(type); - if (info_type != InfoType::Status) { - LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type); + const auto info_type = static_cast(type); + if (info_type != ProcessInfoType::ProcessState) { + LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type); return ResultInvalidEnumValue; } - *out = static_cast(process->GetStatus()); + *out = static_cast(process->GetState()); return ResultSuccess; } @@ -2580,14 +2671,24 @@ static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr ou return ResultSuccess; } -static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system, - [[maybe_unused]] Handle handle, [[maybe_unused]] u32 address, - [[maybe_unused]] u32 size) { - // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op, - // as all emulation is done in the same cache level in host architecture, thus data cache - // does not need flushing. - LOG_DEBUG(Kernel_SVC, "called"); - return ResultSuccess; +static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, + u64 size) { + // Validate address/size. + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS(address == static_cast(address), ResultInvalidCurrentMemory); + R_UNLESS(size == static_cast(size), ResultInvalidCurrentMemory); + + // Get the process from its handle. + KScopedAutoObject process = + system.Kernel().CurrentProcess()->GetHandleTable().GetObject(process_handle); + R_UNLESS(process.IsNotNull(), ResultInvalidHandle); + + // Verify the region is within range. + auto& page_table = process->PageTable(); + R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); + + // Perform the operation. + R_RETURN(system.Memory().FlushDataCache(*process, address, size)); } namespace { @@ -2860,10 +2961,10 @@ static const FunctionDef SVC_Table_64[] = { {0x3D, SvcWrap64, "ChangeKernelTraceState"}, {0x3E, nullptr, "Unknown3e"}, {0x3F, nullptr, "Unknown3f"}, - {0x40, nullptr, "CreateSession"}, + {0x40, SvcWrap64, "CreateSession"}, {0x41, nullptr, "AcceptSession"}, {0x42, nullptr, "ReplyAndReceiveLight"}, - {0x43, nullptr, "ReplyAndReceive"}, + {0x43, SvcWrap64, "ReplyAndReceive"}, {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, {0x45, SvcWrap64, "CreateEvent"}, {0x46, nullptr, "MapIoRegion"}, diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h index 95750c3..8550671 100644 --- a/src/core/hle/kernel/svc_common.h +++ b/src/core/hle/kernel/svc_common.h @@ -14,8 +14,11 @@ namespace Kernel::Svc { using namespace Common::Literals; -constexpr s32 ArgumentHandleCountMax = 0x40; -constexpr u32 HandleWaitMask{1u << 30}; +constexpr inline s32 ArgumentHandleCountMax = 0x40; + +constexpr inline u32 HandleWaitMask = 1u << 30; + +constexpr inline s64 WaitInfinite = -1; constexpr inline std::size_t HeapSizeAlignment = 2_MiB; diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h index f27cade..b7ca530 100644 --- a/src/core/hle/kernel/svc_results.h +++ b/src/core/hle/kernel/svc_results.h @@ -37,6 +37,7 @@ constexpr Result ResultInvalidState{ErrorModule::Kernel, 125}; constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126}; constexpr Result ResultPortClosed{ErrorModule::Kernel, 131}; constexpr Result ResultLimitReached{ErrorModule::Kernel, 132}; +constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259}; constexpr Result ResultInvalidId{ErrorModule::Kernel, 519}; } // namespace Kernel diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 79e1518..33eebce 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -8,6 +8,8 @@ namespace Kernel::Svc { +using Handle = u32; + enum class MemoryState : u32 { Free = 0x00, Io = 0x01, @@ -22,8 +24,8 @@ enum class MemoryState : u32 { Ipc = 0x0A, Stack = 0x0B, ThreadLocal = 0x0C, - Transferred = 0x0D, - SharedTransferred = 0x0E, + Transfered = 0x0D, + SharedTransfered = 0x0E, SharedCode = 0x0F, Inaccessible = 0x10, NonSecureIpc = 0x11, @@ -32,6 +34,7 @@ enum class MemoryState : u32 { GeneratedCode = 0x14, CodeOut = 0x15, Coverage = 0x16, + Insecure = 0x17, }; DECLARE_ENUM_FLAG_OPERATORS(MemoryState); @@ -54,17 +57,6 @@ enum class MemoryPermission : u32 { }; DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission); -struct MemoryInfo { - u64 addr{}; - u64 size{}; - MemoryState state{}; - MemoryAttribute attr{}; - MemoryPermission perm{}; - u32 ipc_refcount{}; - u32 device_refcount{}; - u32 padding{}; -}; - enum class SignalType : u32 { Signal = 0, SignalAndIncrementIfEqual = 1, @@ -83,6 +75,13 @@ enum class YieldType : s64 { ToAnyThread = -2, }; +enum class ThreadExitReason : u32 { + ExitThread = 0, + TerminateThread = 1, + ExitProcess = 2, + TerminateProcess = 3, +}; + enum class ThreadActivity : u32 { Runnable = 0, Paused = 1, @@ -95,6 +94,502 @@ constexpr inline s32 IdealCoreNoUpdate = -3; constexpr inline s32 LowestThreadPriority = 63; constexpr inline s32 HighestThreadPriority = 0; +constexpr inline s32 SystemThreadPriorityHighest = 16; + +enum class ProcessState : u32 { + Created = 0, + CreatedAttached = 1, + Running = 2, + Crashed = 3, + RunningAttached = 4, + Terminating = 5, + Terminated = 6, + DebugBreak = 7, +}; + +enum class ProcessExitReason : u32 { + ExitProcess = 0, + TerminateProcess = 1, + Exception = 2, +}; + constexpr inline size_t ThreadLocalRegionSize = 0x200; +struct PageInfo { + u32 flags; +}; + +// Info Types. +enum class InfoType : u32 { + CoreMask = 0, + PriorityMask = 1, + AliasRegionAddress = 2, + AliasRegionSize = 3, + HeapRegionAddress = 4, + HeapRegionSize = 5, + TotalMemorySize = 6, + UsedMemorySize = 7, + DebuggerAttached = 8, + ResourceLimit = 9, + IdleTickCount = 10, + RandomEntropy = 11, + AslrRegionAddress = 12, + AslrRegionSize = 13, + StackRegionAddress = 14, + StackRegionSize = 15, + SystemResourceSizeTotal = 16, + SystemResourceSizeUsed = 17, + ProgramId = 18, + InitialProcessIdRange = 19, + UserExceptionContextAddress = 20, + TotalNonSystemMemorySize = 21, + UsedNonSystemMemorySize = 22, + IsApplication = 23, + FreeThreadCount = 24, + ThreadTickCount = 25, + IsSvcPermitted = 26, + + MesosphereMeta = 65000, + MesosphereCurrentProcess = 65001, +}; + +enum class BreakReason : u32 { + Panic = 0, + Assert = 1, + User = 2, + PreLoadDll = 3, + PostLoadDll = 4, + PreUnloadDll = 5, + PostUnloadDll = 6, + CppException = 7, + + NotificationOnlyFlag = 0x80000000, +}; + +enum class DebugEvent : u32 { + CreateProcess = 0, + CreateThread = 1, + ExitProcess = 2, + ExitThread = 3, + Exception = 4, +}; + +enum class DebugThreadParam : u32 { + Priority = 0, + State = 1, + IdealCore = 2, + CurrentCore = 3, + AffinityMask = 4, +}; + +enum class DebugException : u32 { + UndefinedInstruction = 0, + InstructionAbort = 1, + DataAbort = 2, + AlignmentFault = 3, + DebuggerAttached = 4, + BreakPoint = 5, + UserBreak = 6, + DebuggerBreak = 7, + UndefinedSystemCall = 8, + MemorySystemError = 9, +}; + +enum class DebugEventFlag : u32 { + Stopped = (1u << 0), +}; + +enum class BreakPointType : u32 { + HardwareInstruction = 0, + HardwareData = 1, +}; + +enum class HardwareBreakPointRegisterName : u32 { + I0 = 0, + I1 = 1, + I2 = 2, + I3 = 3, + I4 = 4, + I5 = 5, + I6 = 6, + I7 = 7, + I8 = 8, + I9 = 9, + I10 = 10, + I11 = 11, + I12 = 12, + I13 = 13, + I14 = 14, + I15 = 15, + D0 = 16, + D1 = 17, + D2 = 18, + D3 = 19, + D4 = 20, + D5 = 21, + D6 = 22, + D7 = 23, + D8 = 24, + D9 = 25, + D10 = 26, + D11 = 27, + D12 = 28, + D13 = 29, + D14 = 30, + D15 = 31, +}; + +namespace lp64 { +struct LastThreadContext { + u64 fp; + u64 sp; + u64 lr; + u64 pc; +}; + +struct PhysicalMemoryInfo { + PAddr physical_address; + u64 virtual_address; + u64 size; +}; + +struct DebugInfoCreateProcess { + u64 program_id; + u64 process_id; + std::array name; + u32 flags; + u64 user_exception_context_address; // 5.0.0+ +}; + +struct DebugInfoCreateThread { + u64 thread_id; + u64 tls_address; + // Removed in 11.0.0 u64 entrypoint; +}; + +struct DebugInfoExitProcess { + ProcessExitReason reason; +}; + +struct DebugInfoExitThread { + ThreadExitReason reason; +}; + +struct DebugInfoUndefinedInstructionException { + u32 insn; +}; + +struct DebugInfoDataAbortException { + u64 address; +}; + +struct DebugInfoAlignmentFaultException { + u64 address; +}; + +struct DebugInfoBreakPointException { + BreakPointType type; + u64 address; +}; + +struct DebugInfoUserBreakException { + BreakReason break_reason; + u64 address; + u64 size; +}; + +struct DebugInfoDebuggerBreakException { + std::array active_thread_ids; +}; + +struct DebugInfoUndefinedSystemCallException { + u32 id; +}; + +union DebugInfoSpecificException { + DebugInfoUndefinedInstructionException undefined_instruction; + DebugInfoDataAbortException data_abort; + DebugInfoAlignmentFaultException alignment_fault; + DebugInfoBreakPointException break_point; + DebugInfoUserBreakException user_break; + DebugInfoDebuggerBreakException debugger_break; + DebugInfoUndefinedSystemCallException undefined_system_call; + u64 raw; +}; + +struct DebugInfoException { + DebugException type; + u64 address; + DebugInfoSpecificException specific; +}; + +union DebugInfo { + DebugInfoCreateProcess create_process; + DebugInfoCreateThread create_thread; + DebugInfoExitProcess exit_process; + DebugInfoExitThread exit_thread; + DebugInfoException exception; +}; + +struct DebugEventInfo { + DebugEvent type; + u32 flags; + u64 thread_id; + DebugInfo info; +}; +static_assert(sizeof(DebugEventInfo) >= 0x40); + +struct SecureMonitorArguments { + std::array r; +}; +static_assert(sizeof(SecureMonitorArguments) == 0x40); +} // namespace lp64 + +namespace ilp32 { +struct LastThreadContext { + u32 fp; + u32 sp; + u32 lr; + u32 pc; +}; + +struct PhysicalMemoryInfo { + PAddr physical_address; + u32 virtual_address; + u32 size; +}; + +struct DebugInfoCreateProcess { + u64 program_id; + u64 process_id; + std::array name; + u32 flags; + u32 user_exception_context_address; // 5.0.0+ +}; + +struct DebugInfoCreateThread { + u64 thread_id; + u32 tls_address; + // Removed in 11.0.0 u32 entrypoint; +}; + +struct DebugInfoExitProcess { + ProcessExitReason reason; +}; + +struct DebugInfoExitThread { + ThreadExitReason reason; +}; + +struct DebugInfoUndefinedInstructionException { + u32 insn; +}; + +struct DebugInfoDataAbortException { + u32 address; +}; + +struct DebugInfoAlignmentFaultException { + u32 address; +}; + +struct DebugInfoBreakPointException { + BreakPointType type; + u32 address; +}; + +struct DebugInfoUserBreakException { + BreakReason break_reason; + u32 address; + u32 size; +}; + +struct DebugInfoDebuggerBreakException { + std::array active_thread_ids; +}; + +struct DebugInfoUndefinedSystemCallException { + u32 id; +}; + +union DebugInfoSpecificException { + DebugInfoUndefinedInstructionException undefined_instruction; + DebugInfoDataAbortException data_abort; + DebugInfoAlignmentFaultException alignment_fault; + DebugInfoBreakPointException break_point; + DebugInfoUserBreakException user_break; + DebugInfoDebuggerBreakException debugger_break; + DebugInfoUndefinedSystemCallException undefined_system_call; + u64 raw; +}; + +struct DebugInfoException { + DebugException type; + u32 address; + DebugInfoSpecificException specific; +}; + +union DebugInfo { + DebugInfoCreateProcess create_process; + DebugInfoCreateThread create_thread; + DebugInfoExitProcess exit_process; + DebugInfoExitThread exit_thread; + DebugInfoException exception; +}; + +struct DebugEventInfo { + DebugEvent type; + u32 flags; + u64 thread_id; + DebugInfo info; +}; + +struct SecureMonitorArguments { + std::array r; +}; +static_assert(sizeof(SecureMonitorArguments) == 0x20); +} // namespace ilp32 + +struct ThreadContext { + std::array r; + u64 fp; + u64 lr; + u64 sp; + u64 pc; + u32 pstate; + u32 padding; + std::array v; + u32 fpcr; + u32 fpsr; + u64 tpidr; +}; +static_assert(sizeof(ThreadContext) == 0x320); + +struct MemoryInfo { + u64 base_address; + u64 size; + MemoryState state; + MemoryAttribute attribute; + MemoryPermission permission; + u32 ipc_count; + u32 device_count; + u32 padding; +}; + +enum class LimitableResource : u32 { + PhysicalMemoryMax = 0, + ThreadCountMax = 1, + EventCountMax = 2, + TransferMemoryCountMax = 3, + SessionCountMax = 4, + Count, +}; + +enum class IoPoolType : u32 { + // Not supported. + Count = 0, +}; + +enum class MemoryMapping : u32 { + IoRegister = 0, + Uncached = 1, + Memory = 2, +}; + +enum class KernelDebugType : u32 { + Thread = 0, + ThreadCallStack = 1, + KernelObject = 2, + Handle_ = 3, + Memory = 4, + PageTable = 5, + CpuUtilization = 6, + Process = 7, + SuspendProcess = 8, + ResumeProcess = 9, + Port = 10, +}; + +enum class KernelTraceState : u32 { + Disabled = 0, + Enabled = 1, +}; + +enum class CodeMemoryOperation : u32 { + Map = 0, + MapToOwner = 1, + Unmap = 2, + UnmapFromOwner = 3, +}; + +enum class InterruptType : u32 { + Edge = 0, + Level = 1, +}; + +enum class DeviceName { + Afi = 0, + Avpc = 1, + Dc = 2, + Dcb = 3, + Hc = 4, + Hda = 5, + Isp2 = 6, + MsencNvenc = 7, + Nv = 8, + Nv2 = 9, + Ppcs = 10, + Sata = 11, + Vi = 12, + Vic = 13, + XusbHost = 14, + XusbDev = 15, + Tsec = 16, + Ppcs1 = 17, + Dc1 = 18, + Sdmmc1a = 19, + Sdmmc2a = 20, + Sdmmc3a = 21, + Sdmmc4a = 22, + Isp2b = 23, + Gpu = 24, + Gpub = 25, + Ppcs2 = 26, + Nvdec = 27, + Ape = 28, + Se = 29, + Nvjpg = 30, + Hc1 = 31, + Se1 = 32, + Axiap = 33, + Etr = 34, + Tsecb = 35, + Tsec1 = 36, + Tsecb1 = 37, + Nvdec1 = 38, + Count, +}; + +enum class SystemInfoType : u32 { + TotalPhysicalMemorySize = 0, + UsedPhysicalMemorySize = 1, + InitialProcessIdRange = 2, +}; + +enum class ProcessInfoType : u32 { + ProcessState = 0, +}; + +struct CreateProcessParameter { + std::array name; + u32 version; + u64 program_id; + u64 code_address; + s32 code_num_pages; + u32 flags; + Handle reslimit; + s32 system_resource_num_pages; +}; +static_assert(sizeof(CreateProcessParameter) == 0x30); + } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 4bc4908..1ea8c7f 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -8,6 +8,7 @@ #include "core/core.h" #include "core/hle/kernel/svc_types.h" #include "core/hle/result.h" +#include "core/memory.h" namespace Kernel { @@ -81,7 +82,7 @@ void SvcWrap64(Core::System& system) { } // Used by ControlCodeMemory -template +template void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast(Param(system, 0)), static_cast(Param(system, 1)), Param(system, 2), Param(system, 3), @@ -326,7 +327,7 @@ void SvcWrap64(Core::System& system) { } // Used by CreateCodeMemory -template +template void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2)).raw; @@ -346,6 +347,37 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } +// Used by CreateSession +template +void SvcWrap64(Core::System& system) { + Handle param_1 = 0; + Handle param_2 = 0; + const u32 retval = func(system, ¶m_1, ¶m_2, static_cast(Param(system, 2)), + static_cast(Param(system, 3))) + .raw; + + system.CurrentArmInterface().SetReg(1, param_1); + system.CurrentArmInterface().SetReg(2, param_2); + FuncReturn(system, retval); +} + +// Used by ReplyAndReceive +template +void SvcWrap64(Core::System& system) { + s32 param_1 = 0; + s32 num_handles = static_cast(Param(system, 2)); + + std::vector handles(num_handles); + system.Memory().ReadBlock(Param(system, 1), handles.data(), num_handles * sizeof(Handle)); + + const u32 retval = func(system, ¶m_1, handles.data(), num_handles, + static_cast(Param(system, 3)), static_cast(Param(system, 4))) + .raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); +} + // Used by WaitForAddress template void SvcWrap64(Core::System& system) { @@ -690,4 +722,12 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } +// Used by Invalidate/Store/FlushProcessDataCache32 +template +void SvcWrap32(Core::System& system) { + const u64 address = (Param(system, 3) << 32) | Param(system, 2); + const u64 size = (Param(system, 4) << 32) | Param(system, 1); + FuncReturn32(system, func(system, Param32(system, 0), address, size).raw); +} + } // namespace Kernel diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 47a1b82..240f066 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -5,6 +5,7 @@ #include "common/assert.h" #include "common/bit_field.h" +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/expected.h" @@ -27,30 +28,49 @@ enum class ErrorModule : u32 { Loader = 9, CMIF = 10, HIPC = 11, + TMA = 12, + DMNT = 13, + GDS = 14, PM = 15, NS = 16, + BSDSockets = 17, HTC = 18, + TSC = 19, NCMContent = 20, SM = 21, RO = 22, + GC = 23, SDMMC = 24, OVLN = 25, SPL = 26, + Socket = 27, + HTCLOW = 29, + DDSF = 30, + HTCFS = 31, + Async = 32, + Util = 33, + TIPC = 35, + ANIF = 37, ETHC = 100, I2C = 101, GPIO = 102, UART = 103, + CPAD = 104, Settings = 105, + FTM = 106, WLAN = 107, XCD = 108, + TMP451 = 109, NIFM = 110, Hwopus = 111, + LSM6DS3 = 112, Bluetooth = 113, VI = 114, NFP = 115, Time = 116, FGM = 117, OE = 118, + BH1730FVC = 119, PCIe = 120, Friends = 121, BCAT = 122, @@ -64,7 +84,7 @@ enum class ErrorModule : u32 { AHID = 130, Qlaunch = 132, PCV = 133, - OMM = 134, + USBPD = 134, BPC = 135, PSM = 136, NIM = 137, @@ -74,18 +94,22 @@ enum class ErrorModule : u32 { NSD = 141, PCTL = 142, BTM = 143, + LA = 144, ETicket = 145, NGC = 146, ERPT = 147, APM = 148, + CEC = 149, Profiler = 150, ErrorUpload = 151, + LIDBE = 152, Audio = 153, NPNS = 154, NPNSHTTPSTREAM = 155, ARP = 157, SWKBD = 158, BOOT = 159, + NetDiag = 160, NFCMifare = 161, UserlandAssert = 162, Fatal = 163, @@ -93,17 +117,68 @@ enum class ErrorModule : u32 { SPSM = 165, BGTC = 167, UserlandCrash = 168, + SASBUS = 169, + PI = 170, + AudioCtrl = 172, + LBL = 173, + JIT = 175, + HDCP = 176, + OMM = 177, + PDM = 178, + OLSC = 179, SREPO = 180, Dauth = 181, + STDFU = 182, + DBG = 183, + DHCPS = 186, + SPI = 187, + AVM = 188, + PWM = 189, + RTC = 191, + Regulator = 192, + LED = 193, + SIO = 195, + PCM = 196, + CLKRST = 197, + POWCTL = 198, + AudioOld = 201, HID = 202, LDN = 203, + CS = 204, Irsensor = 205, Capture = 206, Manu = 208, ATK = 209, + WEB = 210, + LCS = 211, GRC = 212, + Repair = 213, + Album = 214, + RID = 215, Migration = 216, MigrationLdcServ = 217, + HIDBUS = 218, + ENS = 219, + WebSocket = 223, + DCDMTP = 227, + PGL = 228, + Notification = 229, + INS = 230, + LP2P = 231, + RCD = 232, + LCM40607 = 233, + PRC = 235, + TMAHTC = 237, + ECTX = 238, + MNPP = 239, + HSHL = 240, + CAPMTP = 242, + DP2HDMI = 244, + Cradle = 245, + SProfile = 246, + NDRM = 250, + TSPM = 499, + DevMenu = 500, GeneralWebApplet = 800, WifiWebAuthApplet = 809, WhitelistedApplet = 810, @@ -130,6 +205,18 @@ union Result { [[nodiscard]] constexpr bool IsError() const { return !IsSuccess(); } + + [[nodiscard]] constexpr bool IsFailure() const { + return !IsSuccess(); + } + + [[nodiscard]] constexpr u32 GetInnerValue() const { + return static_cast(module.Value()) | (description << module.bits); + } + + [[nodiscard]] constexpr bool Includes(Result result) const { + return GetInnerValue() == result.GetInnerValue(); + } }; static_assert(std::is_trivial_v); @@ -349,19 +436,116 @@ private: } \ } while (false) -#define R_SUCCEEDED(res) (res.IsSuccess()) +#define R_SUCCEEDED(res) (static_cast(res).IsSuccess()) +#define R_FAILED(res) (static_cast(res).IsFailure()) -/// Evaluates a boolean expression, and succeeds if that expression is true. -#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) +namespace ResultImpl { +template +class ScopedResultGuard { + YUZU_NON_COPYABLE(ScopedResultGuard); + YUZU_NON_MOVEABLE(ScopedResultGuard); + +private: + Result& m_ref; + F m_f; + +public: + constexpr ScopedResultGuard(Result& ref, F f) : m_ref(ref), m_f(std::move(f)) {} + constexpr ~ScopedResultGuard() { + if (EvaluateResult(m_ref)) { + m_f(); + } + } +}; + +template +class ResultReferenceForScopedResultGuard { +private: + Result& m_ref; + +public: + constexpr ResultReferenceForScopedResultGuard(Result& r) : m_ref(r) {} + constexpr operator Result&() const { + return m_ref; + } +}; + +template +constexpr ScopedResultGuard operator+( + ResultReferenceForScopedResultGuard ref, F&& f) { + return ScopedResultGuard(static_cast(ref), std::forward(f)); +} + +constexpr bool EvaluateResultSuccess(const Result& r) { + return R_SUCCEEDED(r); +} +constexpr bool EvaluateResultFailure(const Result& r) { + return R_FAILED(r); +} + +template +constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete; +// Intentionally not defined + +template <> +constexpr void UpdateCurrentResultReference(Result& result_reference, Result result) { + result_reference = result; +} + +template <> +constexpr void UpdateCurrentResultReference(Result result_reference, Result result) {} +} // namespace ResultImpl + +#define DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(COUNTER_VALUE) \ + [[maybe_unused]] constexpr bool CONCAT2(HasPrevRef_, COUNTER_VALUE) = \ + std::same_as; \ + [[maybe_unused]] Result CONCAT2(PrevRef_, COUNTER_VALUE) = __TmpCurrentResultReference; \ + [[maybe_unused]] Result CONCAT2(__tmp_result_, COUNTER_VALUE) = ResultSuccess; \ + Result& __TmpCurrentResultReference = CONCAT2(HasPrevRef_, COUNTER_VALUE) \ + ? CONCAT2(PrevRef_, COUNTER_VALUE) \ + : CONCAT2(__tmp_result_, COUNTER_VALUE) + +#define ON_RESULT_RETURN_IMPL(...) \ + static_assert(std::same_as); \ + auto CONCAT2(RESULT_GUARD_STATE_, __COUNTER__) = \ + ResultImpl::ResultReferenceForScopedResultGuard<__VA_ARGS__>( \ + __TmpCurrentResultReference) + \ + [&]() + +#define ON_RESULT_FAILURE_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultFailure) + +#define ON_RESULT_FAILURE \ + DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \ + ON_RESULT_FAILURE_2 + +#define ON_RESULT_SUCCESS_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultSuccess) + +#define ON_RESULT_SUCCESS \ + DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \ + ON_RESULT_SUCCESS_2 + +constexpr inline Result __TmpCurrentResultReference = ResultSuccess; + +/// Returns a result. +#define R_RETURN(res_expr) \ + { \ + const Result _tmp_r_throw_rc = (res_expr); \ + ResultImpl::UpdateCurrentResultReference( \ + __TmpCurrentResultReference, _tmp_r_throw_rc); \ + return _tmp_r_throw_rc; \ + } + +/// Returns ResultSuccess() +#define R_SUCCEED() R_RETURN(ResultSuccess) + +/// Throws a result. +#define R_THROW(res_expr) R_RETURN(res_expr) /// Evaluates a boolean expression, and returns a result unless that expression is true. #define R_UNLESS(expr, res) \ { \ if (!(expr)) { \ - if (res.IsError()) { \ - LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \ - } \ - return res; \ + R_THROW(res); \ } \ } @@ -369,7 +553,10 @@ private: #define R_TRY(res_expr) \ { \ const auto _tmp_r_try_rc = (res_expr); \ - if (_tmp_r_try_rc.IsError()) { \ - return _tmp_r_try_rc; \ + if (R_FAILED(_tmp_r_try_rc)) { \ + R_THROW(_tmp_r_try_rc); \ } \ } + +/// Evaluates a boolean expression, and succeeds if that expression is true. +#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index bb838e2..6d1084f 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -7,6 +7,7 @@ #include "common/fs/file.h" #include "common/fs/path_util.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/string_util.h" #include "common/swap.h" #include "core/constants.h" @@ -512,10 +513,11 @@ protected: class IManagerForApplication final : public ServiceFramework { public: - explicit IManagerForApplication(Core::System& system_, Common::UUID user_id_) + explicit IManagerForApplication(Core::System& system_, + const std::shared_ptr& profile_manager_) : ServiceFramework{system_, "IManagerForApplication"}, ensure_token_id{std::make_shared(system)}, - user_id{user_id_} { + profile_manager{profile_manager_} { // clang-format off static const FunctionInfo functions[] = { {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, @@ -545,7 +547,7 @@ private: IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.PushRaw(user_id.Hash()); + rb.PushRaw(profile_manager->GetLastOpenedUser().Hash()); } void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) { @@ -575,17 +577,20 @@ private: IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.PushRaw(user_id.Hash()); + rb.PushRaw(profile_manager->GetLastOpenedUser().Hash()); } void StoreOpenContext(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_DEBUG(Service_ACC, "called"); + + profile_manager->StoreOpenedUsers(); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } std::shared_ptr ensure_token_id{}; - Common::UUID user_id{}; + std::shared_ptr profile_manager; }; // 6.0.0+ @@ -790,7 +795,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system, profile_manager->GetLastOpenedUser()); + rb.PushIpcInterface(system, profile_manager); } void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) { @@ -849,22 +854,10 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } -void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); - - // This is similar to GetBaasAccountManagerForApplication - // This command is used concurrently with ListOpenContextStoredUsers - // TODO: Find the differences between this and GetBaasAccountManagerForApplication - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, profile_manager->GetLastOpenedUser()); -} - void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_DEBUG(Service_ACC, "called"); - // TODO(ogniK): Handle open contexts - ctx.WriteBuffer(profile_manager->GetOpenUsers()); + ctx.WriteBuffer(profile_manager->GetStoredOpenedUsers()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 1621e7c..9411b0b 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -35,7 +35,6 @@ public: void InitializeApplicationInfoV2(Kernel::HLERequestContext& ctx); void GetProfileEditor(Kernel::HLERequestContext& ctx); void ListQualifiedUsers(Kernel::HLERequestContext& ctx); - void LoadOpenContext(Kernel::HLERequestContext& ctx); void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx); void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 65023b8..54844bf 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -28,7 +28,7 @@ ACC_U0::ACC_U0(std::shared_ptr module_, std::shared_ptr {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {120, nullptr, "CreateGuestLoginRequest"}, - {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+ + {130, nullptr, "LoadOpenContext"}, // 5.0.0+ {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ diff --git a/src/core/hle/service/acc/async_context.cpp b/src/core/hle/service/acc/async_context.cpp index c85b2e4..713689d 100644 --- a/src/core/hle/service/acc/async_context.cpp +++ b/src/core/hle/service/acc/async_context.cpp @@ -64,7 +64,7 @@ void IAsyncContext::GetResult(Kernel::HLERequestContext& ctx) { void IAsyncContext::MarkComplete() { is_complete.store(true); - completion_event->GetWritableEvent().Signal(); + completion_event->Signal(); } } // namespace Service::Account diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index a58da4d..97f7c66 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -9,6 +9,7 @@ #include "common/fs/file.h" #include "common/fs/fs.h" #include "common/fs/path_util.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "core/hle/service/acc/profile_manager.h" @@ -261,6 +262,31 @@ UUID ProfileManager::GetLastOpenedUser() const { return last_opened_user; } +/// Gets the list of stored opened users. +UserIDArray ProfileManager::GetStoredOpenedUsers() const { + UserIDArray output{}; + std::ranges::transform(stored_opened_profiles, output.begin(), [](const ProfileInfo& p) { + if (p.is_open) + return p.user_uuid; + return Common::InvalidUUID; + }); + std::stable_partition(output.begin(), output.end(), + [](const UUID& uuid) { return uuid.IsValid(); }); + return output; +} + +/// Captures the opened users, which can be queried across process launches with +/// ListOpenContextStoredUsers. +void ProfileManager::StoreOpenedUsers() { + size_t profile_index{}; + stored_opened_profiles = {}; + std::for_each(profiles.begin(), profiles.end(), [&](const auto& profile) { + if (profile.is_open) { + stored_opened_profiles[profile_index++] = profile; + } + }); +} + /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(std::optional index, ProfileBase& profile, UserData& data) const { diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 135f7d0..993a5a5 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -86,6 +86,8 @@ public: UserIDArray GetOpenUsers() const; UserIDArray GetAllUsers() const; Common::UUID GetLastOpenedUser() const; + UserIDArray GetStoredOpenedUsers() const; + void StoreOpenedUsers(); bool CanSystemRegisterUser() const; @@ -101,6 +103,7 @@ private: bool RemoveProfileAtIndex(std::size_t index); std::array profiles{}; + std::array stored_opened_profiles{}; std::size_t user_count{}; Common::UUID last_opened_user{}; }; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 6fb7e19..22999c9 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -299,7 +299,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, {110, nullptr, "SetApplicationAlbumUserData"}, {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"}, - {130, nullptr, "SetRecordVolumeMuted"}, + {130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"}, {1000, nullptr, "GetDebugStorageChannel"}, }; // clang-format on @@ -316,7 +316,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv accumulated_suspended_tick_changed_event = service_context.CreateEvent("ISelfController:AccumulatedSuspendedTickChangedEvent"); - accumulated_suspended_tick_changed_event->GetWritableEvent().Signal(); + accumulated_suspended_tick_changed_event->Signal(); } ISelfController::~ISelfController() { @@ -378,7 +378,7 @@ void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) { void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); - launchable_event->GetWritableEvent().Signal(); + launchable_event->Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); @@ -597,6 +597,17 @@ void ISelfController::SaveCurrentScreenshot(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void ISelfController::SetRecordVolumeMuted(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto is_record_volume_muted = rp.Pop(); + + LOG_WARNING(Service_AM, "(STUBBED) called. is_record_volume_muted={}", is_record_volume_muted); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + AppletMessageQueue::AppletMessageQueue(Core::System& system) : service_context{system, "AppletMessageQueue"} { on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); @@ -618,18 +629,18 @@ Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() { void AppletMessageQueue::PushMessage(AppletMessage msg) { messages.push(msg); - on_new_message->GetWritableEvent().Signal(); + on_new_message->Signal(); } AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { if (messages.empty()) { - on_new_message->GetWritableEvent().Clear(); + on_new_message->Clear(); return AppletMessage::None; } auto msg = messages.front(); messages.pop(); if (messages.empty()) { - on_new_message->GetWritableEvent().Clear(); + on_new_message->Clear(); } return msg; } @@ -653,7 +664,7 @@ void AppletMessageQueue::FocusStateChanged() { void AppletMessageQueue::OperationModeChanged() { PushMessage(AppletMessage::OperationModeChanged); PushMessage(AppletMessage::PerformanceModeChanged); - on_operation_mode_changed->GetWritableEvent().Signal(); + on_operation_mode_changed->Signal(); } ICommonStateGetter::ICommonStateGetter(Core::System& system_, @@ -1114,7 +1125,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { const u64 offset{rp.Pop()}; const std::vector data{ctx.ReadBuffer()}; - const std::size_t size{std::min(data.size(), backing.GetSize() - offset)}; + const std::size_t size{std::min(data.size(), backing.GetSize() - offset)}; LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); @@ -1138,7 +1149,7 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 offset{rp.Pop()}; - const std::size_t size{std::min(ctx.GetWriteBufferSize(), backing.GetSize() - offset)}; + const std::size_t size{std::min(ctx.GetWriteBufferSize(), backing.GetSize() - offset)}; LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index bb75c62..a0fbfcf 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -182,6 +182,7 @@ private: void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx); void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx); + void SetRecordVolumeMuted(Kernel::HLERequestContext& ctx); enum class ScreenshotPermission : u32 { Inherit = 0, diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp new file mode 100644 index 0000000..d0969b0 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -0,0 +1,177 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/frontend/applets/cabinet.h" +#include "core/hid/hid_core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_cabinet.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/nfp/nfp_device.h" + +namespace Service::AM::Applets { + +Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::CabinetApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_}, service_context{ + system_, + "CabinetApplet"} { + + availability_change_event = + service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent"); +} + +Cabinet::~Cabinet() = default; + +void Cabinet::Initialize() { + Applet::Initialize(); + + LOG_INFO(Service_HID, "Initializing Cabinet Applet."); + + LOG_DEBUG(Service_HID, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); + + const auto storage = broker.PopNormalDataToApplet(); + ASSERT(storage != nullptr); + + const auto applet_input_data = storage->GetData(); + ASSERT(applet_input_data.size() >= sizeof(StartParamForAmiiboSettings)); + + std::memcpy(&applet_input_common, applet_input_data.data(), + sizeof(StartParamForAmiiboSettings)); +} + +bool Cabinet::TransactionComplete() const { + return is_complete; +} + +Result Cabinet::GetStatus() const { + return ResultSuccess; +} + +void Cabinet::ExecuteInteractive() { + ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); +} + +void Cabinet::Execute() { + if (is_complete) { + return; + } + + const auto callback = [this](bool apply_changes, const std::string& amiibo_name) { + DisplayCompleted(apply_changes, amiibo_name); + }; + + // TODO: listen on all controllers + if (nfp_device == nullptr) { + nfp_device = std::make_shared( + system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event); + nfp_device->Initialize(); + nfp_device->StartDetection(Service::NFP::TagProtocol::All); + } + + const Core::Frontend::CabinetParameters parameters{ + .tag_info = applet_input_common.tag_info, + .register_info = applet_input_common.register_info, + .mode = applet_input_common.applet_mode, + }; + + switch (applet_input_common.applet_mode) { + case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: + case Service::NFP::CabinetMode::StartGameDataEraser: + case Service::NFP::CabinetMode::StartRestorer: + case Service::NFP::CabinetMode::StartFormatter: + frontend.ShowCabinetApplet(callback, parameters, nfp_device); + break; + default: + UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode); + DisplayCompleted(false, {}); + break; + } +} + +void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) { + Service::Mii::MiiManager manager; + ReturnValueForAmiiboSettings applet_output{}; + + if (!apply_changes) { + Cancel(); + } + + if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound && + nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) { + Cancel(); + } + + if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) { + nfp_device->Mount(Service::NFP::MountTarget::All); + } + + switch (applet_input_common.applet_mode) { + case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: { + Service::NFP::AmiiboName name{}; + std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1)); + nfp_device->SetNicknameAndOwner(name); + break; + } + case Service::NFP::CabinetMode::StartGameDataEraser: + nfp_device->DeleteApplicationArea(); + break; + case Service::NFP::CabinetMode::StartRestorer: + nfp_device->RestoreAmiibo(); + break; + case Service::NFP::CabinetMode::StartFormatter: + nfp_device->DeleteAllData(); + break; + default: + UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode); + break; + } + + applet_output.device_handle = applet_input_common.device_handle; + applet_output.result = CabinetResult::Cancel; + const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info); + const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info); + nfp_device->Finalize(); + + if (reg_result.IsSuccess()) { + applet_output.result |= CabinetResult::RegisterInfo; + } + + if (tag_result.IsSuccess()) { + applet_output.result |= CabinetResult::TagInfo; + } + + std::vector out_data(sizeof(ReturnValueForAmiiboSettings)); + std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings)); + + is_complete = true; + + broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out_data))); + broker.SignalStateChanged(); +} + +void Cabinet::Cancel() { + ReturnValueForAmiiboSettings applet_output{}; + applet_output.device_handle = applet_input_common.device_handle; + applet_output.result = CabinetResult::Cancel; + nfp_device->Finalize(); + + std::vector out_data(sizeof(ReturnValueForAmiiboSettings)); + std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings)); + + is_complete = true; + + broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out_data))); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_cabinet.h b/src/core/hle/service/am/applets/applet_cabinet.h new file mode 100644 index 0000000..84197a8 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_cabinet.h @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nfp/nfp_types.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Core { +class System; +} // namespace Core + +namespace Service::NFP { +class NfpDevice; +} + +namespace Service::AM::Applets { + +enum class CabinetAppletVersion : u32 { + Version1 = 0x1, +}; + +enum class CabinetResult : u8 { + Cancel = 0, + TagInfo = 1 << 1, + RegisterInfo = 1 << 2, + All = TagInfo | RegisterInfo, +}; +DECLARE_ENUM_FLAG_OPERATORS(CabinetResult) + +// This is nn::nfp::AmiiboSettingsStartParam +struct AmiiboSettingsStartParam { + u64 device_handle; + std::array param_1; + u8 param_2; +}; +static_assert(sizeof(AmiiboSettingsStartParam) == 0x30, + "AmiiboSettingsStartParam is an invalid size"); + +#pragma pack(push, 1) +// This is nn::nfp::StartParamForAmiiboSettings +struct StartParamForAmiiboSettings { + u8 param_1; + Service::NFP::CabinetMode applet_mode; + u8 flags; + u8 amiibo_settings_1; + u64 device_handle; + Service::NFP::TagInfo tag_info; + Service::NFP::RegisterInfo register_info; + std::array amiibo_settings_3; + INSERT_PADDING_BYTES(0x24); +}; +static_assert(sizeof(StartParamForAmiiboSettings) == 0x1A8, + "StartParamForAmiiboSettings is an invalid size"); + +// This is nn::nfp::ReturnValueForAmiiboSettings +struct ReturnValueForAmiiboSettings { + CabinetResult result; + INSERT_PADDING_BYTES(0x3); + u64 device_handle; + Service::NFP::TagInfo tag_info; + Service::NFP::RegisterInfo register_info; + INSERT_PADDING_BYTES(0x24); +}; +static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188, + "ReturnValueForAmiiboSettings is an invalid size"); +#pragma pack(pop) + +class Cabinet final : public Applet { +public: + explicit Cabinet(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::CabinetApplet& frontend_); + ~Cabinet() override; + + void Initialize() override; + + bool TransactionComplete() const override; + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + void DisplayCompleted(bool apply_changes, std::string_view amiibo_name); + void Cancel(); + +private: + const Core::Frontend::CabinetApplet& frontend; + Core::System& system; + + bool is_complete{false}; + std::shared_ptr nfp_device; + Kernel::KEvent* availability_change_event; + KernelHelpers::ServiceContext service_context; + StartParamForAmiiboSettings applet_input_common{}; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index fcf34bf..bae0d99 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -144,6 +144,7 @@ void Error::Initialize() { break; default: UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); + break; } } diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp index c34ef08..e50acda 100644 --- a/src/core/hle/service/am/applets/applet_general_backend.cpp +++ b/src/core/hle/service/am/applets/applet_general_backend.cpp @@ -129,6 +129,7 @@ void Auth::Execute() { } default: unimplemented_log(); + break; } } @@ -192,6 +193,7 @@ void PhotoViewer::Execute() { break; default: UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); + break; } } diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index b5b8e4c..10afbc2 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -5,6 +5,7 @@ #include "common/assert.h" #include "core/core.h" +#include "core/frontend/applets/cabinet.h" #include "core/frontend/applets/controller.h" #include "core/frontend/applets/error.h" #include "core/frontend/applets/general_frontend.h" @@ -16,6 +17,7 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/applets/applet_cabinet.h" #include "core/hle/service/am/applets/applet_controller.h" #include "core/hle/service/am/applets/applet_error.h" #include "core/hle/service/am/applets/applet_general_backend.h" @@ -65,7 +67,7 @@ std::shared_ptr AppletDataBroker::PopNormalDataToGame() { auto out = std::move(out_channel.front()); out_channel.pop_front(); - pop_out_data_event->GetWritableEvent().Clear(); + pop_out_data_event->Clear(); return out; } @@ -84,7 +86,7 @@ std::shared_ptr AppletDataBroker::PopInteractiveDataToGame() { auto out = std::move(out_interactive_channel.front()); out_interactive_channel.pop_front(); - pop_interactive_out_data_event->GetWritableEvent().Clear(); + pop_interactive_out_data_event->Clear(); return out; } @@ -103,7 +105,7 @@ void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr&& storag void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr&& storage) { out_channel.emplace_back(std::move(storage)); - pop_out_data_event->GetWritableEvent().Signal(); + pop_out_data_event->Signal(); } void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr&& storage) { @@ -112,11 +114,11 @@ void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr&& s void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr&& storage) { out_interactive_channel.emplace_back(std::move(storage)); - pop_interactive_out_data_event->GetWritableEvent().Signal(); + pop_interactive_out_data_event->Signal(); } void AppletDataBroker::SignalStateChanged() { - state_changed_event->GetWritableEvent().Signal(); + state_changed_event->Signal(); switch (applet_mode) { case LibraryAppletMode::AllForeground: @@ -171,13 +173,15 @@ void Applet::Initialize() { AppletFrontendSet::AppletFrontendSet() = default; -AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, +AppletFrontendSet::AppletFrontendSet(CabinetApplet cabinet_applet, + ControllerApplet controller_applet, ErrorApplet error_applet, MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_, ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) - : controller{std::move(controller_applet)}, error{std::move(error_applet)}, - mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)}, + : cabinet{std::move(cabinet_applet)}, controller{std::move(controller_applet)}, + error{std::move(error_applet)}, mii_edit{std::move(mii_edit_)}, + parental_controls{std::move(parental_controls_applet)}, photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} @@ -196,6 +200,10 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const { } void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { + if (set.cabinet != nullptr) { + frontend.cabinet = std::move(set.cabinet); + } + if (set.controller != nullptr) { frontend.controller = std::move(set.controller); } @@ -235,6 +243,10 @@ void AppletManager::SetDefaultAppletFrontendSet() { } void AppletManager::SetDefaultAppletsIfMissing() { + if (frontend.cabinet == nullptr) { + frontend.cabinet = std::make_unique(); + } + if (frontend.controller == nullptr) { frontend.controller = std::make_unique(system.HIDCore()); @@ -279,6 +291,8 @@ std::shared_ptr AppletManager::GetApplet(AppletId id, LibraryAppletMode switch (id) { case AppletId::Auth: return std::make_shared(system, mode, *frontend.parental_controls); + case AppletId::Cabinet: + return std::make_shared(system, mode, *frontend.cabinet); case AppletId::Controller: return std::make_shared(system, mode, *frontend.controller); case AppletId::Error: diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index e78a576..a22eb62 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -16,6 +16,7 @@ class System; } namespace Core::Frontend { +class CabinetApplet; class ControllerApplet; class ECommerceApplet; class ErrorApplet; @@ -164,7 +165,7 @@ protected: u32_le size; u32_le library_version; u32_le theme_color; - u8 play_startup_sound; + bool play_startup_sound; u64_le system_tick; }; static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); @@ -176,6 +177,7 @@ protected: }; struct AppletFrontendSet { + using CabinetApplet = std::unique_ptr; using ControllerApplet = std::unique_ptr; using ErrorApplet = std::unique_ptr; using MiiEdit = std::unique_ptr; @@ -186,10 +188,11 @@ struct AppletFrontendSet { using WebBrowser = std::unique_ptr; AppletFrontendSet(); - AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, - MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet, - PhotoViewer photo_viewer_, ProfileSelect profile_select_, - SoftwareKeyboard software_keyboard_, WebBrowser web_browser_); + AppletFrontendSet(CabinetApplet cabinet_applet, ControllerApplet controller_applet, + ErrorApplet error_applet, MiiEdit mii_edit_, + ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_, + ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_, + WebBrowser web_browser_); ~AppletFrontendSet(); AppletFrontendSet(const AppletFrontendSet&) = delete; @@ -198,6 +201,7 @@ struct AppletFrontendSet { AppletFrontendSet(AppletFrontendSet&&) noexcept; AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; + CabinetApplet cabinet; ControllerApplet controller; ErrorApplet error; MiiEdit mii_edit; diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index 4a2ae5f..5abf22b 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp @@ -45,9 +45,25 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} { {32, nullptr, "GetActiveOutputTarget"}, {33, nullptr, "GetTargetDeviceInfo"}, {34, nullptr, "AcquireTargetNotification"}, + {35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"}, + {36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"}, + {37, nullptr, "SetHearingProtectionSafeguardEnabled"}, + {38, nullptr, "IsHearingProtectionSafeguardEnabled"}, + {39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"}, + {40, nullptr, "GetSystemInformationForDebug"}, + {41, nullptr, "SetVolumeButtonLongPressTime"}, + {42, nullptr, "SetNativeVolumeForDebug"}, {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"}, {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"}, {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"}, + {10100, nullptr, "GetAudioVolumeDataForPlayReport"}, + {10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"}, + {10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"}, + {10103, nullptr, "GetAudioOutputTargetForPlayReport"}, + {10104, nullptr, "GetAudioOutputChannelCountForPlayReport"}, + {10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, + {10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"}, + {50000, nullptr, "SetAnalogInputBoostGainForPrototyping"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 48a9a73..053e8f9 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -17,7 +17,7 @@ using namespace AudioCore::AudioIn; class IAudioIn final : public ServiceFramework { public: explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, - std::string& device_name, const AudioInParameter& in_params, u32 handle, + const std::string& device_name, const AudioInParameter& in_params, u32 handle, u64 applet_resource_user_id) : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, @@ -122,10 +122,10 @@ private: } void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) { - auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); - std::vector released_buffers(write_buffer_size, 0); + const auto write_buffer_size = ctx.GetWriteBufferNumElements(); + std::vector released_buffers(write_buffer_size); - auto count = impl->GetReleasedBuffers(released_buffers); + const auto count = impl->GetReleasedBuffers(released_buffers); [[maybe_unused]] std::string tags{}; for (u32 i = 0; i < count; i++) { @@ -228,7 +228,7 @@ void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); const auto write_count = - static_cast(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + static_cast(ctx.GetWriteBufferNumElements()); std::vector device_names{}; u32 out_count{0}; @@ -248,7 +248,7 @@ void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); const auto write_count = - static_cast(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + static_cast(ctx.GetWriteBufferNumElements()); std::vector device_names{}; u32 out_count{0}; diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 49c0923..29751f0 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -24,7 +24,7 @@ using namespace AudioCore::AudioOut; class IAudioOut final : public ServiceFramework { public: explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, - size_t session_id, std::string& device_name, + size_t session_id, const std::string& device_name, const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( @@ -129,16 +129,16 @@ private: } void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) { - auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); - std::vector released_buffers(write_buffer_size, 0); + const auto write_buffer_size = ctx.GetWriteBufferNumElements(); + std::vector released_buffers(write_buffer_size); - auto count = impl->GetReleasedBuffers(released_buffers); + const auto count = impl->GetReleasedBuffers(released_buffers); [[maybe_unused]] std::string tags{}; for (u32 i = 0; i < count; i++) { tags += fmt::format("{:08X}, ", released_buffers[i]); } - [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + [[maybe_unused]] const auto sessionid{impl->GetSystem().GetSessionId()}; LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, tags); @@ -244,7 +244,7 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { std::scoped_lock l{impl->mutex}; const auto write_count = - static_cast(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + static_cast(ctx.GetWriteBufferNumElements()); std::vector device_names{}; if (write_count > 0) { device_names.emplace_back("DeviceOut"); diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 6fb07c3..3a1c231 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -14,6 +14,7 @@ #include "common/bit_util.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/string_util.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" @@ -52,6 +53,8 @@ public: {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"}, {11, nullptr, "ExecuteAudioRendererRendering"}, + {12, &IAudioRenderer::SetVoiceDropParameter, "SetVoiceDropParameter"}, + {13, &IAudioRenderer::GetVoiceDropParameter, "GetVoiceDropParameter"}, }; // clang-format on RegisterHandlers(functions); @@ -205,6 +208,30 @@ private: LOG_DEBUG(Service_Audio, "called"); } + void SetVoiceDropParameter(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + IPC::RequestParser rp{ctx}; + auto voice_drop_param{rp.Pop()}; + + auto& system_ = impl->GetSystem(); + system_.SetVoiceDropParameter(voice_drop_param); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetVoiceDropParameter(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + auto& system_ = impl->GetSystem(); + auto voice_drop_param{system_.GetVoiceDropParameter()}; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(voice_drop_param); + } + KernelHelpers::ServiceContext service_context; Kernel::KEvent* rendered_event; Manager& manager; @@ -239,7 +266,7 @@ public: }; RegisterHandlers(functions); - event->GetWritableEvent().Signal(); + event->Signal(); } ~IAudioDevice() override { @@ -248,7 +275,7 @@ public: private: void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { - const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); + const size_t in_count = ctx.GetWriteBufferNumElements(); std::vector out_names{}; @@ -309,7 +336,7 @@ private: } void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { - const auto write_size = ctx.GetWriteBufferSize() / sizeof(char); + const auto write_size = ctx.GetWriteBufferSize(); std::string out_name{"AudioTvOutput"}; LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name); @@ -325,7 +352,7 @@ private: void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "(STUBBED) called"); - event->GetWritableEvent().Signal(); + event->Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); @@ -361,7 +388,7 @@ private: } void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) { - const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); + const size_t in_count = ctx.GetWriteBufferNumElements(); std::vector out_names{}; diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 8bafc3a..825fb8b 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -68,7 +68,7 @@ private: ExtraBehavior extra_behavior) { u32 consumed = 0; u32 sample_count = 0; - std::vector samples(ctx.GetWriteBufferSize() / sizeof(opus_int16)); + std::vector samples(ctx.GetWriteBufferNumElements()); if (extra_behavior == ExtraBehavior::ResetContext) { ResetDecoderContext(); diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index cd0b405..847f769 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp @@ -82,7 +82,7 @@ void ProgressServiceBackend::FinishDownload(Result result) { } void ProgressServiceBackend::SignalUpdate() { - update_event->GetWritableEvent().Signal(); + update_event->Signal(); } Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp index bc08ac4..cbe690a 100644 --- a/src/core/hle/service/bcat/bcat_module.cpp +++ b/src/core/hle/service/bcat/bcat_module.cpp @@ -443,7 +443,7 @@ private: } void Read(Kernel::HLERequestContext& ctx) { - auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry); + auto write_size = ctx.GetWriteBufferNumElements(); LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size); @@ -533,7 +533,7 @@ private: } void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) { - auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName); + auto size = ctx.GetWriteBufferNumElements(); LOG_DEBUG(Service_BCAT, "called, size={:016X}", size); diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index ff9b042..d183e58 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -192,12 +192,10 @@ private: } void ListCommonTicketRightsIds(Kernel::HLERequestContext& ctx) { - u32 out_entries; - if (keys.GetCommonTickets().empty()) - out_entries = 0; - else - out_entries = static_cast(ctx.GetWriteBufferSize() / sizeof(u128)); - + size_t out_entries = 0; + if (!keys.GetCommonTickets().empty()) { + out_entries = ctx.GetWriteBufferNumElements(); + } LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); keys.PopulateTickets(); @@ -206,20 +204,19 @@ private: std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), [](const auto& pair) { return pair.first; }); - out_entries = static_cast(std::min(ids.size(), out_entries)); + out_entries = std::min(ids.size(), out_entries); ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(out_entries); + rb.Push(static_cast(out_entries)); } void ListPersonalizedTicketRightsIds(Kernel::HLERequestContext& ctx) { - u32 out_entries; - if (keys.GetPersonalizedTickets().empty()) - out_entries = 0; - else - out_entries = static_cast(ctx.GetWriteBufferSize() / sizeof(u128)); + size_t out_entries = 0; + if (!keys.GetPersonalizedTickets().empty()) { + out_entries = ctx.GetWriteBufferNumElements(); + } LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); @@ -229,12 +226,12 @@ private: std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), [](const auto& pair) { return pair.first; }); - out_entries = static_cast(std::min(ids.size(), out_entries)); + out_entries = std::min(ids.size(), out_entries); ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(out_entries); + rb.Push(static_cast(out_entries)); } void GetCommonTicketSize(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index e23eae3..fbb16a7 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -277,7 +277,7 @@ private: LOG_DEBUG(Service_FS, "called."); // Calculate how many entries we can fit in the output buffer - const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); + const u64 count_entries = ctx.GetWriteBufferNumElements(); // Cap at total number of entries. const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index); @@ -543,7 +543,7 @@ public: LOG_DEBUG(Service_FS, "called"); // Calculate how many entries we can fit in the output buffer - const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo); + const u64 count_entries = ctx.GetWriteBufferNumElements(); // Cap at total number of entries. const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index); @@ -707,7 +707,7 @@ FSP_SRV::FSP_SRV(Core::System& system_) {31, nullptr, "OpenGameCardFileSystem"}, {32, nullptr, "ExtendSaveDataFileSystem"}, {33, nullptr, "DeleteCacheStorage"}, - {34, nullptr, "GetCacheStorageSize"}, + {34, &FSP_SRV::GetCacheStorageSize, "GetCacheStorageSize"}, {35, nullptr, "CreateSaveDataFileSystemByHashSalt"}, {36, nullptr, "OpenHostFileSystemWithOption"}, {51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"}, @@ -1107,6 +1107,18 @@ void FSP_SRV::GetProgramIndexForAccessLog(Kernel::HLERequestContext& ctx) { rb.Push(access_log_program_index); } +void FSP_SRV::GetCacheStorageSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto index{rp.Pop()}; + + LOG_WARNING(Service_FS, "(STUBBED) called with index={}", index); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.Push(s64{0}); + rb.Push(s64{0}); +} + class IMultiCommitManager final : public ServiceFramework { public: explicit IMultiCommitManager(Core::System& system_) diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 36f552e..3d88b97 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -54,6 +54,7 @@ private: void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); void GetProgramIndexForAccessLog(Kernel::HLERequestContext& ctx); void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); + void GetCacheStorageSize(Kernel::HLERequestContext& ctx); FileSystemController& fsc; const FileSys::ContentProvider& content_provider; diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index e0db787..fad5321 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -26,7 +26,7 @@ public: {10101, &IFriendService::GetFriendList, "GetFriendList"}, {10102, nullptr, "UpdateFriendInfo"}, {10110, nullptr, "GetFriendProfileImage"}, - {10120, nullptr, "IsFriendListCacheAvailable"}, + {10120, &IFriendService::CheckFriendListAvailability, "CheckFriendListAvailability"}, {10121, nullptr, "EnsureFriendListAvailable"}, {10200, nullptr, "SendFriendRequestForApplication"}, {10211, nullptr, "AddFacedFriendRequestForApplication"}, @@ -194,6 +194,17 @@ private: // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" } + void CheckFriendListAvailability(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid{rp.PopRaw()}; + + LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString()); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(true); + } + KernelHelpers::ServiceContext service_context; Kernel::KEvent* completion_event; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index cb29004..2f871de 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -16,7 +16,6 @@ #include "core/hid/hid_core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/hid/errors.h" #include "core/hle/service/kernel_helpers.h" @@ -167,7 +166,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { const auto& battery_level = controller.device->GetBattery(); auto* shared_memory = controller.shared_memory; if (controller_type == Core::HID::NpadStyleIndex::None) { - controller.styleset_changed_event->GetWritableEvent().Signal(); + controller.styleset_changed_event->Signal(); return; } @@ -660,7 +659,6 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing ASSERT(false); break; case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Pokeball: set_motion_state(sixaxis_fullkey_state, motion_state[0]); break; case Core::HID::NpadStyleIndex::Handheld: @@ -676,6 +674,11 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing case Core::HID::NpadStyleIndex::JoyconRight: set_motion_state(sixaxis_right_lifo_state, motion_state[1]); break; + case Core::HID::NpadStyleIndex::Pokeball: + using namespace std::literals::chrono_literals; + set_motion_state(sixaxis_fullkey_state, motion_state[0]); + sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); + break; default: break; } @@ -742,8 +745,9 @@ void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { } void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { - ASSERT(max_length < supported_npad_id_types.size()); - std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size()); + const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); + ASSERT(max_length <= copy_amount); + std::memcpy(data, supported_npad_id_types.data(), copy_amount); } std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { @@ -864,7 +868,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, return false; } - if (!controller.device->IsVibrationEnabled()) { + if (!controller.device->IsVibrationEnabled(device_index)) { if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f || controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) { // Send an empty vibration to stop any vibrations. @@ -997,7 +1001,7 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa } controller.vibration[device_index].device_mounted = - controller.device->TestVibration(device_index); + controller.device->IsVibrationEnabled(device_index); } void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { @@ -1029,7 +1033,7 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { const auto& controller = GetControllerFromNpadIdType(npad_id); - controller.styleset_changed_event->GetWritableEvent().Signal(); + controller.styleset_changed_event->Signal(); } void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, @@ -1498,25 +1502,25 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller Core::HID::NpadStyleTag style = GetSupportedStyleSet(); switch (controller) { case Core::HID::NpadStyleIndex::ProController: - return style.fullkey; + return style.fullkey.As(); case Core::HID::NpadStyleIndex::JoyconDual: - return style.joycon_dual; + return style.joycon_dual.As(); case Core::HID::NpadStyleIndex::JoyconLeft: - return style.joycon_left; + return style.joycon_left.As(); case Core::HID::NpadStyleIndex::JoyconRight: - return style.joycon_right; + return style.joycon_right.As(); case Core::HID::NpadStyleIndex::GameCube: - return style.gamecube; + return style.gamecube.As(); case Core::HID::NpadStyleIndex::Pokeball: - return style.palma; + return style.palma.As(); case Core::HID::NpadStyleIndex::NES: - return style.lark; + return style.lark.As(); case Core::HID::NpadStyleIndex::SNES: - return style.lucia; + return style.lucia.As(); case Core::HID::NpadStyleIndex::N64: - return style.lagoon; + return style.lagoon.As(); case Core::HID::NpadStyleIndex::SegaGenesis: - return style.lager; + return style.lager.As(); default: return false; } diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp new file mode 100644 index 0000000..4564ea1 --- /dev/null +++ b/src/core/hle/service/hid/controllers/palma.cpp @@ -0,0 +1,229 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core_timing.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/hid/controllers/palma.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Service::HID { + +Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_) + : ControllerBase{hid_core_}, service_context{service_context_} { + controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); + operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); +} + +Controller_Palma::~Controller_Palma() = default; + +void Controller_Palma::OnInit() {} + +void Controller_Palma::OnRelease() {} + +void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { + if (!IsControllerActivated()) { + return; + } +} + +Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, + PalmaConnectionHandle& handle) { + active_handle.npad_id = npad_id; + handle = active_handle; + return ResultSuccess; +} + +Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + ActivateController(); + return ResultSuccess; +} + +Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( + const PalmaConnectionHandle& handle) const { + if (handle.npad_id != active_handle.npad_id) { + LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); + } + return operation_complete_event->GetReadableEvent(); +} + +Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, + PalmaOperationType& operation_type, + PalmaOperationData& data) const { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation_type = operation.operation; + data = operation.data; + return ResultSuccess; +} + +Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, + u64 palma_activity) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::PlayActivity; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, + PalmaFrModeType fr_mode_) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + fr_mode = fr_mode_; + return ResultSuccess; +} + +Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadStep; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->Signal(); + return ResultSuccess; +} + +Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return ResultSuccess; +} + +Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return ResultSuccess; +} + +void Controller_Palma::ReadPalmaApplicationSection() {} + +void Controller_Palma::WritePalmaApplicationSection() {} + +Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadUniqueCode; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::SetUniqueCodeInvalid; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->Signal(); + return ResultSuccess; +} + +void Controller_Palma::WritePalmaActivityEntry() {} + +Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, + u64 unknown) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::WriteRgbLedPatternEntry; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->Signal(); + return ResultSuccess; +} + +Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, + u8* t_mem, u64 size) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::WriteWaveEntry; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, + s32 database_id_version_) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + database_id_version = database_id_version_; + operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; + operation.result = PalmaResultSuccess; + operation.data[0] = {}; + operation_complete_event->Signal(); + return ResultSuccess; +} + +Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( + const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation.data[0] = static_cast(database_id_version); + operation_complete_event->Signal(); + return ResultSuccess; +} + +void Controller_Palma::SuspendPalmaFeature() {} + +Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return operation.result; +} +void Controller_Palma::ReadPalmaPlayLog() {} + +void Controller_Palma::ResetPalmaPlayLog() {} + +void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { + // If true controllers are able to be paired + is_connectable = is_all_connectable; +} + +void Controller_Palma::SetIsPalmaPairedConnectable() {} + +Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + // TODO: Do something + return ResultSuccess; +} + +void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {} + +void Controller_Palma::CancelWritePalmaWaveEntry() {} + +void Controller_Palma::EnablePalmaBoostMode() {} + +void Controller_Palma::GetPalmaBluetoothAddress() {} + +void Controller_Palma::SetDisallowedPalmaConnection() {} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h new file mode 100644 index 0000000..1d7fc94 --- /dev/null +++ b/src/core/hle/service/hid/controllers/palma.h @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/hid/errors.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Service::KernelHelpers { +class ServiceContext; +} + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { +class Controller_Palma final : public ControllerBase { +public: + using PalmaOperationData = std::array; + + // This is nn::hid::PalmaOperationType + enum class PalmaOperationType { + PlayActivity, + SetFrModeType, + ReadStep, + EnableStep, + ResetStep, + ReadApplicationSection, + WriteApplicationSection, + ReadUniqueCode, + SetUniqueCodeInvalid, + WriteActivityEntry, + WriteRgbLedPatternEntry, + WriteWaveEntry, + ReadDataBaseIdentificationVersion, + WriteDataBaseIdentificationVersion, + SuspendFeature, + ReadPlayLog, + ResetPlayLog, + }; + + // This is nn::hid::PalmaWaveSet + enum class PalmaWaveSet : u64 { + Small, + Medium, + Large, + }; + + // This is nn::hid::PalmaFrModeType + enum class PalmaFrModeType : u64 { + Off, + B01, + B02, + B03, + Downloaded, + }; + + // This is nn::hid::PalmaFeature + enum class PalmaFeature : u64 { + FrMode, + RumbleFeedback, + Step, + MuteSwitch, + }; + + // This is nn::hid::PalmaOperationInfo + struct PalmaOperationInfo { + PalmaOperationType operation{}; + Result result{PalmaResultSuccess}; + PalmaOperationData data{}; + }; + static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size"); + + // This is nn::hid::PalmaActivityEntry + struct PalmaActivityEntry { + u32 rgb_led_pattern_index; + INSERT_PADDING_BYTES(2); + PalmaWaveSet wave_set; + u32 wave_index; + INSERT_PADDING_BYTES(12); + }; + static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size"); + + struct PalmaConnectionHandle { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_BYTES(4); // Unknown + }; + static_assert(sizeof(PalmaConnectionHandle) == 0x8, + "PalmaConnectionHandle has incorrect size."); + + explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_); + ~Controller_Palma() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; + + Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle); + Result InitializePalma(const PalmaConnectionHandle& handle); + Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent( + const PalmaConnectionHandle& handle) const; + Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle, + PalmaOperationType& operation_type, + PalmaOperationData& data) const; + Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity); + Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_); + Result ReadPalmaStep(const PalmaConnectionHandle& handle); + Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled); + Result ResetPalmaStep(const PalmaConnectionHandle& handle); + Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle); + Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle); + Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown); + Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, u8* t_mem, + u64 size); + Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, + s32 database_id_version_); + Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle); + Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const; + void SetIsPalmaAllConnectable(bool is_all_connectable); + Result PairPalma(const PalmaConnectionHandle& handle); + void SetPalmaBoostMode(bool boost_mode); + +private: + void ReadPalmaApplicationSection(); + void WritePalmaApplicationSection(); + void WritePalmaActivityEntry(); + void SuspendPalmaFeature(); + void ReadPalmaPlayLog(); + void ResetPalmaPlayLog(); + void SetIsPalmaPairedConnectable(); + void CancelWritePalmaWaveEntry(); + void EnablePalmaBoostMode(); + void GetPalmaBluetoothAddress(); + void SetDisallowedPalmaConnection(); + + bool is_connectable{}; + s32 database_id_version{}; + PalmaOperationInfo operation{}; + PalmaFrModeType fr_mode{}; + PalmaConnectionHandle active_handle{}; + + Core::HID::EmulatedController* controller; + + Kernel::KEvent* operation_complete_event; + KernelHelpers::ServiceContext& service_context; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h index 4613a4e..76208e9 100644 --- a/src/core/hle/service/hid/errors.h +++ b/src/core/hle/service/hid/errors.h @@ -7,6 +7,7 @@ namespace Service::HID { +constexpr Result PalmaResultSuccess{ErrorModule::HID, 0}; constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; @@ -17,6 +18,7 @@ constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; constexpr Result NpadIsSameType{ErrorModule::HID, 602}; constexpr Result InvalidNpadId{ErrorModule::HID, 709}; constexpr Result NpadNotConnected{ErrorModule::HID, 710}; +constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302}; } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 7e92346..bf28440 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -27,6 +27,7 @@ #include "core/hle/service/hid/controllers/keyboard.h" #include "core/hle/service/hid/controllers/mouse.h" #include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/palma.h" #include "core/hle/service/hid/controllers/stubbed.h" #include "core/hle/service/hid/controllers/touchscreen.h" #include "core/hle/service/hid/controllers/xpad.h" @@ -35,8 +36,9 @@ namespace Service::HID { // Updating period for each HID device. // Period time is obtained by measuring the number of samples in a second on HW using a homebrew -// Correct pad_update_ns is 4ms this is overclocked to lower input lag -constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) +// Correct npad_update_ns is 4ms this is overclocked to lower input lag +constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) +constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz) constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) @@ -61,6 +63,7 @@ IAppletResource::IAppletResource(Core::System& system_, MakeControllerWithServiceContext(HidController::NPad, shared_memory); MakeController(HidController::Gesture, shared_memory); MakeController(HidController::ConsoleSixAxisSensor, shared_memory); + MakeControllerWithServiceContext(HidController::Palma, shared_memory); // Homebrew doesn't try to activate some controllers, so we activate them by default GetController(HidController::NPad).ActivateController(); @@ -73,8 +76,16 @@ IAppletResource::IAppletResource(Core::System& system_, GetController(HidController::UniquePad).SetCommonHeaderOffset(0x5A00); // Register update callbacks - pad_update_event = Core::Timing::CreateEvent( + npad_update_event = Core::Timing::CreateEvent( "HID::UpdatePadCallback", + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional { + const auto guard = LockService(); + UpdateNpad(user_data, ns_late); + return std::nullopt; + }); + default_update_event = Core::Timing::CreateEvent( + "HID::UpdateDefaultCallback", [this](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) -> std::optional { const auto guard = LockService(); @@ -98,7 +109,9 @@ IAppletResource::IAppletResource(Core::System& system_, return std::nullopt; }); - system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event); + system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); + system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, + default_update_event); system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, mouse_keyboard_update_event); system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, @@ -116,7 +129,8 @@ void IAppletResource::DeactivateController(HidController controller) { } IAppletResource::~IAppletResource() { - system.CoreTiming().UnscheduleEvent(pad_update_event, 0); + system.CoreTiming().UnscheduleEvent(npad_update_event, 0); + system.CoreTiming().UnscheduleEvent(default_update_event, 0); system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); system.CoreTiming().UnscheduleEvent(motion_update_event, 0); } @@ -142,10 +156,20 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data, if (controller == controllers[static_cast(HidController::Mouse)]) { continue; } + // Npad has it's own update event + if (controller == controllers[static_cast(HidController::NPad)]) { + continue; + } controller->OnUpdate(core_timing); } } +void IAppletResource::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + auto& core_timing = system.CoreTiming(); + + controllers[static_cast(HidController::NPad)]->OnUpdate(core_timing); +} + void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); @@ -311,36 +335,36 @@ Hid::Hid(Core::System& system_) {406, nullptr, "GetNpadLeftRightInterfaceType"}, {407, nullptr, "GetNpadOfHighestBatteryLevel"}, {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, - {500, nullptr, "GetPalmaConnectionHandle"}, - {501, nullptr, "InitializePalma"}, - {502, nullptr, "AcquirePalmaOperationCompleteEvent"}, - {503, nullptr, "GetPalmaOperationInfo"}, - {504, nullptr, "PlayPalmaActivity"}, - {505, nullptr, "SetPalmaFrModeType"}, - {506, nullptr, "ReadPalmaStep"}, - {507, nullptr, "EnablePalmaStep"}, - {508, nullptr, "ResetPalmaStep"}, - {509, nullptr, "ReadPalmaApplicationSection"}, - {510, nullptr, "WritePalmaApplicationSection"}, - {511, nullptr, "ReadPalmaUniqueCode"}, - {512, nullptr, "SetPalmaUniqueCodeInvalid"}, - {513, nullptr, "WritePalmaActivityEntry"}, - {514, nullptr, "WritePalmaRgbLedPatternEntry"}, - {515, nullptr, "WritePalmaWaveEntry"}, - {516, nullptr, "SetPalmaDataBaseIdentificationVersion"}, - {517, nullptr, "GetPalmaDataBaseIdentificationVersion"}, - {518, nullptr, "SuspendPalmaFeature"}, - {519, nullptr, "GetPalmaOperationResult"}, - {520, nullptr, "ReadPalmaPlayLog"}, - {521, nullptr, "ResetPalmaPlayLog"}, + {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"}, + {501, &Hid::InitializePalma, "InitializePalma"}, + {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"}, + {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"}, + {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"}, + {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"}, + {506, &Hid::ReadPalmaStep, "ReadPalmaStep"}, + {507, &Hid::EnablePalmaStep, "EnablePalmaStep"}, + {508, &Hid::ResetPalmaStep, "ResetPalmaStep"}, + {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"}, + {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"}, + {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"}, + {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"}, + {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"}, + {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"}, + {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"}, + {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"}, + {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"}, + {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"}, + {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"}, + {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"}, + {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"}, {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, - {523, nullptr, "SetIsPalmaPairedConnectable"}, - {524, nullptr, "PairPalma"}, + {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"}, + {524, &Hid::PairPalma, "PairPalma"}, {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, - {526, nullptr, "CancelWritePalmaWaveEntry"}, - {527, nullptr, "EnablePalmaBoostMode"}, - {528, nullptr, "GetPalmaBluetoothAddress"}, - {529, nullptr, "SetDisallowedPalmaConnection"}, + {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"}, + {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"}, + {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"}, + {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"}, {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, @@ -1879,14 +1903,361 @@ void Hid::IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx) { rb.Push(false); } -void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { +void Hid::GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - const auto is_palma_all_connectable{rp.Pop()}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); + + Controller_Palma::PalmaConnectionHandle handle; + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.PushRaw(handle); +} + +void Hid::InitializePalma(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.InitializePalma(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle)); +} + +void Hid::GetPalmaOperationInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + Controller_Palma::PalmaOperationType operation_type; + Controller_Palma::PalmaOperationData data; + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data); + + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(static_cast(operation_type)); +} + +void Hid::PlayPalmaActivity(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto palma_activity{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", + connection_handle.npad_id, palma_activity); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::SetPalmaFrModeType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto fr_mode{rp.PopEnum()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", + connection_handle.npad_id, fr_mode); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.ReadPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::EnablePalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_enabled; + INSERT_PADDING_WORDS_NOINIT(1); + Controller_Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", + parameters.connection_handle.npad_id, parameters.is_enabled); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = + controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ResetPalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController(HidController::Palma); + const auto result = controller.ResetPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaApplicationSection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController(HidController::Palma) + .ReadPalmaUniqueCode(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController(HidController::Palma) + .SetPalmaUniqueCodeInvalid(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaActivityEntry(Kernel::HLERequestContext& ctx) { + LOG_CRITICAL(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto unknown{rp.Pop()}; + + const auto buffer = ctx.ReadBuffer(); + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", + connection_handle.npad_id, unknown); + + applet_resource->GetController(HidController::Palma) + .WritePalmaRgbLedPatternEntry(connection_handle, unknown); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaWaveEntry(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto wave_set{rp.PopEnum()}; + const auto unknown{rp.Pop()}; + const auto t_mem_size{rp.Pop()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + const auto size{rp.Pop()}; + + ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject(t_mem_handle); + + if (t_mem.IsNull()) { + LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); LOG_WARNING(Service_HID, - "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}", - applet_resource_user_id, is_palma_all_connectable); + "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, " + "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", + connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); + + applet_resource->GetController(HidController::Palma) + .WritePalmaWaveEntry(connection_handle, wave_set, + system.Memory().GetPointer(t_mem->GetSourceAddress()), t_mem_size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + s32 database_id_version; + INSERT_PADDING_WORDS_NOINIT(1); + Controller_Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", + parameters.connection_handle.npad_id, parameters.database_id_version); + + applet_resource->GetController(HidController::Palma) + .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle, + parameters.database_id_version); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController(HidController::Palma) + .GetPalmaDataBaseIdentificationVersion(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SuspendPalmaFeature(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaOperationResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + const auto result = applet_resource->GetController(HidController::Palma) + .GetPalmaOperationResult(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaPlayLog(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::ResetPalmaPlayLog(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_palma_all_connectable; + INSERT_PADDING_BYTES_NOINIT(7); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", + parameters.is_palma_all_connectable, parameters.applet_resource_user_id); + + applet_resource->GetController(HidController::Palma) + .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::PairPalma(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController(HidController::Palma) + .PairPalma(connection_handle); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -1898,6 +2269,37 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); + applet_resource->GetController(HidController::Palma) + .SetPalmaBoostMode(palma_boost_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::EnablePalmaBoostMode(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index ac43330..b7c2a23 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -33,6 +33,7 @@ enum class HidController : std::size_t { NPad, Gesture, ConsoleSixAxisSensor, + Palma, MaxControllers, }; @@ -70,12 +71,14 @@ private: void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); KernelHelpers::ServiceContext& service_context; - std::shared_ptr pad_update_event; + std::shared_ptr npad_update_event; + std::shared_ptr default_update_event; std::shared_ptr mouse_keyboard_update_event; std::shared_ptr motion_update_event; @@ -166,8 +169,36 @@ private: void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx); + void GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx); + void InitializePalma(Kernel::HLERequestContext& ctx); + void AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx); + void GetPalmaOperationInfo(Kernel::HLERequestContext& ctx); + void PlayPalmaActivity(Kernel::HLERequestContext& ctx); + void SetPalmaFrModeType(Kernel::HLERequestContext& ctx); + void ReadPalmaStep(Kernel::HLERequestContext& ctx); + void EnablePalmaStep(Kernel::HLERequestContext& ctx); + void ResetPalmaStep(Kernel::HLERequestContext& ctx); + void ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx); + void WritePalmaApplicationSection(Kernel::HLERequestContext& ctx); + void ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx); + void SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx); + void WritePalmaActivityEntry(Kernel::HLERequestContext& ctx); + void WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx); + void WritePalmaWaveEntry(Kernel::HLERequestContext& ctx); + void SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx); + void GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx); + void SuspendPalmaFeature(Kernel::HLERequestContext& ctx); + void GetPalmaOperationResult(Kernel::HLERequestContext& ctx); + void ReadPalmaPlayLog(Kernel::HLERequestContext& ctx); + void ResetPalmaPlayLog(Kernel::HLERequestContext& ctx); void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); + void SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx); + void PairPalma(Kernel::HLERequestContext& ctx); void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); + void CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx); + void EnablePalmaBoostMode(Kernel::HLERequestContext& ctx); + void GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx); + void SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx); void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp index ad223d6..57f1a2a 100644 --- a/src/core/hle/service/hid/hidbus/ringcon.cpp +++ b/src/core/hle/service/hid/hidbus/ringcon.cpp @@ -131,12 +131,12 @@ bool RingController::SetCommand(const std::vector& data) { case RingConCommands::ReadRepCount: case RingConCommands::ReadTotalPushCount: ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); - send_command_async_event->GetWritableEvent().Signal(); + send_command_async_event->Signal(); return true; case RingConCommands::ResetRepCount: ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); total_rep_count = 0; - send_command_async_event->GetWritableEvent().Signal(); + send_command_async_event->Signal(); return true; case RingConCommands::SaveCalData: { ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes"); @@ -144,14 +144,14 @@ bool RingController::SetCommand(const std::vector& data) { SaveCalData save_info{}; std::memcpy(&save_info, data.data(), sizeof(SaveCalData)); user_calibration = save_info.calibration; - send_command_async_event->GetWritableEvent().Signal(); + send_command_async_event->Signal(); return true; } default: LOG_ERROR(Service_HID, "Command not implemented {}", command); command = RingConCommands::Error; // Signal a reply to avoid softlocking the game - send_command_async_event->GetWritableEvent().Signal(); + send_command_async_event->Signal(); return false; } } diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index c4b44cb..6a34534 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -542,7 +542,8 @@ Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_h Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry( const Core::IrSensor::IrCameraHandle& camera_handle) { - ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id"); + const auto npad_id_max_index = static_cast(sizeof(StatusManager::device)); + ASSERT_MSG(camera_handle.npad_id < npad_id_max_index, "invalid npad_id"); return shared_memory->device[camera_handle.npad_id]; } diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h index cf49307..d63423a 100644 --- a/src/core/hle/service/hid/irsensor/pointing_processor.h +++ b/src/core/hle/service/hid/irsensor/pointing_processor.h @@ -37,10 +37,10 @@ private: u8 pointing_status; INSERT_PADDING_BYTES(3); u32 unknown; - float unkown_float1; + float unknown_float1; float position_x; float position_y; - float unkown_float2; + float unknown_float2; Core::IrSensor::IrsRect window_of_interest; }; static_assert(sizeof(PointingProcessorMarkerData) == 0x20, diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index 3e31736..4299192 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -9,7 +9,6 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scoped_resource_reservation.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/kernel_helpers.h" namespace Service::KernelHelpers { @@ -32,7 +31,7 @@ ServiceContext::~ServiceContext() { Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { // Reserve a new event from the process resource limit Kernel::KScopedResourceReservation event_reservation(process, - Kernel::LimitableResource::Events); + Kernel::LimitableResource::EventCountMax); if (!event_reservation.Succeeded()) { LOG_CRITICAL(Service, "Resource limit reached!"); return {}; @@ -46,7 +45,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { } // Initialize the event. - event->Initialize(std::move(name), process); + event->Initialize(process); // Commit the thread reservation. event_reservation.Commit(); @@ -59,7 +58,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { void ServiceContext::CloseEvent(Kernel::KEvent* event) { event->GetReadableEvent().Close(); - event->GetWritableEvent().Close(); + event->Close(); } } // namespace Service::KernelHelpers diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp index 8f3c045..1b4b474 100644 --- a/src/core/hle/service/ldn/lan_discovery.cpp +++ b/src/core/hle/service/ldn/lan_discovery.cpp @@ -487,7 +487,7 @@ void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) { std::scoped_lock lock{packet_mutex}; switch (packet.type) { case Network::LDNPacketType::Scan: { - LOG_INFO(Frontend, "Scan packet received!"); + LOG_DEBUG(Frontend, "Scan packet received!"); if (state == State::AccessPointCreated) { // Reply to the sender SendPacket(Network::LDNPacketType::ScanResp, network_info, packet.local_ip); @@ -495,7 +495,7 @@ void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) { break; } case Network::LDNPacketType::ScanResp: { - LOG_INFO(Frontend, "ScanResp packet received!"); + LOG_DEBUG(Frontend, "ScanResp packet received!"); NetworkInfo info{}; std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); @@ -611,13 +611,6 @@ MacAddress LANDiscovery::GetFakeMac() const { Result LANDiscovery::GetNodeInfo(NodeInfo& node, const UserConfig& userConfig, u16 localCommunicationVersion) { - const auto network_interface = Network::GetSelectedNetworkInterface(); - - if (!network_interface) { - LOG_ERROR(Service_LDN, "No network interface available"); - return ResultNoIpAddress; - } - node.mac_address = GetFakeMac(); node.is_connected = 1; std::memcpy(node.user_name.data(), userConfig.user_name.data(), UserNameBytesMax + 1); diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index ea3e7e5..addd40a 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -150,7 +150,7 @@ public: } ~IUserLocalCommunicationService() { - if (is_initialized) { + if (is_network_available) { if (auto room_member = room_network.GetRoomMember().lock()) { room_member->Unbind(ldn_packet_received); } @@ -165,7 +165,7 @@ public: } void OnEventFired() { - state_change_event->GetWritableEvent().Signal(); + state_change_event->Signal(); } void GetState(Kernel::HLERequestContext& ctx) { @@ -193,7 +193,7 @@ public: NetworkInfo network_info{}; const auto rc = lan_discovery.GetNetworkInfo(network_info); if (rc.IsError()) { - LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); + LOG_DEBUG(Service_LDN, "NetworkInfo is not valid {}", rc.raw); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(rc); return; @@ -205,6 +205,14 @@ public: } void GetIpv4Address(Kernel::HLERequestContext& ctx) { + if (!is_network_available) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushRaw(Ipv4Address{127, 0, 0, 1}); + rb.PushRaw(Ipv4Address{255, 255, 255, 0}); + return; + } + const auto network_interface = Network::GetSelectedNetworkInterface(); if (!network_interface) { @@ -292,7 +300,7 @@ public: void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) { const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0); - const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); + const std::size_t node_buffer_count = ctx.GetWriteBufferNumElements(1); if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size, @@ -333,7 +341,7 @@ public: const auto channel{rp.PopEnum()}; const auto scan_filter{rp.PopRaw()}; - const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo); + const std::size_t network_info_size = ctx.GetWriteBufferNumElements(); if (network_info_size == 0) { LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size); @@ -342,6 +350,13 @@ public: return; } + if (!is_network_available) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); + return; + } + u16 count = 0; std::vector network_infos(network_info_size); Result rc = lan_discovery.Scan(network_infos, count, scan_filter); @@ -488,18 +503,18 @@ public: } void Initialize(Kernel::HLERequestContext& ctx) { - const auto rc = InitializeImpl(ctx); - if (rc.IsError()) { - LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); - } + InitializeImpl(ctx); + // Initialize always returns success IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(rc); + rb.Push(ResultSuccess); } void Finalize(Kernel::HLERequestContext& ctx) { - if (auto room_member = room_network.GetRoomMember().lock()) { - room_member->Unbind(ldn_packet_received); + if (is_network_available) { + if (auto room_member = room_network.GetRoomMember().lock()) { + room_member->Unbind(ldn_packet_received); + } } is_initialized = false; @@ -519,22 +534,25 @@ public: } Result InitializeImpl(Kernel::HLERequestContext& ctx) { + lan_discovery.Initialize([&]() { OnEventFired(); }); + is_initialized = true; + is_network_available = false; + const auto network_interface = Network::GetSelectedNetworkInterface(); if (!network_interface) { LOG_ERROR(Service_LDN, "No network interface is set"); - return ResultAirplaneModeEnabled; + return ResultSuccess; } if (auto room_member = room_network.GetRoomMember().lock()) { ldn_packet_received = room_member->BindOnLdnPacketReceived( [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); }); + is_network_available = true; } else { LOG_ERROR(Service_LDN, "Couldn't bind callback!"); - return ResultAirplaneModeEnabled; + return ResultSuccess; } - lan_discovery.Initialize([&]() { OnEventFired(); }); - is_initialized = true; return ResultSuccess; } @@ -547,6 +565,7 @@ public: Network::RoomMember::CallbackHandle ldn_packet_received; bool is_initialized{}; + bool is_network_available{}; }; class LDNS final : public ServiceFramework { diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index becd6d1..652441b 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -290,7 +290,7 @@ public: const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize}; const auto start_info{page_table.QueryInfo(start - 1)}; - if (start_info.state != Kernel::KMemoryState::Free) { + if (start_info.GetState() != Kernel::KMemoryState::Free) { return {}; } @@ -300,7 +300,7 @@ public: const auto end_info{page_table.QueryInfo(start + size)}; - if (end_info.state != Kernel::KMemoryState::Free) { + if (end_info.GetState() != Kernel::KMemoryState::Free) { return {}; } diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index c484a9c..3a2fe93 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -427,12 +427,11 @@ CharInfo MiiManager::BuildDefault(std::size_t index) { return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); } -CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { +CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { Service::Mii::MiiManager manager; auto mii = manager.BuildDefault(0); - // Check if mii data exist - if (mii_v3.mii_name[0] == 0) { + if (!ValidateV3Info(mii_v3)) { return mii; } @@ -443,8 +442,15 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { mii.height = mii_v3.height; mii.build = mii_v3.build; - memset(mii.name.data(), 0, sizeof(mii.name)); - memcpy(mii.name.data(), mii_v3.mii_name.data(), sizeof(mii_v3.mii_name)); + // Copy name until string terminator + mii.name = {}; + for (std::size_t index = 0; index < mii.name.size() - 1; index++) { + mii.name[index] = mii_v3.mii_name[index]; + if (mii.name[index] == 0) { + break; + } + } + mii.font_region = mii_v3.region_information.character_set; mii.faceline_type = mii_v3.appearance_bits1.face_shape; @@ -504,6 +510,151 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { return mii; } +Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const { + Service::Mii::MiiManager manager; + Ver3StoreData mii_v3{}; + + // TODO: We are ignoring a bunch of data from the mii_v3 + + mii_v3.version = 1; + mii_v3.mii_information.gender.Assign(mii.gender); + mii_v3.mii_information.favorite_color.Assign(mii.favorite_color); + mii_v3.height = mii.height; + mii_v3.build = mii.build; + + // Copy name until string terminator + mii_v3.mii_name = {}; + for (std::size_t index = 0; index < mii.name.size() - 1; index++) { + mii_v3.mii_name[index] = mii.name[index]; + if (mii_v3.mii_name[index] == 0) { + break; + } + } + + mii_v3.region_information.character_set.Assign(mii.font_region); + + mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type); + mii_v3.appearance_bits1.skin_color.Assign(mii.faceline_color); + mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle); + mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make); + + mii_v3.hair_style = mii.hair_type; + mii_v3.appearance_bits3.hair_color.Assign(mii.hair_color); + mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip); + + mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type); + mii_v3.appearance_bits4.eye_color.Assign(mii.eye_color); + mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale); + mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect); + mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate); + mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x); + mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y); + + mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type); + mii_v3.appearance_bits5.eyebrow_color.Assign(mii.eyebrow_color); + mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale); + mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect); + mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate); + mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x); + mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y); + + mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type); + mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale); + mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y); + + mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type); + mii_v3.appearance_bits7.mouth_color.Assign(mii.mouth_color); + mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale); + mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect); + mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y); + + mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type); + mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale); + mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y); + + mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type); + mii_v3.appearance_bits9.facial_hair_color.Assign(mii.beard_color); + + mii_v3.appearance_bits10.glasses_type.Assign(mii.glasses_type); + mii_v3.appearance_bits10.glasses_color.Assign(mii.glasses_color); + mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale); + mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y); + + mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type); + mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale); + mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x); + mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y); + + // TODO: Validate mii_v3 data + + return mii_v3; +} + +bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const { + bool is_valid = mii_v3.version == 0 || mii_v3.version == 3; + + is_valid = is_valid && (mii_v3.mii_name[0] != 0); + + is_valid = is_valid && (mii_v3.mii_information.birth_month < 13); + is_valid = is_valid && (mii_v3.mii_information.birth_day < 32); + is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12); + is_valid = is_valid && (mii_v3.height < 128); + is_valid = is_valid && (mii_v3.build < 128); + + is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12); + is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7); + is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12); + is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12); + + is_valid = is_valid && (mii_v3.hair_style < 132); + is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8); + + is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18); + is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7); + is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6); + is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7); + is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17); + + is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6); + is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8); + + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21); + + is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31); + + return is_valid; +} + ResultVal> MiiManager::GetDefault(SourceFlag source_flag) { std::vector result; diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index d847de0..83ad3d3 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -22,7 +22,9 @@ public: ResultVal UpdateLatest(const CharInfo& info, SourceFlag source_flag); CharInfo BuildRandom(Age age, Gender gender, Race race); CharInfo BuildDefault(std::size_t index); - CharInfo ConvertV3ToCharInfo(Ver3StoreData mii_v3) const; + CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; + Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const; + bool ValidateV3Info(const Ver3StoreData& mii_v3) const; ResultVal> GetDefault(SourceFlag source_flag); Result GetIndex(const CharInfo& info, u32& index); diff --git a/src/core/hle/service/nfc/mifare_user.cpp b/src/core/hle/service/nfc/mifare_user.cpp new file mode 100644 index 0000000..51523a3 --- /dev/null +++ b/src/core/hle/service/nfc/mifare_user.cpp @@ -0,0 +1,400 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/nfc/mifare_user.h" +#include "core/hle/service/nfc/nfc_device.h" +#include "core/hle/service/nfc/nfc_result.h" + +namespace Service::NFC { + +MFIUser::MFIUser(Core::System& system_) + : ServiceFramework{system_, "NFC::MFIUser"}, service_context{system_, service_name} { + static const FunctionInfo functions[] = { + {0, &MFIUser::Initialize, "Initialize"}, + {1, &MFIUser::Finalize, "Finalize"}, + {2, &MFIUser::ListDevices, "ListDevices"}, + {3, &MFIUser::StartDetection, "StartDetection"}, + {4, &MFIUser::StopDetection, "StopDetection"}, + {5, &MFIUser::Read, "Read"}, + {6, &MFIUser::Write, "Write"}, + {7, &MFIUser::GetTagInfo, "GetTagInfo"}, + {8, &MFIUser::GetActivateEventHandle, "GetActivateEventHandle"}, + {9, &MFIUser::GetDeactivateEventHandle, "GetDeactivateEventHandle"}, + {10, &MFIUser::GetState, "GetState"}, + {11, &MFIUser::GetDeviceState, "GetDeviceState"}, + {12, &MFIUser::GetNpadId, "GetNpadId"}, + {13, &MFIUser::GetAvailabilityChangeEventHandle, "GetAvailabilityChangeEventHandle"}, + }; + RegisterHandlers(functions); + + availability_change_event = service_context.CreateEvent("MFIUser:AvailabilityChangeEvent"); + + for (u32 device_index = 0; device_index < 10; device_index++) { + devices[device_index] = + std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, + service_context, availability_change_event); + } +} + +MFIUser ::~MFIUser() { + availability_change_event->Close(); +} + +void MFIUser::Initialize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::Initialized; + + for (auto& device : devices) { + device->Initialize(); + } + + IPC::ResponseBuilder rb{ctx, 2, 0}; + rb.Push(ResultSuccess); +} + +void MFIUser::Finalize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::NonInitialized; + + for (auto& device : devices) { + device->Finalize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void MFIUser::ListDevices(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareInvalidArgument); + return; + } + + if (ctx.GetWriteBufferSize() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareInvalidArgument); + return; + } + + std::vector nfp_devices; + const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements(); + + for (const auto& device : devices) { + if (nfp_devices.size() >= max_allowed_devices) { + continue; + } + if (device->GetCurrentState() != NFP::DeviceState::Unavailable) { + nfp_devices.push_back(device->GetHandle()); + } + } + + if (nfp_devices.empty()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + ctx.WriteBuffer(nfp_devices); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(nfp_devices.size())); +} + +void MFIUser::StartDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + const auto result = device.value()->StartDetection(NFP::TagProtocol::All); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFIUser::StopDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + const auto result = device.value()->StopDetection(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFIUser::Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto buffer{ctx.ReadBuffer()}; + const auto number_of_commands{ctx.GetReadBufferNumElements()}; + std::vector read_commands(number_of_commands); + + memcpy(read_commands.data(), buffer.data(), + number_of_commands * sizeof(NFP::MifareReadBlockParameter)); + + LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", + device_handle, number_of_commands); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + Result result = ResultSuccess; + std::vector out_data(number_of_commands); + for (std::size_t i = 0; i < number_of_commands; i++) { + result = device.value()->MifareRead(read_commands[i], out_data[i]); + if (result.IsError()) { + break; + } + } + + ctx.WriteBuffer(out_data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFIUser::Write(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto buffer{ctx.ReadBuffer()}; + const auto number_of_commands{ctx.GetReadBufferNumElements()}; + std::vector write_commands(number_of_commands); + + memcpy(write_commands.data(), buffer.data(), + number_of_commands * sizeof(NFP::MifareWriteBlockParameter)); + + LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}", + device_handle, number_of_commands); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + Result result = ResultSuccess; + std::vector out_data(number_of_commands); + for (std::size_t i = 0; i < number_of_commands; i++) { + result = device.value()->MifareWrite(write_commands[i]); + if (result.IsError()) { + break; + } + } + + if (result.IsSuccess()) { + result = device.value()->Flush(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFIUser::GetTagInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + NFP::TagInfo tag_info{}; + const auto result = device.value()->GetTagInfo(tag_info, true); + ctx.WriteBuffer(tag_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFIUser::GetActivateEventHandle(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetActivateEvent()); +} + +void MFIUser::GetDeactivateEventHandle(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetDeactivateEvent()); +} + +void MFIUser::GetState(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(state); +} + +void MFIUser::GetDeviceState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetCurrentState()); +} + +void MFIUser::GetNpadId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetNpadId()); +} + +void MFIUser::GetAvailabilityChangeEventHandle(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +} + +std::optional> MFIUser::GetNfcDevice(u64 handle) { + for (auto& device : devices) { + if (device->GetHandle() == handle) { + return device; + } + } + return std::nullopt; +} + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/mifare_user.h b/src/core/hle/service/nfc/mifare_user.h new file mode 100644 index 0000000..0e0638c --- /dev/null +++ b/src/core/hle/service/nfc/mifare_user.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::NFC { +class NfcDevice; + +class MFIUser final : public ServiceFramework { +public: + explicit MFIUser(Core::System& system_); + ~MFIUser(); + +private: + enum class State : u32 { + NonInitialized, + Initialized, + }; + + void Initialize(Kernel::HLERequestContext& ctx); + void Finalize(Kernel::HLERequestContext& ctx); + void ListDevices(Kernel::HLERequestContext& ctx); + void StartDetection(Kernel::HLERequestContext& ctx); + void StopDetection(Kernel::HLERequestContext& ctx); + void Read(Kernel::HLERequestContext& ctx); + void Write(Kernel::HLERequestContext& ctx); + void GetTagInfo(Kernel::HLERequestContext& ctx); + void GetActivateEventHandle(Kernel::HLERequestContext& ctx); + void GetDeactivateEventHandle(Kernel::HLERequestContext& ctx); + void GetState(Kernel::HLERequestContext& ctx); + void GetDeviceState(Kernel::HLERequestContext& ctx); + void GetNpadId(Kernel::HLERequestContext& ctx); + void GetAvailabilityChangeEventHandle(Kernel::HLERequestContext& ctx); + + std::optional> GetNfcDevice(u64 handle); + + KernelHelpers::ServiceContext service_context; + + std::array, 10> devices{}; + + State state{State::NonInitialized}; + Kernel::KEvent* availability_change_event; +}; + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 13a843a..b17b18a 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -6,7 +6,9 @@ #include "common/logging/log.h" #include "common/settings.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/service/nfc/mifare_user.h" #include "core/hle/service/nfc/nfc.h" +#include "core/hle/service/nfc/nfc_user.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -49,32 +51,6 @@ private: } }; -class MFIUser final : public ServiceFramework { -public: - explicit MFIUser(Core::System& system_) : ServiceFramework{system_, "NFC::MFIUser"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "Initialize"}, - {1, nullptr, "Finalize"}, - {2, nullptr, "ListDevices"}, - {3, nullptr, "StartDetection"}, - {4, nullptr, "StopDetection"}, - {5, nullptr, "Read"}, - {6, nullptr, "Write"}, - {7, nullptr, "GetTagInfo"}, - {8, nullptr, "GetActivateEventHandle"}, - {9, nullptr, "GetDeactivateEventHandle"}, - {10, nullptr, "GetState"}, - {11, nullptr, "GetDeviceState"}, - {12, nullptr, "GetNpadId"}, - {13, nullptr, "GetAvailabilityChangeEventHandle"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - class NFC_MF_U final : public ServiceFramework { public: explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} { @@ -97,76 +73,6 @@ private: } }; -class IUser final : public ServiceFramework { -public: - explicit IUser(Core::System& system_) : ServiceFramework{system_, "NFC::IUser"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IUser::InitializeOld, "InitializeOld"}, - {1, &IUser::FinalizeOld, "FinalizeOld"}, - {2, &IUser::GetStateOld, "GetStateOld"}, - {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"}, - {400, nullptr, "Initialize"}, - {401, nullptr, "Finalize"}, - {402, nullptr, "GetState"}, - {403, nullptr, "IsNfcEnabled"}, - {404, nullptr, "ListDevices"}, - {405, nullptr, "GetDeviceState"}, - {406, nullptr, "GetNpadId"}, - {407, nullptr, "AttachAvailabilityChangeEvent"}, - {408, nullptr, "StartDetection"}, - {409, nullptr, "StopDetection"}, - {410, nullptr, "GetTagInfo"}, - {411, nullptr, "AttachActivateEvent"}, - {412, nullptr, "AttachDeactivateEvent"}, - {1000, nullptr, "ReadMifare"}, - {1001, nullptr, "WriteMifare"}, - {1300, nullptr, "SendCommandByPassThrough"}, - {1301, nullptr, "KeepPassThroughSession"}, - {1302, nullptr, "ReleasePassThroughSession"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - enum class NfcStates : u32 { - Finalized = 6, - }; - - void InitializeOld(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0}; - rb.Push(ResultSuccess); - // We don't deal with hardware initialization so we can just stub this. - } - - void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "IsNfcEnabledOld"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw(true); - } - - void GetStateOld(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFC, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(NfcStates::Finalized); // TODO(ogniK): Figure out if this matches nfp - } - - void FinalizeOld(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFC, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } -}; - class NFC_U final : public ServiceFramework { public: explicit NFC_U(Core::System& system_) : ServiceFramework{system_, "nfc:user"} { diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp new file mode 100644 index 0000000..78578f7 --- /dev/null +++ b/src/core/hle/service/nfc/nfc_device.cpp @@ -0,0 +1,273 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/input.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/nfc/nfc_device.h" +#include "core/hle/service/nfc/nfc_result.h" +#include "core/hle/service/nfc/nfc_user.h" + +namespace Service::NFC { +NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_) + : npad_id{npad_id_}, system{system_}, service_context{service_context_}, + availability_change_event{availability_change_event_} { + activate_event = service_context.CreateEvent("IUser:NFCActivateEvent"); + deactivate_event = service_context.CreateEvent("IUser:NFCDeactivateEvent"); + npad_device = system.HIDCore().GetEmulatedController(npad_id); + + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); }, + .is_npad_service = false, + }; + is_controller_set = true; + callback_key = npad_device->SetCallback(engine_callback); +} + +NfcDevice::~NfcDevice() { + activate_event->Close(); + deactivate_event->Close(); + if (!is_controller_set) { + return; + } + npad_device->DeleteCallback(callback_key); + is_controller_set = false; +}; + +void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { + if (type == Core::HID::ControllerTriggerType::Connected || + type == Core::HID::ControllerTriggerType::Disconnected) { + availability_change_event->Signal(); + return; + } + + if (type != Core::HID::ControllerTriggerType::Nfc) { + return; + } + + if (!npad_device->IsConnected()) { + return; + } + + const auto nfc_status = npad_device->GetNfc(); + switch (nfc_status.state) { + case Common::Input::NfcState::NewAmiibo: + LoadNfcTag(nfc_status.data); + break; + case Common::Input::NfcState::AmiiboRemoved: + if (device_state != NFP::DeviceState::SearchingForTag) { + CloseNfcTag(); + } + break; + default: + break; + } +} + +bool NfcDevice::LoadNfcTag(std::span data) { + if (device_state != NFP::DeviceState::SearchingForTag) { + LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state); + return false; + } + + if (data.size() < sizeof(NFP::EncryptedNTAG215File)) { + LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size()); + return false; + } + + tag_data.resize(data.size()); + memcpy(tag_data.data(), data.data(), data.size()); + memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); + + device_state = NFP::DeviceState::TagFound; + deactivate_event->GetReadableEvent().Clear(); + activate_event->Signal(); + return true; +} + +void NfcDevice::CloseNfcTag() { + LOG_INFO(Service_NFC, "Remove nfc tag"); + + device_state = NFP::DeviceState::TagRemoved; + encrypted_tag_data = {}; + activate_event->GetReadableEvent().Clear(); + deactivate_event->Signal(); +} + +Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const { + return activate_event->GetReadableEvent(); +} + +Kernel::KReadableEvent& NfcDevice::GetDeactivateEvent() const { + return deactivate_event->GetReadableEvent(); +} + +void NfcDevice::Initialize() { + device_state = + npad_device->HasNfc() ? NFP::DeviceState::Initialized : NFP::DeviceState::Unavailable; + encrypted_tag_data = {}; +} + +void NfcDevice::Finalize() { + if (device_state == NFP::DeviceState::SearchingForTag || + device_state == NFP::DeviceState::TagRemoved) { + StopDetection(); + } + device_state = NFP::DeviceState::Unavailable; +} + +Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) { + if (device_state != NFP::DeviceState::Initialized && + device_state != NFP::DeviceState::TagRemoved) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { + LOG_ERROR(Service_NFC, "Nfc not supported"); + return NfcDisabled; + } + + device_state = NFP::DeviceState::SearchingForTag; + allowed_protocols = allowed_protocol; + return ResultSuccess; +} + +Result NfcDevice::StopDetection() { + npad_device->SetPollingMode(Common::Input::PollingMode::Active); + + if (device_state == NFP::DeviceState::Initialized) { + return ResultSuccess; + } + + if (device_state == NFP::DeviceState::TagFound || + device_state == NFP::DeviceState::TagMounted) { + CloseNfcTag(); + return ResultSuccess; + } + if (device_state == NFP::DeviceState::SearchingForTag || + device_state == NFP::DeviceState::TagRemoved) { + device_state = NFP::DeviceState::Initialized; + return ResultSuccess; + } + + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + return WrongDeviceState; +} + +Result NfcDevice::Flush() { + if (device_state != NFP::DeviceState::TagFound && + device_state != NFP::DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == NFP::DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (!npad_device->WriteNfc(tag_data)) { + LOG_ERROR(Service_NFP, "Error writing to file"); + return MifareReadError; + } + + return ResultSuccess; +} + +Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { + if (device_state != NFP::DeviceState::TagFound && + device_state != NFP::DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == NFP::DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (is_mifare) { + tag_info = { + .uuid = encrypted_tag_data.uuid.uid, + .uuid_length = static_cast(encrypted_tag_data.uuid.uid.size()), + .protocol = NFP::TagProtocol::TypeA, + .tag_type = NFP::TagType::Type4, + }; + return ResultSuccess; + } + + // Protocol and tag type may change here + tag_info = { + .uuid = encrypted_tag_data.uuid.uid, + .uuid_length = static_cast(encrypted_tag_data.uuid.uid.size()), + .protocol = NFP::TagProtocol::TypeA, + .tag_type = NFP::TagType::Type2, + }; + + return ResultSuccess; +} + +Result NfcDevice::MifareRead(const NFP::MifareReadBlockParameter& parameter, + NFP::MifareReadBlockData& read_block_data) { + const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock); + read_block_data.sector_number = parameter.sector_number; + + if (device_state != NFP::DeviceState::TagFound && + device_state != NFP::DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == NFP::DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) { + return MifareReadError; + } + + // TODO: Use parameter.sector_key to read encrypted data + memcpy(read_block_data.data.data(), tag_data.data() + sector_index, sizeof(NFP::DataBlock)); + + return ResultSuccess; +} + +Result NfcDevice::MifareWrite(const NFP::MifareWriteBlockParameter& parameter) { + const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock); + + if (device_state != NFP::DeviceState::TagFound && + device_state != NFP::DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == NFP::DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) { + return MifareReadError; + } + + // TODO: Use parameter.sector_key to encrypt the data + memcpy(tag_data.data() + sector_index, parameter.data.data(), sizeof(NFP::DataBlock)); + + return ResultSuccess; +} + +u64 NfcDevice::GetHandle() const { + // Generate a handle based of the npad id + return static_cast(npad_id); +} + +NFP::DeviceState NfcDevice::GetCurrentState() const { + return device_state; +} + +Core::HID::NpadIdType NfcDevice::GetNpadId() const { + return npad_id; +} + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_device.h b/src/core/hle/service/nfc/nfc_device.h new file mode 100644 index 0000000..a6e114d --- /dev/null +++ b/src/core/hle/service/nfc/nfc_device.h @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nfp/nfp_types.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Core { +class System; +} // namespace Core + +namespace Core::HID { +class EmulatedController; +enum class ControllerTriggerType; +enum class NpadIdType : u32; +} // namespace Core::HID + +namespace Service::NFC { +class NfcDevice { +public: + NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_); + ~NfcDevice(); + + void Initialize(); + void Finalize(); + + Result StartDetection(NFP::TagProtocol allowed_protocol); + Result StopDetection(); + Result Flush(); + + Result GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const; + + Result MifareRead(const NFP::MifareReadBlockParameter& parameter, + NFP::MifareReadBlockData& read_block_data); + + Result MifareWrite(const NFP::MifareWriteBlockParameter& parameter); + + u64 GetHandle() const; + NFP::DeviceState GetCurrentState() const; + Core::HID::NpadIdType GetNpadId() const; + + Kernel::KReadableEvent& GetActivateEvent() const; + Kernel::KReadableEvent& GetDeactivateEvent() const; + +private: + void NpadUpdate(Core::HID::ControllerTriggerType type); + bool LoadNfcTag(std::span data); + void CloseNfcTag(); + + bool is_controller_set{}; + int callback_key; + const Core::HID::NpadIdType npad_id; + Core::System& system; + Core::HID::EmulatedController* npad_device = nullptr; + KernelHelpers::ServiceContext& service_context; + Kernel::KEvent* activate_event = nullptr; + Kernel::KEvent* deactivate_event = nullptr; + Kernel::KEvent* availability_change_event = nullptr; + + NFP::TagProtocol allowed_protocols{}; + NFP::DeviceState device_state{NFP::DeviceState::Unavailable}; + + NFP::EncryptedNTAG215File encrypted_tag_data{}; + std::vector tag_data{}; +}; + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h new file mode 100644 index 0000000..146b8ba --- /dev/null +++ b/src/core/hle/service/nfc/nfc_result.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::NFC { + +constexpr Result DeviceNotFound(ErrorModule::NFC, 64); +constexpr Result InvalidArgument(ErrorModule::NFC, 65); +constexpr Result WrongDeviceState(ErrorModule::NFC, 73); +constexpr Result NfcDisabled(ErrorModule::NFC, 80); +constexpr Result TagRemoved(ErrorModule::NFC, 97); + +constexpr Result MifareDeviceNotFound(ErrorModule::NFCMifare, 64); +constexpr Result MifareInvalidArgument(ErrorModule::NFCMifare, 65); +constexpr Result MifareWrongDeviceState(ErrorModule::NFCMifare, 73); +constexpr Result MifareNfcDisabled(ErrorModule::NFCMifare, 80); +constexpr Result MifareTagRemoved(ErrorModule::NFCMifare, 97); +constexpr Result MifareReadError(ErrorModule::NFCMifare, 288); + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_user.cpp b/src/core/hle/service/nfc/nfc_user.cpp new file mode 100644 index 0000000..89aa6b3 --- /dev/null +++ b/src/core/hle/service/nfc/nfc_user.cpp @@ -0,0 +1,365 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/nfc/nfc_device.h" +#include "core/hle/service/nfc/nfc_result.h" +#include "core/hle/service/nfc/nfc_user.h" +#include "core/hle/service/time/clock_types.h" + +namespace Service::NFC { + +IUser::IUser(Core::System& system_) + : ServiceFramework{system_, "NFC::IUser"}, service_context{system_, service_name} { + static const FunctionInfo functions[] = { + {0, &IUser::Initialize, "InitializeOld"}, + {1, &IUser::Finalize, "FinalizeOld"}, + {2, &IUser::GetState, "GetStateOld"}, + {3, &IUser::IsNfcEnabled, "IsNfcEnabledOld"}, + {400, &IUser::Initialize, "Initialize"}, + {401, &IUser::Finalize, "Finalize"}, + {402, &IUser::GetState, "GetState"}, + {403, &IUser::IsNfcEnabled, "IsNfcEnabled"}, + {404, &IUser::ListDevices, "ListDevices"}, + {405, &IUser::GetDeviceState, "GetDeviceState"}, + {406, &IUser::GetNpadId, "GetNpadId"}, + {407, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, + {408, &IUser::StartDetection, "StartDetection"}, + {409, &IUser::StopDetection, "StopDetection"}, + {410, &IUser::GetTagInfo, "GetTagInfo"}, + {411, &IUser::AttachActivateEvent, "AttachActivateEvent"}, + {412, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, + {1000, nullptr, "ReadMifare"}, + {1001, nullptr, "WriteMifare"}, + {1300, &IUser::SendCommandByPassThrough, "SendCommandByPassThrough"}, + {1301, nullptr, "KeepPassThroughSession"}, + {1302, nullptr, "ReleasePassThroughSession"}, + }; + RegisterHandlers(functions); + + availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); + + for (u32 device_index = 0; device_index < 10; device_index++) { + devices[device_index] = + std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, + service_context, availability_change_event); + } +} + +IUser ::~IUser() { + availability_change_event->Close(); +} + +void IUser::Initialize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::Initialized; + + for (auto& device : devices) { + device->Initialize(); + } + + IPC::ResponseBuilder rb{ctx, 2, 0}; + rb.Push(ResultSuccess); +} + +void IUser::Finalize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::NonInitialized; + + for (auto& device : devices) { + device->Finalize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IUser::GetState(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(state); +} + +void IUser::IsNfcEnabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(state != State::NonInitialized); +} + +void IUser::ListDevices(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + if (ctx.GetWriteBufferSize() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + std::vector nfp_devices; + const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements(); + + for (auto& device : devices) { + if (nfp_devices.size() >= max_allowed_devices) { + continue; + } + if (device->GetCurrentState() != NFP::DeviceState::Unavailable) { + nfp_devices.push_back(device->GetHandle()); + } + } + + if (nfp_devices.empty()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + ctx.WriteBuffer(nfp_devices); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(nfp_devices.size())); +} + +void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetCurrentState()); +} + +void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetNpadId()); +} + +void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +} + +void IUser::StartDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto nfp_protocol{rp.PopEnum()}; + LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StartDetection(nfp_protocol); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::StopDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StopDetection(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + NFP::TagInfo tag_info{}; + const auto result = device.value()->GetTagInfo(tag_info, false); + ctx.WriteBuffer(tag_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetActivateEvent()); +} + +void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetDeactivateEvent()); +} + +void IUser::SendCommandByPassThrough(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto timeout{rp.PopRaw()}; + const auto command_data{ctx.ReadBuffer()}; + + LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}", + device_handle, timeout.ToSeconds(), command_data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + std::vector out_data(1); + // TODO: Request data from nfc device + ctx.WriteBuffer(out_data); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(out_data.size())); +} + +std::optional> IUser::GetNfcDevice(u64 handle) { + for (auto& device : devices) { + if (device->GetHandle() == handle) { + return device; + } + } + return std::nullopt; +} + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_user.h b/src/core/hle/service/nfc/nfc_user.h new file mode 100644 index 0000000..a5a4f12 --- /dev/null +++ b/src/core/hle/service/nfc/nfc_user.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::NFC { +class NfcDevice; + +class IUser final : public ServiceFramework { +public: + explicit IUser(Core::System& system_); + ~IUser(); + +private: + enum class State : u32 { + NonInitialized, + Initialized, + }; + + void Initialize(Kernel::HLERequestContext& ctx); + void Finalize(Kernel::HLERequestContext& ctx); + void GetState(Kernel::HLERequestContext& ctx); + void IsNfcEnabled(Kernel::HLERequestContext& ctx); + void ListDevices(Kernel::HLERequestContext& ctx); + void GetDeviceState(Kernel::HLERequestContext& ctx); + void GetNpadId(Kernel::HLERequestContext& ctx); + void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); + void StartDetection(Kernel::HLERequestContext& ctx); + void StopDetection(Kernel::HLERequestContext& ctx); + void GetTagInfo(Kernel::HLERequestContext& ctx); + void AttachActivateEvent(Kernel::HLERequestContext& ctx); + void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); + void SendCommandByPassThrough(Kernel::HLERequestContext& ctx); + + std::optional> GetNfcDevice(u64 handle); + + KernelHelpers::ServiceContext service_context; + + std::array, 10> devices{}; + + State state{State::NonInitialized}; + Kernel::KEvent* availability_change_event; +}; + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp index 31dd3a3..ffb2f95 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.cpp +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp @@ -9,9 +9,9 @@ #include #include "common/fs/file.h" +#include "common/fs/fs.h" #include "common/fs/path_util.h" #include "common/logging/log.h" -#include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/nfp/amiibo_crypto.h" namespace Service::NFP::AmiiboCrypto { @@ -20,14 +20,15 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { const auto& amiibo_data = ntag_file.user_memory; LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); - LOG_INFO(Service_NFP, "write_count={}", amiibo_data.write_counter); + LOG_DEBUG(Service_NFP, "write_count={}", static_cast(amiibo_data.write_counter)); - LOG_INFO(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); - LOG_INFO(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); - LOG_INFO(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); - LOG_INFO(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number); - LOG_INFO(Service_NFP, "series={}", amiibo_data.model_info.series); - LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value); + LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); + LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); + LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); + LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", + static_cast(amiibo_data.model_info.model_number)); + LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); + LOG_DEBUG(Service_NFP, "tag_type=0x{0:x}", amiibo_data.model_info.tag_type); LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0); @@ -35,11 +36,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { // Validate UUID constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` - if ((CT ^ ntag_file.uuid[0] ^ ntag_file.uuid[1] ^ ntag_file.uuid[2]) != ntag_file.uuid[3]) { + if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) != + ntag_file.uuid.uid[3]) { return false; } - if ((ntag_file.uuid[4] ^ ntag_file.uuid[5] ^ ntag_file.uuid[6] ^ ntag_file.uuid[7]) != - ntag_file.uuid[8]) { + if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^ + ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) { return false; } @@ -53,11 +55,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { if (amiibo_data.constant_value != 0xA5) { return false; } - if (amiibo_data.model_info.constant_value != 0x02) { + if (amiibo_data.model_info.tag_type != PackedTagType::Type2) { + return false; + } + if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) { return false; } - // dynamic_lock value apparently is not constant - // ntag_file.dynamic_lock == 0x0F0001 if (ntag_file.CFG0 != 0x04000000U) { return false; } @@ -70,7 +73,8 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { NTAG215File encoded_data{}; - memcpy(encoded_data.uuid2.data(), nfc_data.uuid.data() + 0x8, sizeof(encoded_data.uuid2)); + encoded_data.uid = nfc_data.uuid.uid; + encoded_data.nintendo_id = nfc_data.uuid.nintendo_id; encoded_data.static_lock = nfc_data.static_lock; encoded_data.compability_container = nfc_data.compability_container; encoded_data.hmac_data = nfc_data.user_memory.hmac_data; @@ -82,10 +86,10 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter; encoded_data.application_area_id = nfc_data.user_memory.application_area_id; encoded_data.unknown = nfc_data.user_memory.unknown; - encoded_data.hash = nfc_data.user_memory.hash; + encoded_data.unknown2 = nfc_data.user_memory.unknown2; encoded_data.application_area = nfc_data.user_memory.application_area; encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; - memcpy(encoded_data.uuid.data(), nfc_data.uuid.data(), sizeof(encoded_data.uuid)); + encoded_data.lock_bytes = nfc_data.uuid.lock_bytes; encoded_data.model_info = nfc_data.user_memory.model_info; encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; encoded_data.dynamic_lock = nfc_data.dynamic_lock; @@ -99,8 +103,9 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { EncryptedNTAG215File nfc_data{}; - memcpy(nfc_data.uuid.data() + 0x8, encoded_data.uuid2.data(), sizeof(encoded_data.uuid2)); - memcpy(nfc_data.uuid.data(), encoded_data.uuid.data(), sizeof(encoded_data.uuid)); + nfc_data.uuid.uid = encoded_data.uid; + nfc_data.uuid.nintendo_id = encoded_data.nintendo_id; + nfc_data.uuid.lock_bytes = encoded_data.lock_bytes; nfc_data.static_lock = encoded_data.static_lock; nfc_data.compability_container = encoded_data.compability_container; nfc_data.user_memory.hmac_data = encoded_data.hmac_data; @@ -112,7 +117,7 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter; nfc_data.user_memory.application_area_id = encoded_data.application_area_id; nfc_data.user_memory.unknown = encoded_data.unknown; - nfc_data.user_memory.hash = encoded_data.hash; + nfc_data.user_memory.unknown2 = encoded_data.unknown2; nfc_data.user_memory.application_area = encoded_data.application_area; nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag; nfc_data.user_memory.model_info = encoded_data.model_info; @@ -127,10 +132,10 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { u32 GetTagPassword(const TagUuid& uuid) { // Verifiy that the generated password is correct - u32 password = 0xAA ^ (uuid[1] ^ uuid[3]); - password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8; - password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16; - password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24; + u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]); + password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8; + password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16; + password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24; return password; } @@ -138,15 +143,13 @@ HashSeed GetSeed(const NTAG215File& data) { HashSeed seed{ .magic = data.write_counter, .padding = {}, - .uuid1 = {}, - .uuid2 = {}, + .uid_1 = data.uid, + .nintendo_id_1 = data.nintendo_id, + .uid_2 = data.uid, + .nintendo_id_2 = data.nintendo_id, .keygen_salt = data.keygen_salt, }; - // Copy the first 8 bytes of uuid - memcpy(seed.uuid1.data(), data.uuid.data(), sizeof(seed.uuid1)); - memcpy(seed.uuid2.data(), data.uuid.data(), sizeof(seed.uuid2)); - return seed; } @@ -165,8 +168,10 @@ std::vector GenerateInternalKey(const InternalKey& key, const HashSeed& seed output.insert(output.end(), key.magic_bytes.begin(), key.magic_bytes.begin() + key.magic_length); - output.insert(output.end(), seed.uuid1.begin(), seed.uuid1.end()); - output.insert(output.end(), seed.uuid2.begin(), seed.uuid2.end()); + output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end()); + output.emplace_back(seed.nintendo_id_1); + output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end()); + output.emplace_back(seed.nintendo_id_2); for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { output.emplace_back(static_cast(seed.keygen_salt[i] ^ key.xor_pad[i])); @@ -177,7 +182,6 @@ std::vector GenerateInternalKey(const InternalKey& key, const HashSeed& seed void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, const std::vector& seed) { - // Initialize context ctx.used = false; ctx.counter = 0; @@ -250,14 +254,15 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou reinterpret_cast(&out_data.settings)); // Copy the rest of the data directly - out_data.uuid2 = in_data.uuid2; + out_data.uid = in_data.uid; + out_data.nintendo_id = in_data.nintendo_id; + out_data.lock_bytes = in_data.lock_bytes; out_data.static_lock = in_data.static_lock; out_data.compability_container = in_data.compability_container; out_data.constant_value = in_data.constant_value; out_data.write_counter = in_data.write_counter; - out_data.uuid = in_data.uuid; out_data.model_info = in_data.model_info; out_data.keygen_salt = in_data.keygen_salt; out_data.dynamic_lock = in_data.dynamic_lock; @@ -274,7 +279,7 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) { Common::FS::FileType::BinaryFile}; if (!keys_file.IsOpen()) { - LOG_ERROR(Service_NFP, "No keys detected"); + LOG_ERROR(Service_NFP, "Failed to open key file"); return false; } @@ -290,6 +295,11 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) { return true; } +bool IsKeyAvailable() { + const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); + return Common::FS::Exists(yuzu_keys_dir / "key_retail.bin"); +} + bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { InternalKey locked_secret{}; InternalKey unfixed_info{}; @@ -309,7 +319,7 @@ bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& t // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC! constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), - sizeof(HmacKey), reinterpret_cast(&tag_data.uuid), + sizeof(HmacKey), reinterpret_cast(&tag_data.uid), input_length, reinterpret_cast(&tag_data.hmac_tag)); // Regenerate data HMAC @@ -350,7 +360,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START; mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), - sizeof(HmacKey), reinterpret_cast(&tag_data.uuid), + sizeof(HmacKey), reinterpret_cast(&tag_data.uid), input_length, reinterpret_cast(&encoded_tag_data.hmac_tag)); // Init mbedtls HMAC context @@ -364,7 +374,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t input_length2); // Data mbedtls_md_hmac_update(&ctx, reinterpret_cast(&encoded_tag_data.hmac_tag), sizeof(HashData)); // Tag HMAC - mbedtls_md_hmac_update(&ctx, reinterpret_cast(&tag_data.uuid), + mbedtls_md_hmac_update(&ctx, reinterpret_cast(&tag_data.uid), input_length); mbedtls_md_hmac_finish(&ctx, reinterpret_cast(&encoded_tag_data.hmac_data)); diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h index af73359..1fa6117 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.h +++ b/src/core/hle/service/nfp/amiibo_crypto.h @@ -5,7 +5,7 @@ #include -#include "core/hle/service/nfp/amiibo_types.h" +#include "core/hle/service/nfp/nfp_types.h" struct mbedtls_md_context_t; @@ -22,10 +22,12 @@ using HmacKey = std::array; using DrgbOutput = std::array; struct HashSeed { - u16 magic; + u16_be magic; std::array padding; - std::array uuid1; - std::array uuid2; + UniqueSerialNumber uid_1; + u8 nintendo_id_1; + UniqueSerialNumber uid_2; + u8 nintendo_id_2; std::array keygen_salt; }; static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); @@ -89,6 +91,9 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou /// Loads both amiibo keys from key_retail.bin bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); +/// Returns true if key_retail.bin exist +bool IsKeyAvailable(); + /// Decodes encripted amiibo data returns true if output is valid bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); diff --git a/src/core/hle/service/nfp/amiibo_types.h b/src/core/hle/service/nfp/amiibo_types.h deleted file mode 100644 index bf2de81..0000000 --- a/src/core/hle/service/nfp/amiibo_types.h +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - -#include "core/hle/service/mii/types.h" - -namespace Service::NFP { -static constexpr std::size_t amiibo_name_length = 0xA; - -enum class ServiceType : u32 { - User, - Debug, - System, -}; - -enum class State : u32 { - NonInitialized, - Initialized, -}; - -enum class DeviceState : u32 { - Initialized, - SearchingForTag, - TagFound, - TagRemoved, - TagMounted, - Unaviable, - Finalized, -}; - -enum class ModelType : u32 { - Amiibo, -}; - -enum class MountTarget : u32 { - Rom, - Ram, - All, -}; - -enum class AmiiboType : u8 { - Figure, - Card, - Yarn, -}; - -enum class AmiiboSeries : u8 { - SuperSmashBros, - SuperMario, - ChibiRobo, - YoshiWoollyWorld, - Splatoon, - AnimalCrossing, - EightBitMario, - Skylanders, - Unknown8, - TheLegendOfZelda, - ShovelKnight, - Unknown11, - Kiby, - Pokemon, - MarioSportsSuperstars, - MonsterHunter, - BoxBoy, - Pikmin, - FireEmblem, - Metroid, - Others, - MegaMan, - Diablo, -}; - -using TagUuid = std::array; -using HashData = std::array; -using ApplicationArea = std::array; - -struct AmiiboDate { - u16 raw_date{}; - - u16 GetYear() const { - return static_cast(((raw_date & 0xFE00) >> 9) + 2000); - } - u8 GetMonth() const { - return static_cast(((raw_date & 0x01E0) >> 5) - 1); - } - u8 GetDay() const { - return static_cast(raw_date & 0x001F); - } -}; -static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); - -struct Settings { - union { - u8 raw{}; - - BitField<4, 1, u8> amiibo_initialized; - BitField<5, 1, u8> appdata_initialized; - }; -}; -static_assert(sizeof(Settings) == 1, "AmiiboDate is an invalid size"); - -struct AmiiboSettings { - Settings settings; - u8 country_code_id; - u16_be crc_counter; // Incremented each time crc is changed - AmiiboDate init_date; - AmiiboDate write_date; - u32_be crc; - std::array amiibo_name; // UTF-16 text -}; -static_assert(sizeof(AmiiboSettings) == 0x20, "AmiiboSettings is an invalid size"); - -struct AmiiboModelInfo { - u16 character_id; - u8 character_variant; - AmiiboType amiibo_type; - u16 model_number; - AmiiboSeries series; - u8 constant_value; // Must be 02 - INSERT_PADDING_BYTES(0x4); // Unknown -}; -static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); - -struct NTAG215Password { - u32 PWD; // Password to allow write access - u16 PACK; // Password acknowledge reply - u16 RFUI; // Reserved for future use -}; -static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); - -#pragma pack(1) -struct EncryptedAmiiboFile { - u8 constant_value; // Must be A5 - u16 write_counter; // Number of times the amiibo has been written? - INSERT_PADDING_BYTES(0x1); // Unknown 1 - AmiiboSettings settings; // Encrypted amiibo settings - HashData hmac_tag; // Hash - AmiiboModelInfo model_info; // Encrypted amiibo model info - HashData keygen_salt; // Salt - HashData hmac_data; // Hash - Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data - u64_be title_id; // Encrypted Game id - u16_be applicaton_write_counter; // Encrypted Counter - u32_be application_area_id; // Encrypted Game id - std::array unknown; - HashData hash; // Probably a SHA256-HMAC hash? - ApplicationArea application_area; // Encrypted Game data -}; -static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); - -struct NTAG215File { - std::array uuid2; - u16 static_lock; // Set defined pages as read only - u32 compability_container; // Defines available memory - HashData hmac_data; // Hash - u8 constant_value; // Must be A5 - u16 write_counter; // Number of times the amiibo has been written? - INSERT_PADDING_BYTES(0x1); // Unknown 1 - AmiiboSettings settings; - Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data - u64_be title_id; - u16_be applicaton_write_counter; // Encrypted Counter - u32_be application_area_id; - std::array unknown; - HashData hash; // Probably a SHA256-HMAC hash? - ApplicationArea application_area; // Encrypted Game data - HashData hmac_tag; // Hash - std::array uuid; - AmiiboModelInfo model_info; - HashData keygen_salt; // Salt - u32 dynamic_lock; // Dynamic lock - u32 CFG0; // Defines memory protected by password - u32 CFG1; // Defines number of verification attempts - NTAG215Password password; // Password data -}; -static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size"); -static_assert(std::is_trivially_copyable_v, "NTAG215File must be trivially copyable."); -#pragma pack() - -struct EncryptedNTAG215File { - TagUuid uuid; // Unique serial number - u16 static_lock; // Set defined pages as read only - u32 compability_container; // Defines available memory - EncryptedAmiiboFile user_memory; // Writable data - u32 dynamic_lock; // Dynamic lock - u32 CFG0; // Defines memory protected by password - u32 CFG1; // Defines number of verification attempts - NTAG215Password password; // Password data -}; -static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an invalid size"); -static_assert(std::is_trivially_copyable_v, - "EncryptedNTAG215File must be trivially copyable."); - -} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index e0ed3f7..0cb55ca 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -1,1098 +1,43 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include - -#include "common/fs/file.h" -#include "common/fs/path_util.h" #include "common/logging/log.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hid/hid_types.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/mii/mii_manager.h" -#include "core/hle/service/nfp/amiibo_crypto.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nfp/nfp_user.h" namespace Service::NFP { -namespace ErrCodes { -constexpr Result DeviceNotFound(ErrorModule::NFP, 64); -constexpr Result WrongDeviceState(ErrorModule::NFP, 73); -constexpr Result NfcDisabled(ErrorModule::NFP, 80); -constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); -constexpr Result TagRemoved(ErrorModule::NFP, 97); -constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); -constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); -constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); -} // namespace ErrCodes -IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_) - : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name}, - nfp_interface{nfp_interface_} { - static const FunctionInfo functions[] = { - {0, &IUser::Initialize, "Initialize"}, - {1, &IUser::Finalize, "Finalize"}, - {2, &IUser::ListDevices, "ListDevices"}, - {3, &IUser::StartDetection, "StartDetection"}, - {4, &IUser::StopDetection, "StopDetection"}, - {5, &IUser::Mount, "Mount"}, - {6, &IUser::Unmount, "Unmount"}, - {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, - {8, &IUser::GetApplicationArea, "GetApplicationArea"}, - {9, &IUser::SetApplicationArea, "SetApplicationArea"}, - {10, &IUser::Flush, "Flush"}, - {11, nullptr, "Restore"}, - {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, - {13, &IUser::GetTagInfo, "GetTagInfo"}, - {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, - {15, &IUser::GetCommonInfo, "GetCommonInfo"}, - {16, &IUser::GetModelInfo, "GetModelInfo"}, - {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, - {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, - {19, &IUser::GetState, "GetState"}, - {20, &IUser::GetDeviceState, "GetDeviceState"}, - {21, &IUser::GetNpadId, "GetNpadId"}, - {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, - {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, - {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"}, - }; - RegisterHandlers(functions); - - availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); -} - -void IUser::Initialize(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); - - state = State::Initialized; - - // TODO(german77): Loop through all interfaces - nfp_interface.Initialize(); - - IPC::ResponseBuilder rb{ctx, 2, 0}; - rb.Push(ResultSuccess); -} - -void IUser::Finalize(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_NFP, "called"); - - state = State::NonInitialized; - - // TODO(german77): Loop through all interfaces - nfp_interface.Finalize(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IUser::ListDevices(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_NFP, "called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - std::vector devices; - - // TODO(german77): Loop through all interfaces - devices.push_back(nfp_interface.GetHandle()); - - if (devices.size() == 0) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); - return; - } - - ctx.WriteBuffer(devices); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(devices.size())); -} - -void IUser::StartDetection(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto nfp_protocol{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.StartDetection(nfp_protocol); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::StopDetection(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.StopDetection(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::Mount(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto model_type{rp.PopEnum()}; - const auto mount_target{rp.PopEnum()}; - LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, - model_type, mount_target); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.Mount(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::Unmount(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.Unmount(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto access_id{rp.Pop()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle, - access_id); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.OpenApplicationArea(access_id); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - ApplicationArea data{}; - const auto result = nfp_interface.GetApplicationArea(data); - ctx.WriteBuffer(data); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(static_cast(data.size())); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto data{ctx.ReadBuffer()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle, - data.size()); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.SetApplicationArea(data); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::Flush(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.Flush(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto access_id{rp.Pop()}; - const auto data{ctx.ReadBuffer()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}", - device_handle, access_id, data.size()); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.CreateApplicationArea(access_id, data); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - TagInfo tag_info{}; - const auto result = nfp_interface.GetTagInfo(tag_info); - ctx.WriteBuffer(tag_info); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - RegisterInfo register_info{}; - const auto result = nfp_interface.GetRegisterInfo(register_info); - ctx.WriteBuffer(register_info); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - CommonInfo common_info{}; - const auto result = nfp_interface.GetCommonInfo(common_info); - ctx.WriteBuffer(common_info); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - ModelInfo model_info{}; - const auto result = nfp_interface.GetModelInfo(model_info); - ctx.WriteBuffer(model_info); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(nfp_interface.GetActivateEvent()); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(nfp_interface.GetDeactivateEvent()); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - IPC::ResponseBuilder rb{ctx, 3, 0}; - rb.Push(ResultSuccess); - rb.PushEnum(state); -} - -void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(nfp_interface.GetCurrentState()); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(nfp_interface.GetNpadId()); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(sizeof(ApplicationArea)); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "(STUBBED) called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(availability_change_event->GetReadableEvent()); -} - -void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto access_id{rp.Pop()}; - const auto data{ctx.ReadBuffer()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}", - device_handle, access_id, data.size()); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::NfcDisabled); - return; - } - - // TODO(german77): Loop through all interfaces - if (device_handle == nfp_interface.GetHandle()) { - const auto result = nfp_interface.RecreateApplicationArea(access_id, data); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::DeviceNotFound); -} - -Module::Interface::Interface(std::shared_ptr module_, Core::System& system_, - const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)}, - npad_id{Core::HID::NpadIdType::Player1}, service_context{system_, service_name} { - activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); - deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); -} - -Module::Interface::~Interface() = default; - -void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(*this, system); -} - -bool Module::Interface::LoadAmiiboFile(const std::string& filename) { - constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); - const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read, - Common::FS::FileType::BinaryFile}; - - if (!amiibo_file.IsOpen()) { - LOG_ERROR(Service_NFP, "Amiibo is already on use"); - return false; - } - - // Workaround for files with missing password data - std::array buffer{}; - if (amiibo_file.Read(buffer) < tag_size_without_password) { - LOG_ERROR(Service_NFP, "Failed to read amiibo file"); - return false; - } - memcpy(&encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File)); - - if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { - LOG_INFO(Service_NFP, "Invalid amiibo"); - return false; - } - - file_path = filename; - return true; -} - -bool Module::Interface::LoadAmiibo(const std::string& filename) { - if (device_state != DeviceState::SearchingForTag) { - LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); - return false; - } - - if (!LoadAmiiboFile(filename)) { - return false; - } - - device_state = DeviceState::TagFound; - activate_event->GetWritableEvent().Signal(); - return true; -} - -void Module::Interface::CloseAmiibo() { - LOG_INFO(Service_NFP, "Remove amiibo"); - device_state = DeviceState::TagRemoved; - is_data_decoded = false; - is_application_area_initialized = false; - encrypted_tag_data = {}; - tag_data = {}; - deactivate_event->GetWritableEvent().Signal(); -} - -Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const { - return activate_event->GetReadableEvent(); -} - -Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const { - return deactivate_event->GetReadableEvent(); -} - -void Module::Interface::Initialize() { - device_state = DeviceState::Initialized; - is_data_decoded = false; - is_application_area_initialized = false; - encrypted_tag_data = {}; - tag_data = {}; -} - -void Module::Interface::Finalize() { - if (device_state == DeviceState::TagMounted) { - Unmount(); - } - if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { - StopDetection(); - } - device_state = DeviceState::Unaviable; -} - -Result Module::Interface::StartDetection(s32 protocol_) { - auto npad_device = system.HIDCore().GetEmulatedController(npad_id); - - // TODO(german77): Add callback for when nfc data is available - - if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { - npad_device->SetPollingMode(Common::Input::PollingMode::NFC); - device_state = DeviceState::SearchingForTag; - protocol = protocol_; - return ResultSuccess; - } - - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; -} - -Result Module::Interface::StopDetection() { - auto npad_device = system.HIDCore().GetEmulatedController(npad_id); - npad_device->SetPollingMode(Common::Input::PollingMode::Active); - - if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { - CloseAmiibo(); - return ResultSuccess; - } - if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { - device_state = DeviceState::Initialized; - return ResultSuccess; - } - - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; -} - -Result Module::Interface::Flush() { - // Ignore write command if we can't encrypt the data - if (!is_data_decoded) { - return ResultSuccess; - } - - constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); - EncryptedNTAG215File tmp_encrypted_tag_data{}; - const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite, - Common::FS::FileType::BinaryFile}; - - if (!amiibo_file.IsOpen()) { - LOG_ERROR(Core, "Amiibo is already on use"); - return ErrCodes::WriteAmiiboFailed; - } - - // Workaround for files with missing password data - std::array buffer{}; - if (amiibo_file.Read(buffer) < tag_size_without_password) { - LOG_ERROR(Core, "Failed to read amiibo file"); - return ErrCodes::WriteAmiiboFailed; - } - memcpy(&tmp_encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File)); - - if (!AmiiboCrypto::IsAmiiboValid(tmp_encrypted_tag_data)) { - LOG_INFO(Service_NFP, "Invalid amiibo"); - return ErrCodes::WriteAmiiboFailed; - } - - bool is_uuid_equal = memcmp(tmp_encrypted_tag_data.uuid.data(), tag_data.uuid.data(), 8) == 0; - bool is_character_equal = tmp_encrypted_tag_data.user_memory.model_info.character_id == - tag_data.model_info.character_id; - if (!is_uuid_equal || !is_character_equal) { - LOG_ERROR(Service_NFP, "Not the same amiibo"); - return ErrCodes::WriteAmiiboFailed; - } - - if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { - LOG_ERROR(Service_NFP, "Failed to encode data"); - return ErrCodes::WriteAmiiboFailed; - } - - // Return to the start of the file - if (!amiibo_file.Seek(0)) { - LOG_ERROR(Service_NFP, "Error writting to file"); - return ErrCodes::WriteAmiiboFailed; - } - - if (!amiibo_file.Write(encrypted_tag_data)) { - LOG_ERROR(Service_NFP, "Error writting to file"); - return ErrCodes::WriteAmiiboFailed; - } - - return ResultSuccess; -} - -Result Module::Interface::Mount() { - if (device_state != DeviceState::TagFound) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; - } - - is_data_decoded = AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data); - LOG_INFO(Service_NFP, "Is amiibo decoded {}", is_data_decoded); - - is_application_area_initialized = false; - device_state = DeviceState::TagMounted; - return ResultSuccess; -} - -Result Module::Interface::Unmount() { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; - } - - is_data_decoded = false; - is_application_area_initialized = false; - device_state = DeviceState::TagFound; - return ResultSuccess; -} - -Result Module::Interface::GetTagInfo(TagInfo& tag_info) const { - if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; - } - - tag_info = { - .uuid = encrypted_tag_data.uuid, - .uuid_length = static_cast(encrypted_tag_data.uuid.size()), - .protocol = protocol, - .tag_type = static_cast(encrypted_tag_data.user_memory.model_info.amiibo_type), - }; - - return ResultSuccess; -} - -Result Module::Interface::GetCommonInfo(CommonInfo& common_info) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; - } - - if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) { - const auto& settings = tag_data.settings; - // TODO: Validate this data - common_info = { - .last_write_year = settings.write_date.GetYear(), - .last_write_month = settings.write_date.GetMonth(), - .last_write_day = settings.write_date.GetDay(), - .write_counter = settings.crc_counter, - .version = 1, - .application_area_size = sizeof(ApplicationArea), +class IUserManager final : public ServiceFramework { +public: + explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IUserManager::CreateUserInterface, "CreateUserInterface"}, }; - return ResultSuccess; + // clang-format on + + RegisterHandlers(functions); } - // Generate a generic answer - common_info = { - .last_write_year = 2022, - .last_write_month = 2, - .last_write_day = 7, - .write_counter = 0, - .version = 1, - .application_area_size = sizeof(ApplicationArea), - }; - return ResultSuccess; -} +private: + void CreateUserInterface(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); -Result Module::Interface::GetModelInfo(ModelInfo& model_info) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return ErrCodes::WrongDeviceState; - } - - const auto& model_info_data = encrypted_tag_data.user_memory.model_info; - model_info = { - .character_id = model_info_data.character_id, - .character_variant = model_info_data.character_variant, - .amiibo_type = model_info_data.amiibo_type, - .model_number = model_info_data.model_number, - .series = model_info_data.series, - .constant_value = model_info_data.constant_value, - }; - return ResultSuccess; -} - -Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; + if (user_interface == nullptr) { + user_interface = std::make_shared(system); } - return ErrCodes::WrongDeviceState; + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(user_interface); } - Service::Mii::MiiManager manager; - - if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) { - const auto& settings = tag_data.settings; - - // TODO: Validate this data - register_info = { - .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), - .first_write_year = settings.init_date.GetYear(), - .first_write_month = settings.init_date.GetMonth(), - .first_write_day = settings.init_date.GetDay(), - .amiibo_name = GetAmiiboName(settings), - .font_region = {}, - }; - - return ResultSuccess; - } - - // Generate a generic answer - register_info = { - .mii_char_info = manager.BuildDefault(0), - .first_write_year = 2022, - .first_write_month = 2, - .first_write_day = 7, - .amiibo_name = {'Y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o', 0}, - .font_region = {}, - }; - return ResultSuccess; -} - -Result Module::Interface::OpenApplicationArea(u32 access_id) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; - } - return ErrCodes::WrongDeviceState; - } - - // Fallback for lack of amiibo keys - if (!is_data_decoded) { - LOG_WARNING(Service_NFP, "Application area is not initialized"); - return ErrCodes::ApplicationAreaIsNotInitialized; - } - - if (tag_data.settings.settings.appdata_initialized == 0) { - LOG_WARNING(Service_NFP, "Application area is not initialized"); - return ErrCodes::ApplicationAreaIsNotInitialized; - } - - if (tag_data.application_area_id != access_id) { - LOG_WARNING(Service_NFP, "Wrong application area id"); - return ErrCodes::WrongApplicationAreaId; - } - - is_application_area_initialized = true; - return ResultSuccess; -} - -Result Module::Interface::GetApplicationArea(ApplicationArea& data) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; - } - return ErrCodes::WrongDeviceState; - } - - if (!is_application_area_initialized) { - LOG_ERROR(Service_NFP, "Application area is not initialized"); - return ErrCodes::ApplicationAreaIsNotInitialized; - } - - data = tag_data.application_area; - - return ResultSuccess; -} - -Result Module::Interface::SetApplicationArea(const std::vector& data) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; - } - return ErrCodes::WrongDeviceState; - } - - if (!is_application_area_initialized) { - LOG_ERROR(Service_NFP, "Application area is not initialized"); - return ErrCodes::ApplicationAreaIsNotInitialized; - } - - if (data.size() != sizeof(ApplicationArea)) { - LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); - return ResultUnknown; - } - - std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); - return ResultSuccess; -} - -Result Module::Interface::CreateApplicationArea(u32 access_id, const std::vector& data) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; - } - return ErrCodes::WrongDeviceState; - } - - if (tag_data.settings.settings.appdata_initialized != 0) { - LOG_ERROR(Service_NFP, "Application area already exist"); - return ErrCodes::ApplicationAreaExist; - } - - if (data.size() != sizeof(ApplicationArea)) { - LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); - return ResultUnknown; - } - - std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); - tag_data.application_area_id = access_id; - - return ResultSuccess; -} - -Result Module::Interface::RecreateApplicationArea(u32 access_id, const std::vector& data) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ErrCodes::TagRemoved; - } - return ErrCodes::WrongDeviceState; - } - - if (data.size() != sizeof(ApplicationArea)) { - LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); - return ResultUnknown; - } - - std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); - tag_data.application_area_id = access_id; - - return ResultSuccess; -} - -u64 Module::Interface::GetHandle() const { - // Generate a handle based of the npad id - return static_cast(npad_id); -} - -DeviceState Module::Interface::GetCurrentState() const { - return device_state; -} - -Core::HID::NpadIdType Module::Interface::GetNpadId() const { - // Return first connected npad id as a workaround for lack of a single nfc interface per - // controller - return system.HIDCore().GetFirstNpadId(); -} - -AmiiboName Module::Interface::GetAmiiboName(const AmiiboSettings& settings) const { - std::array settings_amiibo_name{}; - AmiiboName amiibo_name{}; - - // Convert from big endian to little endian - for (std::size_t i = 0; i < amiibo_name_length; i++) { - settings_amiibo_name[i] = static_cast(settings.amiibo_name[i]); - } - - // Convert from utf16 to utf8 - const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); - memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); - - return amiibo_name; -} + std::shared_ptr user_interface; +}; void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { - auto module = std::make_shared(); - std::make_shared(module, system)->InstallAsService(service_manager); + std::make_shared(system)->InstallAsService(service_manager); } } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 0de0b48..a25c362 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -3,170 +3,9 @@ #pragma once -#include -#include - -#include "common/common_funcs.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/mii/types.h" -#include "core/hle/service/nfp/amiibo_types.h" #include "core/hle/service/service.h" -namespace Kernel { -class KEvent; -class KReadableEvent; -} // namespace Kernel - -namespace Core::HID { -enum class NpadIdType : u32; -} // namespace Core::HID - namespace Service::NFP { -using AmiiboName = std::array; - -struct TagInfo { - TagUuid uuid; - u8 uuid_length; - INSERT_PADDING_BYTES(0x15); - s32 protocol; - u32 tag_type; - INSERT_PADDING_BYTES(0x30); -}; -static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); - -struct CommonInfo { - u16 last_write_year; - u8 last_write_month; - u8 last_write_day; - u16 write_counter; - u16 version; - u32 application_area_size; - INSERT_PADDING_BYTES(0x34); -}; -static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); - -struct ModelInfo { - u16 character_id; - u8 character_variant; - AmiiboType amiibo_type; - u16 model_number; - AmiiboSeries series; - u8 constant_value; // Must be 02 - INSERT_PADDING_BYTES(0x38); // Unknown -}; -static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); - -struct RegisterInfo { - Service::Mii::CharInfo mii_char_info; - u16 first_write_year; - u8 first_write_month; - u8 first_write_day; - AmiiboName amiibo_name; - u8 font_region; - INSERT_PADDING_BYTES(0x7A); -}; -static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); - -class Module final { -public: - class Interface : public ServiceFramework { - public: - explicit Interface(std::shared_ptr module_, Core::System& system_, - const char* name); - ~Interface() override; - - void CreateUserInterface(Kernel::HLERequestContext& ctx); - bool LoadAmiibo(const std::string& filename); - bool LoadAmiiboFile(const std::string& filename); - void CloseAmiibo(); - - void Initialize(); - void Finalize(); - - Result StartDetection(s32 protocol_); - Result StopDetection(); - Result Mount(); - Result Unmount(); - Result Flush(); - - Result GetTagInfo(TagInfo& tag_info) const; - Result GetCommonInfo(CommonInfo& common_info) const; - Result GetModelInfo(ModelInfo& model_info) const; - Result GetRegisterInfo(RegisterInfo& register_info) const; - - Result OpenApplicationArea(u32 access_id); - Result GetApplicationArea(ApplicationArea& data) const; - Result SetApplicationArea(const std::vector& data); - Result CreateApplicationArea(u32 access_id, const std::vector& data); - Result RecreateApplicationArea(u32 access_id, const std::vector& data); - - u64 GetHandle() const; - DeviceState GetCurrentState() const; - Core::HID::NpadIdType GetNpadId() const; - - Kernel::KReadableEvent& GetActivateEvent() const; - Kernel::KReadableEvent& GetDeactivateEvent() const; - - protected: - std::shared_ptr module; - - private: - AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; - - const Core::HID::NpadIdType npad_id; - - bool is_data_decoded{}; - bool is_application_area_initialized{}; - s32 protocol; - std::string file_path{}; - Kernel::KEvent* activate_event; - Kernel::KEvent* deactivate_event; - DeviceState device_state{DeviceState::Unaviable}; - KernelHelpers::ServiceContext service_context; - - NTAG215File tag_data{}; - EncryptedNTAG215File encrypted_tag_data{}; - }; -}; - -class IUser final : public ServiceFramework { -public: - explicit IUser(Module::Interface& nfp_interface_, Core::System& system_); - -private: - void Initialize(Kernel::HLERequestContext& ctx); - void Finalize(Kernel::HLERequestContext& ctx); - void ListDevices(Kernel::HLERequestContext& ctx); - void StartDetection(Kernel::HLERequestContext& ctx); - void StopDetection(Kernel::HLERequestContext& ctx); - void Mount(Kernel::HLERequestContext& ctx); - void Unmount(Kernel::HLERequestContext& ctx); - void OpenApplicationArea(Kernel::HLERequestContext& ctx); - void GetApplicationArea(Kernel::HLERequestContext& ctx); - void SetApplicationArea(Kernel::HLERequestContext& ctx); - void Flush(Kernel::HLERequestContext& ctx); - void CreateApplicationArea(Kernel::HLERequestContext& ctx); - void GetTagInfo(Kernel::HLERequestContext& ctx); - void GetRegisterInfo(Kernel::HLERequestContext& ctx); - void GetCommonInfo(Kernel::HLERequestContext& ctx); - void GetModelInfo(Kernel::HLERequestContext& ctx); - void AttachActivateEvent(Kernel::HLERequestContext& ctx); - void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); - void GetState(Kernel::HLERequestContext& ctx); - void GetDeviceState(Kernel::HLERequestContext& ctx); - void GetNpadId(Kernel::HLERequestContext& ctx); - void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); - void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); - void RecreateApplicationArea(Kernel::HLERequestContext& ctx); - - KernelHelpers::ServiceContext service_context; - - // TODO(german77): We should have a vector of interfaces - Module::Interface& nfp_interface; - - State state{State::NonInitialized}; - Kernel::KEvent* availability_change_event; -}; void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp new file mode 100644 index 0000000..c860fd1 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -0,0 +1,719 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "common/input.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "common/tiny_mt.h" +#include "core/core.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/mii/types.h" +#include "core/hle/service/nfp/amiibo_crypto.h" +#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfp/nfp_result.h" +#include "core/hle/service/nfp/nfp_user.h" +#include "core/hle/service/time/time_manager.h" +#include "core/hle/service/time/time_zone_content_manager.h" +#include "core/hle/service/time/time_zone_types.h" + +namespace Service::NFP { +NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_) + : npad_id{npad_id_}, system{system_}, service_context{service_context_}, + availability_change_event{availability_change_event_} { + activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); + deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); + npad_device = system.HIDCore().GetEmulatedController(npad_id); + + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); }, + .is_npad_service = false, + }; + is_controller_set = true; + callback_key = npad_device->SetCallback(engine_callback); + + auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; + current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point; +} + +NfpDevice::~NfpDevice() { + activate_event->Close(); + deactivate_event->Close(); + if (!is_controller_set) { + return; + } + npad_device->DeleteCallback(callback_key); + is_controller_set = false; +}; + +void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { + if (type == Core::HID::ControllerTriggerType::Connected || + type == Core::HID::ControllerTriggerType::Disconnected) { + availability_change_event->Signal(); + return; + } + + if (type != Core::HID::ControllerTriggerType::Nfc) { + return; + } + + if (!npad_device->IsConnected()) { + return; + } + + const auto nfc_status = npad_device->GetNfc(); + switch (nfc_status.state) { + case Common::Input::NfcState::NewAmiibo: + LoadAmiibo(nfc_status.data); + break; + case Common::Input::NfcState::AmiiboRemoved: + if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { + break; + } + if (device_state != DeviceState::SearchingForTag) { + CloseAmiibo(); + } + break; + default: + break; + } +} + +bool NfpDevice::LoadAmiibo(std::span data) { + if (device_state != DeviceState::SearchingForTag) { + LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); + return false; + } + + if (data.size() != sizeof(EncryptedNTAG215File)) { + LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size()); + return false; + } + + // TODO: Filter by allowed_protocols here + + memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); + + device_state = DeviceState::TagFound; + deactivate_event->GetReadableEvent().Clear(); + activate_event->Signal(); + return true; +} + +void NfpDevice::CloseAmiibo() { + LOG_INFO(Service_NFP, "Remove amiibo"); + + if (device_state == DeviceState::TagMounted) { + Unmount(); + } + + device_state = DeviceState::TagRemoved; + encrypted_tag_data = {}; + tag_data = {}; + activate_event->GetReadableEvent().Clear(); + deactivate_event->Signal(); +} + +Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const { + return activate_event->GetReadableEvent(); +} + +Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const { + return deactivate_event->GetReadableEvent(); +} + +void NfpDevice::Initialize() { + device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; + encrypted_tag_data = {}; + tag_data = {}; +} + +void NfpDevice::Finalize() { + if (device_state == DeviceState::TagMounted) { + Unmount(); + } + if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { + StopDetection(); + } + device_state = DeviceState::Unavailable; +} + +Result NfpDevice::StartDetection(TagProtocol allowed_protocol) { + if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { + LOG_ERROR(Service_NFP, "Nfc not supported"); + return NfcDisabled; + } + + device_state = DeviceState::SearchingForTag; + allowed_protocols = allowed_protocol; + return ResultSuccess; +} + +Result NfpDevice::StopDetection() { + npad_device->SetPollingMode(Common::Input::PollingMode::Active); + + if (device_state == DeviceState::Initialized) { + return ResultSuccess; + } + + if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { + CloseAmiibo(); + return ResultSuccess; + } + if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { + device_state = DeviceState::Initialized; + return ResultSuccess; + } + + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; +} + +Result NfpDevice::Flush() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + auto& settings = tag_data.settings; + + const auto& current_date = GetAmiiboDate(current_posix_time); + if (settings.write_date.raw_date != current_date.raw_date) { + settings.write_date = current_date; + settings.crc_counter++; + // TODO: Find how to calculate the crc check + // settings.crc = CalculateCRC(settings); + } + + tag_data.write_counter++; + + if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { + LOG_ERROR(Service_NFP, "Failed to encode data"); + return WriteAmiiboFailed; + } + + std::vector data(sizeof(encrypted_tag_data)); + memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + + if (!npad_device->WriteNfc(data)) { + LOG_ERROR(Service_NFP, "Error writing to file"); + return WriteAmiiboFailed; + } + + is_data_moddified = false; + + return ResultSuccess; +} + +Result NfpDevice::Mount(MountTarget mount_target_) { + if (device_state != DeviceState::TagFound) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { + LOG_ERROR(Service_NFP, "Not an amiibo"); + return NotAnAmiibo; + } + + // Mark amiibos as read only when keys are missing + if (!AmiiboCrypto::IsKeyAvailable()) { + LOG_ERROR(Service_NFP, "No keys detected"); + device_state = DeviceState::TagMounted; + mount_target = MountTarget::Rom; + return ResultSuccess; + } + + if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { + LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); + return CorruptedData; + } + + device_state = DeviceState::TagMounted; + mount_target = mount_target_; + return ResultSuccess; +} + +Result NfpDevice::Unmount() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + // Save data before unloading the amiibo + if (is_data_moddified) { + Flush(); + } + + device_state = DeviceState::TagFound; + mount_target = MountTarget::None; + is_app_area_open = false; + + return ResultSuccess; +} + +Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { + if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + tag_info = { + .uuid = encrypted_tag_data.uuid.uid, + .uuid_length = static_cast(encrypted_tag_data.uuid.uid.size()), + .protocol = TagProtocol::TypeA, + .tag_type = TagType::Type2, + }; + + return ResultSuccess; +} + +Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + const auto& settings = tag_data.settings; + + // TODO: Validate this data + common_info = { + .last_write_date = settings.write_date.GetWriteDate(), + .write_counter = tag_data.write_counter, + .version = 0, + .application_area_size = sizeof(ApplicationArea), + }; + return ResultSuccess; +} + +Result NfpDevice::GetModelInfo(ModelInfo& model_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + const auto& model_info_data = encrypted_tag_data.user_memory.model_info; + model_info = { + .character_id = model_info_data.character_id, + .character_variant = model_info_data.character_variant, + .amiibo_type = model_info_data.amiibo_type, + .model_number = model_info_data.model_number, + .series = model_info_data.series, + }; + return ResultSuccess; +} + +Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (tag_data.settings.settings.amiibo_initialized == 0) { + return RegistrationIsNotInitialized; + } + + Service::Mii::MiiManager manager; + const auto& settings = tag_data.settings; + + // TODO: Validate this data + register_info = { + .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), + .creation_date = settings.init_date.GetWriteDate(), + .amiibo_name = GetAmiiboName(settings), + .font_region = {}, + }; + + return ResultSuccess; +} + +Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + Service::Mii::MiiManager manager; + auto& settings = tag_data.settings; + + settings.init_date = GetAmiiboDate(current_posix_time); + settings.write_date = GetAmiiboDate(current_posix_time); + settings.crc_counter++; + // TODO: Find how to calculate the crc check + // settings.crc = CalculateCRC(settings); + + SetAmiiboName(settings, amiibo_name); + tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0)); + settings.settings.amiibo_initialized.Assign(1); + + return Flush(); +} + +Result NfpDevice::RestoreAmiibo() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + // TODO: Load amiibo from backup on system + LOG_ERROR(Service_NFP, "Not Implemented"); + return ResultSuccess; +} + +Result NfpDevice::DeleteAllData() { + const auto result = DeleteApplicationArea(); + if (result.IsError()) { + return result; + } + + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii)); + tag_data.settings.settings.amiibo_initialized.Assign(0); + + return Flush(); +} + +Result NfpDevice::OpenApplicationArea(u32 access_id) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_WARNING(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (tag_data.application_area_id != access_id) { + LOG_WARNING(Service_NFP, "Wrong application area id"); + return WrongApplicationAreaId; + } + + is_app_area_open = true; + + return ResultSuccess; +} + +Result NfpDevice::GetApplicationAreaId(u32& application_area_id) const { + application_area_id = {}; + + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_WARNING(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + application_area_id = tag_data.application_area_id; + + return ResultSuccess; +} + +Result NfpDevice::GetApplicationArea(std::vector& data) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (!is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is not open"); + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_ERROR(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (data.size() > sizeof(ApplicationArea)) { + data.resize(sizeof(ApplicationArea)); + } + + memcpy(data.data(), tag_data.application_area.data(), data.size()); + + return ResultSuccess; +} + +Result NfpDevice::SetApplicationArea(std::span data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (!is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is not open"); + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_ERROR(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (data.size() > sizeof(ApplicationArea)) { + LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); + return ResultUnknown; + } + + Common::TinyMT rng{}; + std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + // Fill remaining data with random numbers + rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), + sizeof(ApplicationArea) - data.size()); + + tag_data.applicaton_write_counter++; + is_data_moddified = true; + + return ResultSuccess; +} + +Result NfpDevice::CreateApplicationArea(u32 access_id, std::span data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() != 0) { + LOG_ERROR(Service_NFP, "Application area already exist"); + return ApplicationAreaExist; + } + + return RecreateApplicationArea(access_id, data); +} + +Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (data.size() > sizeof(ApplicationArea)) { + LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); + return WrongApplicationAreaSize; + } + + Common::TinyMT rng{}; + std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + // Fill remaining data with random numbers + rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), + sizeof(ApplicationArea) - data.size()); + + // TODO: Investigate why the title id needs to be moddified + tag_data.title_id = system.GetCurrentProcessProgramID(); + tag_data.title_id = tag_data.title_id | 0x30000000ULL; + tag_data.settings.settings.appdata_initialized.Assign(1); + tag_data.application_area_id = access_id; + tag_data.applicaton_write_counter++; + tag_data.unknown = {}; + + return Flush(); +} + +Result NfpDevice::DeleteApplicationArea() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); + rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64)); + rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32)); + tag_data.settings.settings.appdata_initialized.Assign(0); + tag_data.applicaton_write_counter++; + tag_data.unknown = {}; + + return Flush(); +} + +u64 NfpDevice::GetHandle() const { + // Generate a handle based of the npad id + return static_cast(npad_id); +} + +u32 NfpDevice::GetApplicationAreaSize() const { + return sizeof(ApplicationArea); +} + +DeviceState NfpDevice::GetCurrentState() const { + return device_state; +} + +Core::HID::NpadIdType NfpDevice::GetNpadId() const { + return npad_id; +} + +AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const { + std::array settings_amiibo_name{}; + AmiiboName amiibo_name{}; + + // Convert from big endian to little endian + for (std::size_t i = 0; i < amiibo_name_length; i++) { + settings_amiibo_name[i] = static_cast(settings.amiibo_name[i]); + } + + // Convert from utf16 to utf8 + const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); + memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); + + return amiibo_name; +} + +void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) { + std::array settings_amiibo_name{}; + + // Convert from utf8 to utf16 + const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data()); + memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(), + amiibo_name_utf16.size() * sizeof(char16_t)); + + // Convert from little endian to big endian + for (std::size_t i = 0; i < amiibo_name_length; i++) { + settings.amiibo_name[i] = static_cast(settings_amiibo_name[i]); + } +} + +AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const { + const auto& time_zone_manager = + system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); + Time::TimeZone::CalendarInfo calendar_info{}; + AmiiboDate amiibo_date{}; + + amiibo_date.SetYear(2000); + amiibo_date.SetMonth(1); + amiibo_date.SetDay(1); + + if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { + amiibo_date.SetYear(calendar_info.time.year); + amiibo_date.SetMonth(calendar_info.time.month); + amiibo_date.SetDay(calendar_info.time.day); + } + + return amiibo_date; +} + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h new file mode 100644 index 0000000..b6a46f2 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nfp/nfp_types.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Core { +class System; +} // namespace Core + +namespace Core::HID { +class EmulatedController; +enum class ControllerTriggerType; +enum class NpadIdType : u32; +} // namespace Core::HID + +namespace Service::NFP { +class NfpDevice { +public: + NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_); + ~NfpDevice(); + + void Initialize(); + void Finalize(); + + Result StartDetection(TagProtocol allowed_protocol); + Result StopDetection(); + Result Mount(MountTarget mount_target); + Result Unmount(); + Result Flush(); + + Result GetTagInfo(TagInfo& tag_info) const; + Result GetCommonInfo(CommonInfo& common_info) const; + Result GetModelInfo(ModelInfo& model_info) const; + Result GetRegisterInfo(RegisterInfo& register_info) const; + + Result SetNicknameAndOwner(const AmiiboName& amiibo_name); + Result RestoreAmiibo(); + Result DeleteAllData(); + + Result OpenApplicationArea(u32 access_id); + Result GetApplicationAreaId(u32& application_area_id) const; + Result GetApplicationArea(std::vector& data) const; + Result SetApplicationArea(std::span data); + Result CreateApplicationArea(u32 access_id, std::span data); + Result RecreateApplicationArea(u32 access_id, std::span data); + Result DeleteApplicationArea(); + + u64 GetHandle() const; + u32 GetApplicationAreaSize() const; + DeviceState GetCurrentState() const; + Core::HID::NpadIdType GetNpadId() const; + + Kernel::KReadableEvent& GetActivateEvent() const; + Kernel::KReadableEvent& GetDeactivateEvent() const; + +private: + void NpadUpdate(Core::HID::ControllerTriggerType type); + bool LoadAmiibo(std::span data); + void CloseAmiibo(); + + AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; + void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name); + AmiiboDate GetAmiiboDate(s64 posix_time) const; + + bool is_controller_set{}; + int callback_key; + const Core::HID::NpadIdType npad_id; + Core::System& system; + Core::HID::EmulatedController* npad_device = nullptr; + KernelHelpers::ServiceContext& service_context; + Kernel::KEvent* activate_event = nullptr; + Kernel::KEvent* deactivate_event = nullptr; + Kernel::KEvent* availability_change_event = nullptr; + + bool is_data_moddified{}; + bool is_app_area_open{}; + TagProtocol allowed_protocols{}; + s64 current_posix_time{}; + MountTarget mount_target{MountTarget::None}; + DeviceState device_state{DeviceState::Unavailable}; + + NTAG215File tag_data{}; + EncryptedNTAG215File encrypted_tag_data{}; +}; + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h new file mode 100644 index 0000000..d8e4cf0 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_result.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::NFP { + +constexpr Result DeviceNotFound(ErrorModule::NFP, 64); +constexpr Result InvalidArgument(ErrorModule::NFP, 65); +constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68); +constexpr Result WrongDeviceState(ErrorModule::NFP, 73); +constexpr Result NfcDisabled(ErrorModule::NFP, 80); +constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); +constexpr Result TagRemoved(ErrorModule::NFP, 97); +constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120); +constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); +constexpr Result CorruptedData(ErrorModule::NFP, 144); +constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); +constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); +constexpr Result NotAnAmiibo(ErrorModule::NFP, 178); + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h new file mode 100644 index 0000000..fc228c2 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_types.h @@ -0,0 +1,372 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/swap.h" +#include "core/hle/service/mii/types.h" + +namespace Service::NFP { +static constexpr std::size_t amiibo_name_length = 0xA; + +enum class ServiceType : u32 { + User, + Debug, + System, +}; + +enum class DeviceState : u32 { + Initialized, + SearchingForTag, + TagFound, + TagRemoved, + TagMounted, + Unavailable, + Finalized, +}; + +enum class ModelType : u32 { + Amiibo, +}; + +enum class MountTarget : u32 { + None, + Rom, + Ram, + All, +}; + +enum class AmiiboType : u8 { + Figure, + Card, + Yarn, +}; + +enum class AmiiboSeries : u8 { + SuperSmashBros, + SuperMario, + ChibiRobo, + YoshiWoollyWorld, + Splatoon, + AnimalCrossing, + EightBitMario, + Skylanders, + Unknown8, + TheLegendOfZelda, + ShovelKnight, + Unknown11, + Kiby, + Pokemon, + MarioSportsSuperstars, + MonsterHunter, + BoxBoy, + Pikmin, + FireEmblem, + Metroid, + Others, + MegaMan, + Diablo, +}; + +enum class TagType : u32 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony Felica RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s +}; + +enum class PackedTagType : u8 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony Felica RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s +}; + +// Verify this enum. It might be completely wrong default protocol is 0x48 +enum class TagProtocol : u32 { + None, + TypeA = 1U << 0, // ISO14443A + TypeB = 1U << 1, // ISO14443B + TypeF = 1U << 2, // Sony Felica + Unknown1 = 1U << 3, + Unknown2 = 1U << 5, + All = 0xFFFFFFFFU, +}; + +enum class CabinetMode : u8 { + StartNicknameAndOwnerSettings, + StartGameDataEraser, + StartRestorer, + StartFormatter, +}; + +enum class MifareCmd : u8 { + AuthA = 0x60, + AuthB = 0x61, + Read = 0x30, + Write = 0xA0, + Transfer = 0xB0, + Decrement = 0xC0, + Increment = 0xC1, + Store = 0xC2 +}; + +using UniqueSerialNumber = std::array; +using LockBytes = std::array; +using HashData = std::array; +using ApplicationArea = std::array; +using AmiiboName = std::array; +using DataBlock = std::array; +using KeyData = std::array; + +struct TagUuid { + UniqueSerialNumber uid; + u8 nintendo_id; + LockBytes lock_bytes; +}; +static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); + +struct WriteDate { + u16 year; + u8 month; + u8 day; +}; +static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); + +struct AmiiboDate { + u16 raw_date{}; + + u16 GetValue() const { + return Common::swap16(raw_date); + } + + u16 GetYear() const { + return static_cast(((GetValue() & 0xFE00) >> 9) + 2000); + } + u8 GetMonth() const { + return static_cast((GetValue() & 0x01E0) >> 5); + } + u8 GetDay() const { + return static_cast(GetValue() & 0x001F); + } + + WriteDate GetWriteDate() const { + if (!IsValidDate()) { + return { + .year = 2000, + .month = 1, + .day = 1, + }; + } + return { + .year = GetYear(), + .month = GetMonth(), + .day = GetDay(), + }; + } + + void SetYear(u16 year) { + const u16 year_converted = static_cast((year - 2000) << 9); + raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted); + } + void SetMonth(u8 month) { + const u16 month_converted = static_cast(month << 5); + raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted); + } + void SetDay(u8 day) { + const u16 day_converted = static_cast(day); + raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted); + } + + bool IsValidDate() const { + const bool is_day_valid = GetDay() > 0 && GetDay() < 32; + const bool is_month_valid = GetMonth() > 0 && GetMonth() < 13; + const bool is_year_valid = GetYear() >= 2000; + return is_year_valid && is_month_valid && is_day_valid; + } +}; +static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); + +struct Settings { + union { + u8 raw{}; + + BitField<4, 1, u8> amiibo_initialized; + BitField<5, 1, u8> appdata_initialized; + }; +}; +static_assert(sizeof(Settings) == 1, "AmiiboDate is an invalid size"); + +struct AmiiboSettings { + Settings settings; + u8 country_code_id; + u16_be crc_counter; // Incremented each time crc is changed + AmiiboDate init_date; + AmiiboDate write_date; + u32_be crc; + std::array amiibo_name; // UTF-16 text +}; +static_assert(sizeof(AmiiboSettings) == 0x20, "AmiiboSettings is an invalid size"); + +struct AmiiboModelInfo { + u16 character_id; + u8 character_variant; + AmiiboType amiibo_type; + u16_be model_number; + AmiiboSeries series; + PackedTagType tag_type; + INSERT_PADDING_BYTES(0x4); // Unknown +}; +static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); + +struct NTAG215Password { + u32 PWD; // Password to allow write access + u16 PACK; // Password acknowledge reply + u16 RFUI; // Reserved for future use +}; +static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); + +#pragma pack(1) +struct EncryptedAmiiboFile { + u8 constant_value; // Must be A5 + u16_be write_counter; // Number of times the amiibo has been written? + INSERT_PADDING_BYTES(0x1); // Unknown 1 + AmiiboSettings settings; // Encrypted amiibo settings + HashData hmac_tag; // Hash + AmiiboModelInfo model_info; // Encrypted amiibo model info + HashData keygen_salt; // Salt + HashData hmac_data; // Hash + Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data + u64_be title_id; // Encrypted Game id + u16_be applicaton_write_counter; // Encrypted Counter + u32_be application_area_id; // Encrypted Game id + std::array unknown; + std::array unknown2; + ApplicationArea application_area; // Encrypted Game data +}; +static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); + +struct NTAG215File { + LockBytes lock_bytes; // Tag UUID + u16 static_lock; // Set defined pages as read only + u32 compability_container; // Defines available memory + HashData hmac_data; // Hash + u8 constant_value; // Must be A5 + u16_be write_counter; // Number of times the amiibo has been written? + INSERT_PADDING_BYTES(0x1); // Unknown 1 + AmiiboSettings settings; + Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data + u64_be title_id; + u16_be applicaton_write_counter; // Encrypted Counter + u32_be application_area_id; + std::array unknown; + std::array unknown2; + ApplicationArea application_area; // Encrypted Game data + HashData hmac_tag; // Hash + UniqueSerialNumber uid; // Unique serial number + u8 nintendo_id; // Tag UUID + AmiiboModelInfo model_info; + HashData keygen_salt; // Salt + u32 dynamic_lock; // Dynamic lock + u32 CFG0; // Defines memory protected by password + u32 CFG1; // Defines number of verification attempts + NTAG215Password password; // Password data +}; +static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size"); +static_assert(std::is_trivially_copyable_v, "NTAG215File must be trivially copyable."); +#pragma pack() + +struct EncryptedNTAG215File { + TagUuid uuid; // Unique serial number + u16 static_lock; // Set defined pages as read only + u32 compability_container; // Defines available memory + EncryptedAmiiboFile user_memory; // Writable data + u32 dynamic_lock; // Dynamic lock + u32 CFG0; // Defines memory protected by password + u32 CFG1; // Defines number of verification attempts + NTAG215Password password; // Password data +}; +static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an invalid size"); +static_assert(std::is_trivially_copyable_v, + "EncryptedNTAG215File must be trivially copyable."); + +struct TagInfo { + UniqueSerialNumber uuid; + INSERT_PADDING_BYTES(0x3); + u8 uuid_length; + INSERT_PADDING_BYTES(0x15); + TagProtocol protocol; + TagType tag_type; + INSERT_PADDING_BYTES(0x30); +}; +static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); + +struct CommonInfo { + WriteDate last_write_date; + u16 write_counter; + u8 version; + INSERT_PADDING_BYTES(0x1); + u32 application_area_size; + INSERT_PADDING_BYTES(0x34); +}; +static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); + +struct ModelInfo { + u16 character_id; + u8 character_variant; + AmiiboType amiibo_type; + u16 model_number; + AmiiboSeries series; + INSERT_PADDING_BYTES(0x39); // Unknown +}; +static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); + +struct RegisterInfo { + Service::Mii::CharInfo mii_char_info; + WriteDate creation_date; + AmiiboName amiibo_name; + u8 font_region; + INSERT_PADDING_BYTES(0x7A); +}; +static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); + +struct SectorKey { + MifareCmd command; + u8 unknown; // Usually 1 + INSERT_PADDING_BYTES(0x6); + KeyData sector_key; + INSERT_PADDING_BYTES(0x2); +}; +static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size"); + +struct MifareReadBlockParameter { + u8 sector_number; + INSERT_PADDING_BYTES(0x7); + SectorKey sector_key; +}; +static_assert(sizeof(MifareReadBlockParameter) == 0x18, + "MifareReadBlockParameter is an invalid size"); + +struct MifareReadBlockData { + DataBlock data; + u8 sector_number; + INSERT_PADDING_BYTES(0x7); +}; +static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size"); + +struct MifareWriteBlockParameter { + DataBlock data; + u8 sector_number; + INSERT_PADDING_BYTES(0x7); + SectorKey sector_key; +}; +static_assert(sizeof(MifareWriteBlockParameter) == 0x28, + "MifareWriteBlockParameter is an invalid size"); + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 2d7b156..a4d3d1b 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -1,18 +1,672 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfp/nfp_result.h" #include "core/hle/service/nfp/nfp_user.h" namespace Service::NFP { -NFP_User::NFP_User(std::shared_ptr module_, Core::System& system_) - : Interface(std::move(module_), system_, "nfp:user") { +IUser::IUser(Core::System& system_) + : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} { static const FunctionInfo functions[] = { - {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, + {0, &IUser::Initialize, "Initialize"}, + {1, &IUser::Finalize, "Finalize"}, + {2, &IUser::ListDevices, "ListDevices"}, + {3, &IUser::StartDetection, "StartDetection"}, + {4, &IUser::StopDetection, "StopDetection"}, + {5, &IUser::Mount, "Mount"}, + {6, &IUser::Unmount, "Unmount"}, + {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, + {8, &IUser::GetApplicationArea, "GetApplicationArea"}, + {9, &IUser::SetApplicationArea, "SetApplicationArea"}, + {10, &IUser::Flush, "Flush"}, + {11, &IUser::Restore, "Restore"}, + {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, + {13, &IUser::GetTagInfo, "GetTagInfo"}, + {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, + {15, &IUser::GetCommonInfo, "GetCommonInfo"}, + {16, &IUser::GetModelInfo, "GetModelInfo"}, + {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, + {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, + {19, &IUser::GetState, "GetState"}, + {20, &IUser::GetDeviceState, "GetDeviceState"}, + {21, &IUser::GetNpadId, "GetNpadId"}, + {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, + {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, + {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"}, }; RegisterHandlers(functions); + + availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); + + for (u32 device_index = 0; device_index < 10; device_index++) { + devices[device_index] = + std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, + service_context, availability_change_event); + } } -NFP_User::~NFP_User() = default; +IUser ::~IUser() { + availability_change_event->Close(); +} + +void IUser::Initialize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + state = State::Initialized; + + for (auto& device : devices) { + device->Initialize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IUser::Finalize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + state = State::NonInitialized; + + for (auto& device : devices) { + device->Finalize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IUser::ListDevices(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + if (ctx.GetWriteBufferSize() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + std::vector nfp_devices; + const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements(); + + for (const auto& device : devices) { + if (nfp_devices.size() >= max_allowed_devices) { + continue; + } + if (device->GetCurrentState() != DeviceState::Unavailable) { + nfp_devices.push_back(device->GetHandle()); + } + } + + if (nfp_devices.empty()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + ctx.WriteBuffer(nfp_devices); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(nfp_devices.size())); +} + +void IUser::StartDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto nfp_protocol{rp.PopEnum()}; + LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StartDetection(nfp_protocol); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::StopDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StopDetection(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Mount(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto model_type{rp.PopEnum()}; + const auto mount_target{rp.PopEnum()}; + LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, + model_type, mount_target); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Mount(mount_target); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Unmount(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Unmount(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto access_id{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->OpenApplicationArea(access_id); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto data_size = ctx.GetWriteBufferSize(); + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + std::vector data(data_size); + const auto result = device.value()->GetApplicationArea(data); + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(static_cast(data_size)); +} + +void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanReadBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->SetApplicationArea(data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Flush(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Flush(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Restore(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->RestoreAmiibo(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto access_id{rp.Pop()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, + access_id, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanReadBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->CreateApplicationArea(access_id, data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + TagInfo tag_info{}; + const auto result = device.value()->GetTagInfo(tag_info); + ctx.WriteBuffer(tag_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + RegisterInfo register_info{}; + const auto result = device.value()->GetRegisterInfo(register_info); + ctx.WriteBuffer(register_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + CommonInfo common_info{}; + const auto result = device.value()->GetCommonInfo(common_info); + ctx.WriteBuffer(common_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + ModelInfo model_info{}; + const auto result = device.value()->GetModelInfo(model_info); + ctx.WriteBuffer(model_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetActivateEvent()); +} + +void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetDeactivateEvent()); +} + +void IUser::GetState(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(state); +} + +void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetCurrentState()); +} + +void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetNpadId()); +} + +void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(device.value()->GetApplicationAreaSize()); +} + +void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +} + +void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto access_id{rp.Pop()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, + access_id, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->RecreateApplicationArea(access_id, data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +std::optional> IUser::GetNfpDevice(u64 handle) { + for (auto& device : devices) { + if (device->GetHandle() == handle) { + return device; + } + } + return std::nullopt; +} } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 519ff56..7e9a90a 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h @@ -3,14 +3,61 @@ #pragma once -#include "core/hle/service/nfp/nfp.h" +#include +#include +#include + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" namespace Service::NFP { +class NfpDevice; -class NFP_User final : public Module::Interface { +class IUser final : public ServiceFramework { public: - explicit NFP_User(std::shared_ptr module_, Core::System& system_); - ~NFP_User() override; + explicit IUser(Core::System& system_); + ~IUser(); + +private: + enum class State : u32 { + NonInitialized, + Initialized, + }; + + void Initialize(Kernel::HLERequestContext& ctx); + void Finalize(Kernel::HLERequestContext& ctx); + void ListDevices(Kernel::HLERequestContext& ctx); + void StartDetection(Kernel::HLERequestContext& ctx); + void StopDetection(Kernel::HLERequestContext& ctx); + void Mount(Kernel::HLERequestContext& ctx); + void Unmount(Kernel::HLERequestContext& ctx); + void OpenApplicationArea(Kernel::HLERequestContext& ctx); + void GetApplicationArea(Kernel::HLERequestContext& ctx); + void SetApplicationArea(Kernel::HLERequestContext& ctx); + void Flush(Kernel::HLERequestContext& ctx); + void Restore(Kernel::HLERequestContext& ctx); + void CreateApplicationArea(Kernel::HLERequestContext& ctx); + void GetTagInfo(Kernel::HLERequestContext& ctx); + void GetRegisterInfo(Kernel::HLERequestContext& ctx); + void GetCommonInfo(Kernel::HLERequestContext& ctx); + void GetModelInfo(Kernel::HLERequestContext& ctx); + void AttachActivateEvent(Kernel::HLERequestContext& ctx); + void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); + void GetState(Kernel::HLERequestContext& ctx); + void GetDeviceState(Kernel::HLERequestContext& ctx); + void GetNpadId(Kernel::HLERequestContext& ctx); + void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); + void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); + void RecreateApplicationArea(Kernel::HLERequestContext& ctx); + + std::optional> GetNfpDevice(u64 handle); + + KernelHelpers::ServiceContext service_context; + + std::array, 10> devices{}; + + State state{State::NonInitialized}; + Kernel::KEvent* availability_change_event; }; } // namespace Service::NFP diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index e3ef064..4fa9f51 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -129,6 +129,9 @@ static_assert(sizeof(NifmNetworkProfileData) == 0x18E, "NifmNetworkProfileData has incorrect size."); #pragma pack(pop) +constexpr Result ResultPendingConnection{ErrorModule::NIFM, 111}; +constexpr Result ResultNetworkCommunicationDisabled{ErrorModule::NIFM, 1111}; + class IScanRequest final : public ServiceFramework { public: explicit IScanRequest(Core::System& system_) : ServiceFramework{system_, "IScanRequest"} { @@ -192,6 +195,10 @@ private: void Submit(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + if (state == RequestState::NotSubmitted) { + UpdateState(RequestState::Pending); + } + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -201,19 +208,32 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - - if (Network::GetHostIPv4Address().has_value()) { - rb.PushEnum(RequestState::Connected); - } else { - rb.PushEnum(RequestState::NotSubmitted); - } + rb.PushEnum(state); } void GetResult(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + const auto result = [this] { + const auto has_connection = Network::GetHostIPv4Address().has_value(); + switch (state) { + case RequestState::NotSubmitted: + return has_connection ? ResultSuccess : ResultNetworkCommunicationDisabled; + case RequestState::Pending: + if (has_connection) { + UpdateState(RequestState::Connected); + } else { + UpdateState(RequestState::Error); + } + return ResultPendingConnection; + case RequestState::Connected: + default: + return ResultSuccess; + } + }(); + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { @@ -252,8 +272,15 @@ private: rb.Push(0); } + void UpdateState(RequestState new_state) { + state = new_state; + event1->Signal(); + } + KernelHelpers::ServiceContext service_context; + RequestState state; + Kernel::KEvent* event1; Kernel::KEvent* event2; }; diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index b2bb742..5a8a91e 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -328,7 +328,7 @@ private: void StartTask(Kernel::HLERequestContext& ctx) { // No need to connect to the internet, just finish the task straight away. LOG_DEBUG(Service_NIM, "called"); - finished_event->GetWritableEvent().Signal(); + finished_event->Signal(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -350,7 +350,7 @@ private: void Cancel(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NIM, "called"); - finished_event->GetWritableEvent().Clear(); + finished_event->Clear(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } diff --git a/src/core/hle/service/ns/iplatform_service_manager.cpp b/src/core/hle/service/ns/iplatform_service_manager.cpp index fd047ff..1fab2f0 100644 --- a/src/core/hle/service/ns/iplatform_service_manager.cpp +++ b/src/core/hle/service/ns/iplatform_service_manager.cpp @@ -279,13 +279,10 @@ void IPlatformServiceManager::GetSharedFontInOrderOfPriority(Kernel::HLERequestC font_sizes.push_back(region.size); } - // Resize buffers if game requests smaller size output. - font_codes.resize( - std::min(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32))); - font_offsets.resize( - std::min(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32))); - font_sizes.resize( - std::min(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32))); + // Resize buffers if game requests smaller size output + font_codes.resize(std::min(font_codes.size(), ctx.GetWriteBufferNumElements(0))); + font_offsets.resize(std::min(font_offsets.size(), ctx.GetWriteBufferNumElements(1))); + font_sizes.resize(std::min(font_sizes.size(), ctx.GetWriteBufferNumElements(2))); ctx.WriteBuffer(font_codes, 0); ctx.WriteBuffer(font_offsets, 1); diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index f7318c3..f59a1a6 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -8,6 +8,7 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/ns/errors.h" #include "core/hle/service/ns/iplatform_service_manager.h" #include "core/hle/service/ns/language.h" @@ -581,7 +582,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "GetApplicationControlData"}, + {0, &IReadOnlyApplicationControlDataInterface::GetApplicationControlData, "GetApplicationControlData"}, {1, nullptr, "GetApplicationDesiredLanguage"}, {2, nullptr, "ConvertApplicationLanguageToLanguageCode"}, {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, @@ -594,6 +595,33 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default; +void IReadOnlyApplicationControlDataInterface::GetApplicationControlData( + Kernel::HLERequestContext& ctx) { + enum class ApplicationControlSource : u8 { + CacheOnly, + Storage, + StorageOnly, + }; + + struct RequestParameters { + ApplicationControlSource source; + u64 application_id; + }; + static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size."); + + IPC::RequestParser rp{ctx}; + const auto parameters{rp.PopRaw()}; + const auto nacp_data{system.GetARPManager().GetControlProperty(parameters.application_id)}; + const auto result = nacp_data ? ResultSuccess : ResultUnknown; + + if (nacp_data) { + ctx.WriteBuffer(nacp_data->data(), nacp_data->size()); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} { // clang-format off static const FunctionInfo functions[] = { diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index 4dc1915..9c18e93 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -78,6 +78,9 @@ class IReadOnlyApplicationControlDataInterface final public: explicit IReadOnlyApplicationControlDataInterface(Core::System& system_); ~IReadOnlyApplicationControlDataInterface() override; + +private: + void GetApplicationControlData(Kernel::HLERequestContext& ctx); }; class NS final : public ServiceFramework { diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp new file mode 100644 index 0000000..37ca24f --- /dev/null +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" +#include "video_core/host1x/host1x.h" + +namespace Service::Nvidia::NvCore { + +struct ContainerImpl { + explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_) + : file{host1x_}, manager{host1x_}, device_file_data{} {} + NvMap file; + SyncpointManager manager; + Container::Host1xDeviceFileData device_file_data; +}; + +Container::Container(Tegra::Host1x::Host1x& host1x_) { + impl = std::make_unique(host1x_); +} + +Container::~Container() = default; + +NvMap& Container::GetNvMapFile() { + return impl->file; +} + +const NvMap& Container::GetNvMapFile() const { + return impl->file; +} + +Container::Host1xDeviceFileData& Container::Host1xDeviceFile() { + return impl->device_file_data; +} + +const Container::Host1xDeviceFileData& Container::Host1xDeviceFile() const { + return impl->device_file_data; +} + +SyncpointManager& Container::GetSyncpointManager() { + return impl->manager; +} + +const SyncpointManager& Container::GetSyncpointManager() const { + return impl->manager; +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h new file mode 100644 index 0000000..b4b63ac --- /dev/null +++ b/src/core/hle/service/nvdrv/core/container.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Tegra::Host1x { +class Host1x; +} // namespace Tegra::Host1x + +namespace Service::Nvidia::NvCore { + +class NvMap; +class SyncpointManager; + +struct ContainerImpl; + +class Container { +public: + explicit Container(Tegra::Host1x::Host1x& host1x); + ~Container(); + + NvMap& GetNvMapFile(); + + const NvMap& GetNvMapFile() const; + + SyncpointManager& GetSyncpointManager(); + + const SyncpointManager& GetSyncpointManager() const; + + struct Host1xDeviceFileData { + std::unordered_map fd_to_id{}; + std::deque syncpts_accumulated{}; + u32 nvdec_next_id{}; + u32 vic_next_id{}; + }; + + Host1xDeviceFileData& Host1xDeviceFile(); + + const Host1xDeviceFileData& Host1xDeviceFile() const; + +private: + std::unique_ptr impl; +}; + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp new file mode 100644 index 0000000..a51ca54 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -0,0 +1,273 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/memory.h" +#include "video_core/host1x/host1x.h" + +using Core::Memory::YUZU_PAGESIZE; + +namespace Service::Nvidia::NvCore { +NvMap::Handle::Handle(u64 size_, Id id_) + : size(size_), aligned_size(size), orig_size(size), id(id_) { + flags.raw = 0; +} + +NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) { + std::scoped_lock lock(mutex); + + // Handles cannot be allocated twice + if (allocated) { + return NvResult::AccessDenied; + } + + flags = pFlags; + kind = pKind; + align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign; + + // This flag is only applicable for handles with an address passed + if (pAddress) { + flags.keep_uncached_after_free.Assign(0); + } else { + LOG_CRITICAL(Service_NVDRV, + "Mapping nvmap handles without a CPU side address is unimplemented!"); + } + + size = Common::AlignUp(size, YUZU_PAGESIZE); + aligned_size = Common::AlignUp(size, align); + address = pAddress; + allocated = true; + + return NvResult::Success; +} + +NvResult NvMap::Handle::Duplicate(bool internal_session) { + std::scoped_lock lock(mutex); + // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS) + if (!allocated) [[unlikely]] { + return NvResult::BadValue; + } + + // If we internally use FromId the duplication tracking of handles won't work accurately due to + // us not implementing per-process handle refs. + if (internal_session) { + internal_dupes++; + } else { + dupes++; + } + + return NvResult::Success; +} + +NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {} + +void NvMap::AddHandle(std::shared_ptr handle_description) { + std::scoped_lock lock(handles_lock); + + handles.emplace(handle_description->id, std::move(handle_description)); +} + +void NvMap::UnmapHandle(Handle& handle_description) { + // Remove pending unmap queue entry if needed + if (handle_description.unmap_queue_entry) { + unmap_queue.erase(*handle_description.unmap_queue_entry); + handle_description.unmap_queue_entry.reset(); + } + + // Free and unmap the handle from the SMMU + host1x.MemoryManager().Unmap(static_cast(handle_description.pin_virt_address), + handle_description.aligned_size); + host1x.Allocator().Free(handle_description.pin_virt_address, + static_cast(handle_description.aligned_size)); + handle_description.pin_virt_address = 0; +} + +bool NvMap::TryRemoveHandle(const Handle& handle_description) { + // No dupes left, we can remove from handle map + if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) { + std::scoped_lock lock(handles_lock); + + auto it{handles.find(handle_description.id)}; + if (it != handles.end()) { + handles.erase(it); + } + + return true; + } else { + return false; + } +} + +NvResult NvMap::CreateHandle(u64 size, std::shared_ptr& result_out) { + if (!size) [[unlikely]] { + return NvResult::BadValue; + } + + u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)}; + auto handle_description{std::make_shared(size, id)}; + AddHandle(handle_description); + + result_out = handle_description; + return NvResult::Success; +} + +std::shared_ptr NvMap::GetHandle(Handle::Id handle) { + std::scoped_lock lock(handles_lock); + try { + return handles.at(handle); + } catch (std::out_of_range&) { + return nullptr; + } +} + +VAddr NvMap::GetHandleAddress(Handle::Id handle) { + std::scoped_lock lock(handles_lock); + try { + return handles.at(handle)->address; + } catch (std::out_of_range&) { + return 0; + } +} + +u32 NvMap::PinHandle(NvMap::Handle::Id handle) { + auto handle_description{GetHandle(handle)}; + if (!handle_description) [[unlikely]] { + return 0; + } + + std::scoped_lock lock(handle_description->mutex); + if (!handle_description->pins) { + // If we're in the unmap queue we can just remove ourselves and return since we're already + // mapped + { + // Lock now to prevent our queue entry from being removed for allocation in-between the + // following check and erase + std::scoped_lock queueLock(unmap_queue_lock); + if (handle_description->unmap_queue_entry) { + unmap_queue.erase(*handle_description->unmap_queue_entry); + handle_description->unmap_queue_entry.reset(); + + handle_description->pins++; + return handle_description->pin_virt_address; + } + } + + // If not then allocate some space and map it + u32 address{}; + auto& smmu_allocator = host1x.Allocator(); + auto& smmu_memory_manager = host1x.MemoryManager(); + while (!(address = + smmu_allocator.Allocate(static_cast(handle_description->aligned_size)))) { + // Free handles until the allocation succeeds + std::scoped_lock queueLock(unmap_queue_lock); + if (auto freeHandleDesc{unmap_queue.front()}) { + // Handles in the unmap queue are guaranteed not to be pinned so don't bother + // checking if they are before unmapping + std::scoped_lock freeLock(freeHandleDesc->mutex); + if (handle_description->pin_virt_address) + UnmapHandle(*freeHandleDesc); + } else { + LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); + } + } + + smmu_memory_manager.Map(static_cast(address), handle_description->address, + handle_description->aligned_size); + handle_description->pin_virt_address = address; + } + + handle_description->pins++; + return handle_description->pin_virt_address; +} + +void NvMap::UnpinHandle(Handle::Id handle) { + auto handle_description{GetHandle(handle)}; + if (!handle_description) { + return; + } + + std::scoped_lock lock(handle_description->mutex); + if (--handle_description->pins < 0) { + LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!"); + } else if (!handle_description->pins) { + std::scoped_lock queueLock(unmap_queue_lock); + + // Add to the unmap queue allowing this handle's memory to be freed if needed + unmap_queue.push_back(handle_description); + handle_description->unmap_queue_entry = std::prev(unmap_queue.end()); + } +} + +void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) { + auto handle_description{GetHandle(handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); + return; + } + + auto result = handle_description->Duplicate(internal_session); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); + } +} + +std::optional NvMap::FreeHandle(Handle::Id handle, bool internal_session) { + std::weak_ptr hWeak{GetHandle(handle)}; + FreeInfo freeInfo; + + // We use a weak ptr here so we can tell when the handle has been freed and report that back to + // guest + if (auto handle_description = hWeak.lock()) { + std::scoped_lock lock(handle_description->mutex); + + if (internal_session) { + if (--handle_description->internal_dupes < 0) + LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!"); + } else { + if (--handle_description->dupes < 0) { + LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!"); + } else if (handle_description->dupes == 0) { + // Force unmap the handle + if (handle_description->pin_virt_address) { + std::scoped_lock queueLock(unmap_queue_lock); + UnmapHandle(*handle_description); + } + + handle_description->pins = 0; + } + } + + // Try to remove the shared ptr to the handle from the map, if nothing else is using the + // handle then it will now be freed when `handle_description` goes out of scope + if (TryRemoveHandle(*handle_description)) { + LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle); + } else { + LOG_DEBUG(Service_NVDRV, + "Tried to free nvmap handle: {} but didn't as it still has duplicates", + handle); + } + + freeInfo = { + .address = handle_description->address, + .size = handle_description->size, + .was_uncached = handle_description->flags.map_uncached.Value() != 0, + .can_unlock = true, + }; + } else { + return std::nullopt; + } + + // If the handle hasn't been freed from memory, mark that + if (!hWeak.expired()) { + LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); + freeInfo.can_unlock = false; + } + + return freeInfo; +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h new file mode 100644 index 0000000..a8e5738 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -0,0 +1,176 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Tegra { + +namespace Host1x { +class Host1x; +} // namespace Host1x + +} // namespace Tegra + +namespace Service::Nvidia::NvCore { +/** + * @brief The nvmap core class holds the global state for nvmap and provides methods to manage + * handles + */ +class NvMap { +public: + /** + * @brief A handle to a contiguous block of memory in an application's address space + */ + struct Handle { + std::mutex mutex; + + u64 align{}; //!< The alignment to use when pinning the handle onto the SMMU + u64 size; //!< Page-aligned size of the memory the handle refers to + u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to + u64 orig_size; //!< Original unaligned size of the memory this handle refers to + + s32 dupes{1}; //!< How many guest references there are to this handle + s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle + + using Id = u32; + Id id; //!< A globally unique identifier for this handle + + s32 pins{}; + u32 pin_virt_address{}; + std::optional>::iterator> unmap_queue_entry{}; + + union Flags { + u32 raw; + BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached + BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was + //!< allocated with a fixed address + BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins + } flags{}; + static_assert(sizeof(Flags) == sizeof(u32)); + + u64 address{}; //!< The memory location in the guest's AS that this handle corresponds to, + //!< this can also be in the nvdrv tmem + bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC + //!< call + + u8 kind{}; //!< Used for memory compression + bool allocated{}; //!< If the handle has been allocated with `Alloc` + + u64 dma_map_addr{}; //! remove me after implementing pinning. + + Handle(u64 size, Id id); + + /** + * @brief Sets up the handle with the given memory config, can allocate memory from the tmem + * if a 0 address is passed + */ + [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress); + + /** + * @brief Increases the dupe counter of the handle for the given session + */ + [[nodiscard]] NvResult Duplicate(bool internal_session); + + /** + * @brief Obtains a pointer to the handle's memory and marks the handle it as having been + * mapped + */ + u8* GetPointer() { + if (!address) { + return nullptr; + } + + is_shared_mem_mapped = true; + return reinterpret_cast(address); + } + }; + + /** + * @brief Encapsulates the result of a FreeHandle operation + */ + struct FreeInfo { + u64 address; //!< Address the handle referred to before deletion + u64 size; //!< Page-aligned handle size + bool was_uncached; //!< If the handle was allocated as uncached + bool can_unlock; //!< If the address region is ready to be unlocked + }; + + explicit NvMap(Tegra::Host1x::Host1x& host1x); + + /** + * @brief Creates an unallocated handle of the given size + */ + [[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr& result_out); + + std::shared_ptr GetHandle(Handle::Id handle); + + VAddr GetHandleAddress(Handle::Id handle); + + /** + * @brief Maps a handle into the SMMU address space + * @note This operation is refcounted, the number of calls to this must eventually match the + * number of calls to `UnpinHandle` + * @return The SMMU virtual address that the handle has been mapped to + */ + u32 PinHandle(Handle::Id handle); + + /** + * @brief When this has been called an equal number of times to `PinHandle` for the supplied + * handle it will be added to a list of handles to be freed when necessary + */ + void UnpinHandle(Handle::Id handle); + + /** + * @brief Tries to duplicate a handle + */ + void DuplicateHandle(Handle::Id handle, bool internal_session = false); + + /** + * @brief Tries to free a handle and remove a single dupe + * @note If a handle has no dupes left and has no other users a FreeInfo struct will be returned + * describing the prior state of the handle + */ + std::optional FreeHandle(Handle::Id handle, bool internal_session); + +private: + std::list> unmap_queue{}; + std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue` + + std::unordered_map> + handles{}; //!< Main owning map of handles + std::mutex handles_lock; //!< Protects access to `handles` + + static constexpr u32 HandleIdIncrement{ + 4}; //!< Each new handle ID is an increment of 4 from the previous + std::atomic next_handle_id{HandleIdIncrement}; + Tegra::Host1x::Host1x& host1x; + + void AddHandle(std::shared_ptr handle); + + /** + * @brief Unmaps and frees the SMMU memory region a handle is mapped to + * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this + */ + void UnmapHandle(Handle& handle_description); + + /** + * @brief Removes a handle from the map taking its dupes into account + * @note handle_description.mutex MUST be locked when calling this + * @return If the handle was removed from the map + */ + bool TryRemoveHandle(const Handle& handle_description); +}; +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp new file mode 100644 index 0000000..aba51d2 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp @@ -0,0 +1,135 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/assert.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" +#include "video_core/host1x/host1x.h" + +namespace Service::Nvidia::NvCore { + +SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} { + constexpr u32 VBlank0SyncpointId{26}; + constexpr u32 VBlank1SyncpointId{27}; + + // Reserve both vblank syncpoints as client managed as they use Continuous Mode + // Refer to section 14.3.5.3 of the TRM for more information on Continuous Mode + // https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/drm/dc.c#L660 + ReserveSyncpoint(VBlank0SyncpointId, true); + ReserveSyncpoint(VBlank1SyncpointId, true); + + for (u32 syncpoint_id : channel_syncpoints) { + if (syncpoint_id) { + ReserveSyncpoint(syncpoint_id, false); + } + } +} + +SyncpointManager::~SyncpointManager() = default; + +u32 SyncpointManager::ReserveSyncpoint(u32 id, bool client_managed) { + auto& syncpoint = syncpoints.at(id); + + if (syncpoint.reserved) { + ASSERT_MSG(false, "Requested syncpoint is in use"); + return 0; + } + + syncpoint.reserved = true; + syncpoint.interface_managed = client_managed; + + return id; +} + +u32 SyncpointManager::FindFreeSyncpoint() { + for (u32 i{1}; i < syncpoints.size(); i++) { + if (!syncpoints[i].reserved) { + return i; + } + } + ASSERT_MSG(false, "Failed to find a free syncpoint!"); + return 0; +} + +u32 SyncpointManager::AllocateSyncpoint(bool client_managed) { + std::lock_guard lock(reservation_lock); + return ReserveSyncpoint(FindFreeSyncpoint(), client_managed); +} + +void SyncpointManager::FreeSyncpoint(u32 id) { + std::lock_guard lock(reservation_lock); + auto& syncpoint = syncpoints.at(id); + ASSERT(syncpoint.reserved); + syncpoint.reserved = false; +} + +bool SyncpointManager::IsSyncpointAllocated(u32 id) const { + return (id <= SyncpointCount) && syncpoints[id].reserved; +} + +bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) const { + const SyncpointInfo& syncpoint{syncpoints.at(id)}; + + if (!syncpoint.reserved) { + ASSERT(false); + return false; + } + + // If the interface manages counters then we don't keep track of the maximum value as it handles + // sanity checking the values then + if (syncpoint.interface_managed) { + return static_cast(syncpoint.counter_min - threshold) >= 0; + } else { + return (syncpoint.counter_max - threshold) >= (syncpoint.counter_min - threshold); + } +} + +u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) { + auto& syncpoint = syncpoints.at(id); + + if (!syncpoint.reserved) { + ASSERT(false); + return 0; + } + + return syncpoint.counter_max += amount; +} + +u32 SyncpointManager::ReadSyncpointMinValue(u32 id) { + auto& syncpoint = syncpoints.at(id); + + if (!syncpoint.reserved) { + ASSERT(false); + return 0; + } + + return syncpoint.counter_min; +} + +u32 SyncpointManager::UpdateMin(u32 id) { + auto& syncpoint = syncpoints.at(id); + + if (!syncpoint.reserved) { + ASSERT(false); + return 0; + } + + syncpoint.counter_min = host1x.GetSyncpointManager().GetHostSyncpointValue(id); + return syncpoint.counter_min; +} + +NvFence SyncpointManager::GetSyncpointFence(u32 id) { + auto& syncpoint = syncpoints.at(id); + + if (!syncpoint.reserved) { + ASSERT(false); + return NvFence{}; + } + + return { + .id = static_cast(id), + .value = syncpoint.counter_max, + }; +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h new file mode 100644 index 0000000..4f2cefa --- /dev/null +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Tegra::Host1x { +class Host1x; +} // namespace Tegra::Host1x + +namespace Service::Nvidia::NvCore { + +enum class ChannelType : u32 { + MsEnc = 0, + VIC = 1, + GPU = 2, + NvDec = 3, + Display = 4, + NvJpg = 5, + TSec = 6, + Max = 7 +}; + +/** + * @brief SyncpointManager handles allocating and accessing host1x syncpoints, these are cached + * versions of the HW syncpoints which are intermittently synced + * @note Refer to Chapter 14 of the Tegra X1 TRM for an exhaustive overview of them + * @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html + * @url + * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c + */ +class SyncpointManager final { +public: + explicit SyncpointManager(Tegra::Host1x::Host1x& host1x); + ~SyncpointManager(); + + /** + * @brief Checks if the given syncpoint is both allocated and below the number of HW syncpoints + */ + bool IsSyncpointAllocated(u32 id) const; + + /** + * @brief Finds a free syncpoint and reserves it + * @return The ID of the reserved syncpoint + */ + u32 AllocateSyncpoint(bool client_managed); + + /** + * @url + * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/syncpt.c#L259 + */ + bool HasSyncpointExpired(u32 id, u32 threshold) const; + + bool IsFenceSignalled(NvFence fence) const { + return HasSyncpointExpired(fence.id, fence.value); + } + + /** + * @brief Atomically increments the maximum value of a syncpoint by the given amount + * @return The new max value of the syncpoint + */ + u32 IncrementSyncpointMaxExt(u32 id, u32 amount); + + /** + * @return The minimum value of the syncpoint + */ + u32 ReadSyncpointMinValue(u32 id); + + /** + * @brief Synchronises the minimum value of the syncpoint to with the GPU + * @return The new minimum value of the syncpoint + */ + u32 UpdateMin(u32 id); + + /** + * @brief Frees the usage of a syncpoint. + */ + void FreeSyncpoint(u32 id); + + /** + * @return A fence that will be signalled once this syncpoint hits its maximum value + */ + NvFence GetSyncpointFence(u32 id); + + static constexpr std::array(ChannelType::Max)> channel_syncpoints{ + 0x0, // `MsEnc` is unimplemented + 0xC, // `VIC` + 0x0, // `GPU` syncpoints are allocated per-channel instead + 0x36, // `NvDec` + 0x0, // `Display` is unimplemented + 0x37, // `NvJpg` + 0x0, // `TSec` is unimplemented + }; //!< Maps each channel ID to a constant syncpoint + +private: + /** + * @note reservation_lock should be locked when calling this + */ + u32 ReserveSyncpoint(u32 id, bool client_managed); + + /** + * @return The ID of the first free syncpoint + */ + u32 FindFreeSyncpoint(); + + struct SyncpointInfo { + std::atomic counter_min; //!< The least value the syncpoint can be (The value it was + //!< when it was last synchronized with host1x) + std::atomic counter_max; //!< The maximum value the syncpoint can reach according to + //!< the current usage + bool interface_managed; //!< If the syncpoint is managed by a host1x client interface, a + //!< client interface is a HW block that can handle host1x + //!< transactions on behalf of a host1x client (Which would + //!< otherwise need to be manually synced using PIO which is + //!< synchronous and requires direct cooperation of the CPU) + bool reserved; //!< If the syncpoint is reserved or not, not to be confused with a reserved + //!< value + }; + + constexpr static std::size_t SyncpointCount{192}; + std::array syncpoints{}; + std::mutex reservation_lock; + + Tegra::Host1x::Host1x& host1x; +}; + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 696e812..204b0e7 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -11,6 +11,10 @@ namespace Core { class System; } +namespace Kernel { +class KEvent; +} + namespace Service::Nvidia::Devices { /// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to @@ -64,6 +68,10 @@ public: */ virtual void OnClose(DeviceFD fd) = 0; + virtual Kernel::KEvent* QueryEvent(u32 event_id) { + return nullptr; + } + protected: Core::System& system; }; diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 6047119..4122fc9 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -5,15 +5,16 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" #include "core/perf_stats.h" #include "video_core/gpu.h" namespace Service::Nvidia::Devices { -nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr nvmap_dev_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} +nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) + : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} nvdisp_disp0::~nvdisp_disp0() = default; NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -39,8 +40,9 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {} void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, u32 stride, android::BufferTransformFlags transform, - const Common::Rectangle& crop_rect) { - const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); + const Common::Rectangle& crop_rect, + std::array& fences, u32 num_fences) { + const VAddr addr = nvmap.GetHandleAddress(buffer_handle); LOG_TRACE(Service, "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", addr, offset, width, height, stride, format); @@ -48,10 +50,15 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, stride, format, transform, crop_rect}; + system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); system.GetPerfStats().EndSystemFrame(); - system.GPU().SwapBuffers(&framebuffer); system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().BeginSystemFrame(); } +Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id); + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 67b105e..04217ab 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -11,13 +11,18 @@ #include "core/hle/service/nvflinger/buffer_transform_flags.h" #include "core/hle/service/nvflinger/pixel_format.h" +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore + namespace Service::Nvidia::Devices { class nvmap; class nvdisp_disp0 final : public nvdevice { public: - explicit nvdisp_disp0(Core::System& system_, std::shared_ptr nvmap_dev_); + explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core); ~nvdisp_disp0() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -33,10 +38,14 @@ public: /// Performs a screen flip, drawing the buffer pointed to by the handle. void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, u32 stride, android::BufferTransformFlags transform, - const Common::Rectangle& crop_rect); + const Common::Rectangle& crop_rect, + std::array& fences, u32 num_fences); + + Kernel::KEvent* QueryEvent(u32 event_id) override; private: - std::shared_ptr nvmap_dev; + NvCore::Container& container; + NvCore::NvMap& nvmap; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 9867a64..b635e6e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -1,21 +1,30 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include #include +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" +#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "video_core/control/channel_state.h" +#include "video_core/gpu.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" namespace Service::Nvidia::Devices { -nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, std::shared_ptr nvmap_dev_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} +nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core) + : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{}, + gmmu{} {} + nvhost_as_gpu::~nvhost_as_gpu() = default; NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -82,12 +91,52 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector& input, std::vector& IoctlAllocAsEx params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); - if (params.big_page_size == 0) { - params.big_page_size = DEFAULT_BIG_PAGE_SIZE; + LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size); + + std::scoped_lock lock(mutex); + + if (vm.initialised) { + ASSERT_MSG(false, "Cannot initialise an address space twice!"); + return NvResult::InvalidState; } - big_page_size = params.big_page_size; + if (params.big_page_size) { + if (!std::has_single_bit(params.big_page_size)) { + LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: 0x{:X}!", params.big_page_size); + return NvResult::BadValue; + } + + if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) { + LOG_ERROR(Service_NVDRV, "Unsupported big page size: 0x{:X}!", params.big_page_size); + return NvResult::BadValue; + } + + vm.big_page_size = params.big_page_size; + vm.big_page_size_bits = static_cast(std::countr_zero(params.big_page_size)); + + vm.va_range_start = params.big_page_size << VM::VA_START_SHIFT; + } + + // If this is unspecified then default values should be used + if (params.va_range_start) { + vm.va_range_start = params.va_range_start; + vm.va_range_split = params.va_range_split; + vm.va_range_end = params.va_range_end; + } + + const auto start_pages{static_cast(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; + const auto end_pages{static_cast(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; + vm.small_page_allocator = std::make_shared(start_pages, end_pages); + + const auto start_big_pages{static_cast(vm.va_range_split >> vm.big_page_size_bits)}; + const auto end_big_pages{ + static_cast((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; + vm.big_page_allocator = std::make_unique(start_big_pages, end_big_pages); + + gmmu = std::make_shared(system, 40, vm.big_page_size_bits, + VM::PAGE_SIZE_BITS); + system.GPU().InitAddressSpace(*gmmu); + vm.initialised = true; return NvResult::Success; } @@ -99,21 +148,76 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector& input, std::vector< LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, params.page_size, params.flags); - const auto size{static_cast(params.pages) * static_cast(params.page_size)}; - if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) { - params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size); - } else { - params.offset = system.GPU().MemoryManager().Allocate(size, params.align); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; } - auto result = NvResult::Success; - if (!params.offset) { - LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); - result = NvResult::InsufficientMemory; + if (params.page_size != VM::YUZU_PAGESIZE && params.page_size != vm.big_page_size) { + return NvResult::BadValue; } + if (params.page_size != vm.big_page_size && + ((params.flags & MappingFlags::Sparse) != MappingFlags::None)) { + UNIMPLEMENTED_MSG("Sparse small pages are not implemented!"); + return NvResult::NotImplemented; + } + + const u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS + : vm.big_page_size_bits}; + + auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator + : *vm.big_page_allocator}; + + if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) { + allocator.AllocateFixed(static_cast(params.offset >> page_size_bits), params.pages); + } else { + params.offset = static_cast(allocator.Allocate(params.pages)) << page_size_bits; + if (!params.offset) { + ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); + return NvResult::InsufficientMemory; + } + } + + u64 size{static_cast(params.pages) * params.page_size}; + + if ((params.flags & MappingFlags::Sparse) != MappingFlags::None) { + gmmu->MapSparse(params.offset, size); + } + + allocation_map[params.offset] = { + .size = size, + .mappings{}, + .page_size = params.page_size, + .sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None, + .big_pages = params.page_size != VM::YUZU_PAGESIZE, + }; + std::memcpy(output.data(), ¶ms, output.size()); - return result; + return NvResult::Success; +} + +void nvhost_as_gpu::FreeMappingLocked(u64 offset) { + auto mapping{mapping_map.at(offset)}; + + if (!mapping->fixed) { + auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + allocator.Free(static_cast(mapping->offset >> page_size_bits), + static_cast(mapping->size >> page_size_bits)); + } + + // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state + // Only FreeSpace can unmap them fully + if (mapping->sparse_alloc) { + gmmu->MapSparse(offset, mapping->size, mapping->big_page); + } else { + gmmu->Unmap(offset, mapping->size); + } + + mapping_map.erase(offset); } NvResult nvhost_as_gpu::FreeSpace(const std::vector& input, std::vector& output) { @@ -123,8 +227,40 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector& input, std::vector& LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, params.pages, params.page_size); - system.GPU().MemoryManager().Unmap(params.offset, - static_cast(params.pages) * params.page_size); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + + try { + auto allocation{allocation_map[params.offset]}; + + if (allocation.page_size != params.page_size || + allocation.size != (static_cast(params.pages) * params.page_size)) { + return NvResult::BadValue; + } + + for (const auto& mapping : allocation.mappings) { + FreeMappingLocked(mapping->offset); + } + + // Unset sparse flag if required + if (allocation.sparse) { + gmmu->Unmap(params.offset, allocation.size); + } + + auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator + : *vm.big_page_allocator}; + u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS + : vm.big_page_size_bits}; + + allocator.Free(static_cast(params.offset >> page_size_bits), + static_cast(allocation.size >> page_size_bits)); + allocation_map.erase(params.offset); + } catch (const std::out_of_range&) { + return NvResult::BadValue; + } std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -135,35 +271,53 @@ NvResult nvhost_as_gpu::Remap(const std::vector& input, std::vector& out LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); - auto result = NvResult::Success; std::vector entries(num_entries); std::memcpy(entries.data(), input.data(), input.size()); - for (const auto& entry : entries) { - LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", - entry.offset, entry.nvmap_handle, entry.pages); + std::scoped_lock lock(mutex); - const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; - if (!object) { - LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); - result = NvResult::InvalidState; - break; + if (!vm.initialised) { + return NvResult::BadValue; + } + + for (const auto& entry : entries) { + GPUVAddr virtual_address{static_cast(entry.as_offset_big_pages) + << vm.big_page_size_bits}; + u64 size{static_cast(entry.big_pages) << vm.big_page_size_bits}; + + auto alloc{allocation_map.upper_bound(virtual_address)}; + + if (alloc-- == allocation_map.begin() || + (virtual_address - alloc->first) + size > alloc->second.size) { + LOG_WARNING(Service_NVDRV, "Cannot remap into an unallocated region!"); + return NvResult::BadValue; } - const auto offset{static_cast(entry.offset) << 0x10}; - const auto size{static_cast(entry.pages) << 0x10}; - const auto map_offset{static_cast(entry.map_offset) << 0x10}; - const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)}; + if (!alloc->second.sparse) { + LOG_WARNING(Service_NVDRV, "Cannot remap a non-sparse mapping!"); + return NvResult::BadValue; + } - if (!addr) { - LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); - result = NvResult::InvalidState; - break; + const bool use_big_pages = alloc->second.big_pages; + if (!entry.handle) { + gmmu->MapSparse(virtual_address, size, use_big_pages); + } else { + auto handle{nvmap.GetHandle(entry.handle)}; + if (!handle) { + return NvResult::BadValue; + } + + VAddr cpu_address{static_cast( + handle->address + + (static_cast(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; + + gmmu->Map(virtual_address, cpu_address, size, static_cast(entry.kind), + use_big_pages); } } std::memcpy(output.data(), entries.data(), output.size()); - return result; + return NvResult::Success; } NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vector& output) { @@ -173,79 +327,101 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector& input, std::vectorGetObject(params.nvmap_handle)}; - if (!object) { - LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; } - // The real nvservices doesn't make a distinction between handles and ids, and - // object can only have one handle and it will be the same as its id. Assert that this is the - // case to prevent unexpected behavior. - ASSERT(object->id == params.nvmap_handle); - auto& gpu = system.GPU(); + // Remaps a subregion of an existing mapping to a different PA + if ((params.flags & MappingFlags::Remap) != MappingFlags::None) { + try { + auto mapping{mapping_map.at(params.offset)}; - u64 page_size{params.page_size}; - if (!page_size) { - page_size = object->align; - } - - if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) { - if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) { - const auto cpu_addr{static_cast(buffer_map->CpuAddr() + params.buffer_offset)}; - const auto gpu_addr{static_cast(params.offset + params.buffer_offset)}; - - if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) { - LOG_CRITICAL(Service_NVDRV, - "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, " - "mapping_size = {}, offset={}", - params.flags, params.nvmap_handle, params.buffer_offset, - params.mapping_size, params.offset); - - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; + if (mapping->size < params.mapping_size) { + LOG_WARNING(Service_NVDRV, + "Cannot remap a partially mapped GPU address space region: 0x{:X}", + params.offset); + return NvResult::BadValue; } - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::Success; - } else { - LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); + u64 gpu_address{static_cast(params.offset + params.buffer_offset)}; + VAddr cpu_address{mapping->ptr + params.buffer_offset}; - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; + gmmu->Map(gpu_address, cpu_address, params.mapping_size, + static_cast(params.kind), mapping->big_page); + + return NvResult::Success; + } catch (const std::out_of_range&) { + LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: 0x{:X}", + params.offset); + return NvResult::BadValue; } } - // We can only map objects that have already been assigned a CPU address. - ASSERT(object->status == nvmap::Object::Status::Allocated); - - const auto physical_address{object->addr + params.buffer_offset}; - u64 size{params.mapping_size}; - if (!size) { - size = object->size; + auto handle{nvmap.GetHandle(params.handle)}; + if (!handle) { + return NvResult::BadValue; } - const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None}; - if (is_alloc) { - params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size); - } else { - params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); - } + VAddr cpu_address{static_cast(handle->address + params.buffer_offset)}; + u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; - auto result = NvResult::Success; - if (!params.offset) { - LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); - result = NvResult::InvalidState; + bool big_page{[&]() { + if (Common::IsAligned(handle->align, vm.big_page_size)) { + return true; + } else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) { + return false; + } else { + ASSERT(false); + return false; + } + }()}; + + if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) { + auto alloc{allocation_map.upper_bound(params.offset)}; + + if (alloc-- == allocation_map.begin() || + (params.offset - alloc->first) + size > alloc->second.size) { + ASSERT_MSG(false, "Cannot perform a fixed mapping into an unallocated region!"); + return NvResult::BadValue; + } + + const bool use_big_pages = alloc->second.big_pages && big_page; + gmmu->Map(params.offset, cpu_address, size, static_cast(params.kind), + use_big_pages); + + auto mapping{std::make_shared(cpu_address, params.offset, size, true, + use_big_pages, alloc->second.sparse)}; + alloc->second.mappings.push_back(mapping); + mapping_map[params.offset] = mapping; } else { - AddBufferMap(params.offset, size, physical_address, is_alloc); + + auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; + u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + params.offset = static_cast(allocator.Allocate( + static_cast(Common::AlignUp(size, page_size) >> page_size_bits))) + << page_size_bits; + if (!params.offset) { + ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); + return NvResult::InsufficientMemory; + } + + gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size), + static_cast(params.kind), big_page); + + auto mapping{ + std::make_shared(cpu_address, params.offset, size, false, big_page, false)}; + mapping_map[params.offset] = mapping; } std::memcpy(output.data(), ¶ms, output.size()); - return result; + return NvResult::Success; } NvResult nvhost_as_gpu::UnmapBuffer(const std::vector& input, std::vector& output) { @@ -254,47 +430,82 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector& input, std::vectorfixed) { + auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + allocator.Free(static_cast(mapping->offset >> page_size_bits), + static_cast(mapping->size >> page_size_bits)); + } + + // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state + // Only FreeSpace can unmap them fully + if (mapping->sparse_alloc) { + gmmu->MapSparse(params.offset, mapping->size, mapping->big_page); + } else { + gmmu->Unmap(params.offset, mapping->size); + } + + mapping_map.erase(params.offset); + } catch (const std::out_of_range&) { + LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset); } - std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; } NvResult nvhost_as_gpu::BindChannel(const std::vector& input, std::vector& output) { IoctlBindChannel params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd); + LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); - channel = params.fd; + auto gpu_channel_device = module.GetDevice(params.fd); + gpu_channel_device->channel_state->memory_manager = gmmu; return NvResult::Success; } +void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) { + params.buf_size = 2 * sizeof(VaRegion); + + params.regions = std::array{ + VaRegion{ + .offset = vm.small_page_allocator->GetVAStart() << VM::PAGE_SIZE_BITS, + .page_size = VM::YUZU_PAGESIZE, + ._pad0_{}, + .pages = vm.small_page_allocator->GetVALimit() - vm.small_page_allocator->GetVAStart(), + }, + VaRegion{ + .offset = vm.big_page_allocator->GetVAStart() << vm.big_page_size_bits, + .page_size = vm.big_page_size, + ._pad0_{}, + .pages = vm.big_page_allocator->GetVALimit() - vm.big_page_allocator->GetVAStart(), + }, + }; +} + NvResult nvhost_as_gpu::GetVARegions(const std::vector& input, std::vector& output) { IoctlGetVaRegions params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, - params.buf_size); + LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, + params.buf_size); - params.buf_size = 0x30; + std::scoped_lock lock(mutex); - params.small = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = DEFAULT_SMALL_PAGE_SIZE, - .pages = 0x3fbfff, - }; + if (!vm.initialised) { + return NvResult::BadValue; + } - params.big = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = big_page_size, - .pages = 0x1bffff, - }; - - // TODO(ogniK): This probably can stay stubbed but should add support way way later + GetVARegionsImpl(params); std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -305,62 +516,27 @@ NvResult nvhost_as_gpu::GetVARegions(const std::vector& input, std::vector nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { - const auto end{buffer_mappings.upper_bound(gpu_addr)}; - for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) { - if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) { - return iter->second; - } - } - - return std::nullopt; -} - -void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, - bool is_allocated) { - buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated}; -} - -std::optional nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) { - if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) { - std::size_t size{}; - - if (iter->second.IsAllocated()) { - size = iter->second.Size(); - } - - buffer_mappings.erase(iter); - - return size; - } - - return std::nullopt; +Kernel::KEvent* nvhost_as_gpu::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown AS GPU Event {}", event_id); + return nullptr; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 555843a..86fe71c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -1,35 +1,50 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once +#include +#include #include #include +#include #include #include +#include "common/address_space.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +namespace Tegra { +class MemoryManager; +} // namespace Tegra + +namespace Service::Nvidia { +class Module; +} + +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore + namespace Service::Nvidia::Devices { -constexpr u32 DEFAULT_BIG_PAGE_SIZE = 1 << 16; -constexpr u32 DEFAULT_SMALL_PAGE_SIZE = 1 << 12; - -class nvmap; - -enum class AddressSpaceFlags : u32 { - None = 0x0, - FixedOffset = 0x1, - Remap = 0x100, +enum class MappingFlags : u32 { + None = 0, + Fixed = 1 << 0, + Sparse = 1 << 1, + Remap = 1 << 8, }; -DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); +DECLARE_ENUM_FLAG_OPERATORS(MappingFlags); class nvhost_as_gpu final : public nvdevice { public: - explicit nvhost_as_gpu(Core::System& system_, std::shared_ptr nvmap_dev_); + explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core); ~nvhost_as_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -42,46 +57,17 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; -private: - class BufferMap final { - public: - constexpr BufferMap() = default; + Kernel::KEvent* QueryEvent(u32 event_id) override; - constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_) - : start_addr{start_addr_}, end_addr{start_addr_ + size_} {} - - constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_, - bool is_allocated_) - : start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_}, - is_allocated{is_allocated_} {} - - constexpr VAddr StartAddr() const { - return start_addr; - } - - constexpr VAddr EndAddr() const { - return end_addr; - } - - constexpr std::size_t Size() const { - return end_addr - start_addr; - } - - constexpr VAddr CpuAddr() const { - return cpu_addr; - } - - constexpr bool IsAllocated() const { - return is_allocated; - } - - private: - GPUVAddr start_addr{}; - GPUVAddr end_addr{}; - VAddr cpu_addr{}; - bool is_allocated{}; + struct VaRegion { + u64 offset; + u32 page_size; + u32 _pad0_; + u64 pages; }; + static_assert(sizeof(VaRegion) == 0x18); +private: struct IoctlAllocAsEx { u32_le flags{}; // usually passes 1 s32_le as_fd{}; // ignored; passes 0 @@ -96,7 +82,7 @@ private: struct IoctlAllocSpace { u32_le pages{}; u32_le page_size{}; - AddressSpaceFlags flags{}; + MappingFlags flags{}; INSERT_PADDING_WORDS(1); union { u64_le offset; @@ -113,19 +99,19 @@ private: static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); struct IoctlRemapEntry { - u16_le flags{}; - u16_le kind{}; - u32_le nvmap_handle{}; - u32_le map_offset{}; - u32_le offset{}; - u32_le pages{}; + u16 flags; + u16 kind; + NvCore::NvMap::Handle::Id handle; + u32 handle_offset_big_pages; + u32 as_offset_big_pages; + u32 big_pages; }; static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); struct IoctlMapBufferEx { - AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable - u32_le kind{}; // -1 is default - u32_le nvmap_handle{}; + MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable + u32_le kind{}; // -1 is default + NvCore::NvMap::Handle::Id handle; u32_le page_size{}; // 0 means don't care s64_le buffer_offset{}; u64_le mapping_size{}; @@ -143,27 +129,15 @@ private: }; static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); - struct IoctlVaRegion { - u64_le offset{}; - u32_le page_size{}; - INSERT_PADDING_WORDS(1); - u64_le pages{}; - }; - static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size"); - struct IoctlGetVaRegions { u64_le buf_addr{}; // (contained output user ptr on linux, ignored) u32_le buf_size{}; // forced to 2*sizeof(struct va_region) u32_le reserved{}; - IoctlVaRegion small{}; - IoctlVaRegion big{}; + std::array regions{}; }; - static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, + static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, "IoctlGetVaRegions is incorrect size"); - s32 channel{}; - u32 big_page_size{DEFAULT_BIG_PAGE_SIZE}; - NvResult AllocAsEx(const std::vector& input, std::vector& output); NvResult AllocateSpace(const std::vector& input, std::vector& output); NvResult Remap(const std::vector& input, std::vector& output); @@ -172,18 +146,75 @@ private: NvResult FreeSpace(const std::vector& input, std::vector& output); NvResult BindChannel(const std::vector& input, std::vector& output); + void GetVARegionsImpl(IoctlGetVaRegions& params); NvResult GetVARegions(const std::vector& input, std::vector& output); NvResult GetVARegions(const std::vector& input, std::vector& output, std::vector& inline_output); - std::optional FindBufferMap(GPUVAddr gpu_addr) const; - void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); - std::optional RemoveBufferMap(GPUVAddr gpu_addr); + void FreeMappingLocked(u64 offset); - std::shared_ptr nvmap_dev; + Module& module; - // This is expected to be ordered, therefore we must use a map, not unordered_map - std::map buffer_mappings; + NvCore::Container& container; + NvCore::NvMap& nvmap; + + struct Mapping { + VAddr ptr; + u64 offset; + u64 size; + bool fixed; + bool big_page; // Only valid if fixed == false + bool sparse_alloc; + + Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_) + : ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_), + sparse_alloc(sparse_alloc_) {} + }; + + struct Allocation { + u64 size; + std::list> mappings; + u32 page_size; + bool sparse; + bool big_pages; + }; + + std::map> + mapping_map; //!< This maps the base addresses of mapped buffers to their total sizes and + //!< mapping type, this is needed as what was originally a single buffer may + //!< have been split into multiple GPU side buffers with the remap flag. + std::map allocation_map; //!< Holds allocations created by AllocSpace from + //!< which fixed buffers can be mapped into + std::mutex mutex; //!< Locks all AS operations + + struct VM { + static constexpr u32 YUZU_PAGESIZE{0x1000}; + static constexpr u32 PAGE_SIZE_BITS{std::countr_zero(YUZU_PAGESIZE)}; + + static constexpr u32 SUPPORTED_BIG_PAGE_SIZES{0x30000}; + static constexpr u32 DEFAULT_BIG_PAGE_SIZE{0x20000}; + u32 big_page_size{DEFAULT_BIG_PAGE_SIZE}; + u32 big_page_size_bits{std::countr_zero(DEFAULT_BIG_PAGE_SIZE)}; + + static constexpr u32 VA_START_SHIFT{10}; + static constexpr u64 DEFAULT_VA_SPLIT{1ULL << 34}; + static constexpr u64 DEFAULT_VA_RANGE{1ULL << 37}; + u64 va_range_start{DEFAULT_BIG_PAGE_SIZE << VA_START_SHIFT}; + u64 va_range_split{DEFAULT_VA_SPLIT}; + u64 va_range_end{DEFAULT_VA_RANGE}; + + using Allocator = Common::FlatAllocator; + + std::unique_ptr big_page_allocator; + std::shared_ptr + small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel + + bool initialised{}; + } vm; + std::shared_ptr gmmu; + + // s32 channel{}; + // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 527531f..eee11fa 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -1,24 +1,38 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later +#include #include #include +#include #include "common/assert.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" namespace Service::Nvidia::Devices { nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, events_interface{events_interface_}, syncpoint_manager{ - syncpoint_manager_} {} -nvhost_ctrl::~nvhost_ctrl() = default; + NvCore::Container& core_) + : nvdevice{system_}, events_interface{events_interface_}, core{core_}, + syncpoint_manager{core_.GetSyncpointManager()} {} + +nvhost_ctrl::~nvhost_ctrl() { + for (auto& event : events) { + if (!event.registered) { + continue; + } + events_interface.FreeEvent(event.kevent); + } +} NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) { @@ -30,13 +44,15 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& case 0x1c: return IocCtrlClearEventWait(input, output); case 0x1d: - return IocCtrlEventWait(input, output, false); - case 0x1e: return IocCtrlEventWait(input, output, true); + case 0x1e: + return IocCtrlEventWait(input, output, false); case 0x1f: return IocCtrlEventRegister(input, output); case 0x20: return IocCtrlEventUnregister(input, output); + case 0x21: + return IocCtrlEventUnregisterBatch(input, output); } break; default: @@ -60,6 +76,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector& } void nvhost_ctrl::OnOpen(DeviceFD fd) {} + void nvhost_ctrl::OnClose(DeviceFD fd) {} NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector& input, std::vector& output) { @@ -71,116 +88,167 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector& input, std::vector } NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector& output, - bool is_async) { + bool is_allocation) { IocCtrlEventWaitParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", - params.syncpt_id, params.threshold, params.timeout, is_async); + LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}", + params.fence.id, params.fence.value, params.timeout, is_allocation); - if (params.syncpt_id >= MaxSyncPoints) { + bool must_unmark_fail = !is_allocation; + const u32 event_id = params.value.raw; + SCOPE_EXIT({ + std::memcpy(output.data(), ¶ms, sizeof(params)); + if (must_unmark_fail) { + events[event_id].fails = 0; + } + }); + + const u32 fence_id = static_cast(params.fence.id); + + if (fence_id >= MaxSyncPoints) { return NvResult::BadParameter; } - u32 event_id = params.value & 0x00FF; + if (params.fence.value == 0) { + if (!syncpoint_manager.IsSyncpointAllocated(params.fence.id)) { + LOG_WARNING(Service_NVDRV, + "Unallocated syncpt_id={}, threshold={}, timeout={}, is_allocation={}", + params.fence.id, params.fence.value, params.timeout, is_allocation); + } else { + params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id); + } + return NvResult::Success; + } - if (event_id >= MaxNvEvents) { - std::memcpy(output.data(), ¶ms, sizeof(params)); + if (syncpoint_manager.IsFenceSignalled(params.fence)) { + params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id); + return NvResult::Success; + } + + if (const auto new_value = syncpoint_manager.UpdateMin(fence_id); + syncpoint_manager.IsFenceSignalled(params.fence)) { + params.value.raw = new_value; + return NvResult::Success; + } + + auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager(); + const u32 target_value = params.fence.value; + + auto lock = NvEventsLock(); + + u32 slot = [&]() { + if (is_allocation) { + params.value.raw = 0; + return FindFreeNvEvent(fence_id); + } else { + return params.value.raw; + } + }(); + + must_unmark_fail = false; + + const auto check_failing = [&]() { + if (events[slot].fails > 2) { + { + auto lk = system.StallProcesses(); + host1x_syncpoint_manager.WaitHost(fence_id, target_value); + system.UnstallProcesses(); + } + params.value.raw = target_value; + return true; + } + return false; + }; + + if (slot >= MaxNvEvents) { return NvResult::BadParameter; } - if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { - params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id); - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; - return NvResult::Success; - } - - if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id); - syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { - params.value = new_value; - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; - return NvResult::Success; - } - - auto& event = events_interface.events[event_id]; - auto& gpu = system.GPU(); - - // This is mostly to take into account unimplemented features. As synced - // gpu is always synced. - if (!gpu.IsAsync()) { - event.event->GetWritableEvent().Signal(); - return NvResult::Success; - } - const u32 current_syncpoint_value = event.fence.value; - const s32 diff = current_syncpoint_value - params.threshold; - if (diff >= 0) { - event.event->GetWritableEvent().Signal(); - params.value = current_syncpoint_value; - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; - return NvResult::Success; - } - const u32 target_value = current_syncpoint_value - diff; - - if (!is_async) { - params.value = 0; - } - if (params.timeout == 0) { - std::memcpy(output.data(), ¶ms, sizeof(params)); + if (check_failing()) { + events[slot].fails = 0; + return NvResult::Success; + } return NvResult::Timeout; } - EventState status = events_interface.status[event_id]; - const bool bad_parameter = status == EventState::Busy; - if (bad_parameter) { - std::memcpy(output.data(), ¶ms, sizeof(params)); + auto& event = events[slot]; + + if (!event.registered) { return NvResult::BadParameter; } - events_interface.SetEventStatus(event_id, EventState::Waiting); - events_interface.assigned_syncpt[event_id] = params.syncpt_id; - events_interface.assigned_value[event_id] = target_value; - if (is_async) { - params.value = params.syncpt_id << 4; - } else { - params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; + + if (event.IsBeingUsed()) { + return NvResult::BadParameter; } - params.value |= event_id; - event.event->GetWritableEvent().Clear(); - if (events_interface.failed[event_id]) { - { - auto lk = system.StallProcesses(); - gpu.WaitFence(params.syncpt_id, target_value); - system.UnstallProcesses(); - } - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + + if (check_failing()) { + event.fails = 0; return NvResult::Success; } - gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); - std::memcpy(output.data(), ¶ms, sizeof(params)); + + params.value.raw = 0; + + event.status.store(EventState::Waiting, std::memory_order_release); + event.assigned_syncpt = fence_id; + event.assigned_value = target_value; + if (is_allocation) { + params.value.syncpoint_id_for_allocation.Assign(static_cast(fence_id)); + params.value.event_allocated.Assign(1); + } else { + params.value.syncpoint_id.Assign(fence_id); + } + params.value.raw |= slot; + + event.wait_handle = + host1x_syncpoint_manager.RegisterHostAction(fence_id, target_value, [this, slot]() { + auto& event_ = events[slot]; + if (event_.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == + EventState::Waiting) { + event_.kevent->Signal(); + } + event_.status.store(EventState::Signalled, std::memory_order_release); + }); return NvResult::Timeout; } +NvResult nvhost_ctrl::FreeEvent(u32 slot) { + if (slot >= MaxNvEvents) { + return NvResult::BadParameter; + } + + auto& event = events[slot]; + + if (!event.registered) { + return NvResult::Success; + } + + if (event.IsBeingUsed()) { + return NvResult::Busy; + } + + FreeNvEvent(slot); + return NvResult::Success; +} + NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector& input, std::vector& output) { IocCtrlEventRegisterParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - const u32 event_id = params.user_event_id & 0x00FF; + const u32 event_id = params.user_event_id; LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); if (event_id >= MaxNvEvents) { return NvResult::BadParameter; } - if (events_interface.registered[event_id]) { - const auto event_state = events_interface.status[event_id]; - if (event_state != EventState::Free) { - LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event"); - events_interface.UnregisterEvent(event_id); - } else { - return NvResult::BadParameter; + + auto lock = NvEventsLock(); + + if (events[event_id].registered) { + const auto result = FreeEvent(event_id); + if (result != NvResult::Success) { + return result; } } - events_interface.RegisterEvent(event_id); + CreateNvEvent(event_id); return NvResult::Success; } @@ -190,34 +258,142 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector& input, std::memcpy(¶ms, input.data(), sizeof(params)); const u32 event_id = params.user_event_id & 0x00FF; LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); - if (event_id >= MaxNvEvents) { - return NvResult::BadParameter; + + auto lock = NvEventsLock(); + return FreeEvent(event_id); +} + +NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector& input, + std::vector& output) { + IocCtrlEventUnregisterBatchParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + u64 event_mask = params.user_events; + LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask); + + auto lock = NvEventsLock(); + while (event_mask != 0) { + const u64 event_id = std::countr_zero(event_mask); + event_mask &= ~(1ULL << event_id); + const auto result = FreeEvent(static_cast(event_id)); + if (result != NvResult::Success) { + return result; + } } - if (!events_interface.registered[event_id]) { - return NvResult::BadParameter; - } - events_interface.UnregisterEvent(event_id); return NvResult::Success; } NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector& input, std::vector& output) { - IocCtrlEventSignalParams params{}; + IocCtrlEventClearParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - u32 event_id = params.event_id & 0x00FF; - LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id); + u32 event_id = params.event_id.slot; + LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id); if (event_id >= MaxNvEvents) { return NvResult::BadParameter; } - if (events_interface.status[event_id] == EventState::Waiting) { - events_interface.LiberateEvent(event_id); - } - events_interface.failed[event_id] = true; - syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id); + auto lock = NvEventsLock(); + + auto& event = events[event_id]; + if (event.status.exchange(EventState::Cancelling, std::memory_order_acq_rel) == + EventState::Waiting) { + auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager(); + host1x_syncpoint_manager.DeregisterHostAction(event.assigned_syncpt, event.wait_handle); + syncpoint_manager.UpdateMin(event.assigned_syncpt); + event.wait_handle = {}; + } + event.fails++; + event.status.store(EventState::Cancelled, std::memory_order_release); + event.kevent->Clear(); return NvResult::Success; } +Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) { + const auto desired_event = SyncpointEventValue{.raw = event_id}; + + const bool allocated = desired_event.event_allocated.Value() != 0; + const u32 slot{allocated ? desired_event.partial_slot.Value() + : static_cast(desired_event.slot)}; + if (slot >= MaxNvEvents) { + ASSERT(false); + return nullptr; + } + + const u32 syncpoint_id{allocated ? desired_event.syncpoint_id_for_allocation.Value() + : desired_event.syncpoint_id.Value()}; + + auto lock = NvEventsLock(); + + auto& event = events[slot]; + if (event.registered && event.assigned_syncpt == syncpoint_id) { + ASSERT(event.kevent); + return event.kevent; + } + // Is this possible in hardware? + ASSERT_MSG(false, "Slot:{}, SyncpointID:{}, requested", slot, syncpoint_id); + return nullptr; +} + +std::unique_lock nvhost_ctrl::NvEventsLock() { + return std::unique_lock(events_mutex); +} + +void nvhost_ctrl::CreateNvEvent(u32 event_id) { + auto& event = events[event_id]; + ASSERT(!event.kevent); + ASSERT(!event.registered); + ASSERT(!event.IsBeingUsed()); + event.kevent = events_interface.CreateEvent(fmt::format("NVCTRL::NvEvent_{}", event_id)); + event.status = EventState::Available; + event.registered = true; + const u64 mask = 1ULL << event_id; + event.fails = 0; + events_mask |= mask; + event.assigned_syncpt = 0; +} + +void nvhost_ctrl::FreeNvEvent(u32 event_id) { + auto& event = events[event_id]; + ASSERT(event.kevent); + ASSERT(event.registered); + ASSERT(!event.IsBeingUsed()); + events_interface.FreeEvent(event.kevent); + event.kevent = nullptr; + event.status = EventState::Available; + event.registered = false; + const u64 mask = ~(1ULL << event_id); + events_mask &= mask; +} + +u32 nvhost_ctrl::FindFreeNvEvent(u32 syncpoint_id) { + u32 slot{MaxNvEvents}; + u32 free_slot{MaxNvEvents}; + for (u32 i = 0; i < MaxNvEvents; i++) { + auto& event = events[i]; + if (event.registered) { + if (!event.IsBeingUsed()) { + slot = i; + if (event.assigned_syncpt == syncpoint_id) { + return slot; + } + } + } else if (free_slot == MaxNvEvents) { + free_slot = i; + } + } + if (free_slot < MaxNvEvents) { + CreateNvEvent(free_slot); + return free_slot; + } + + if (slot < MaxNvEvents) { + return slot; + } + + LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event"); + return 0; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 4fbb89b..0b56d70 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -1,20 +1,28 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include #include +#include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "video_core/host1x/syncpoint_manager.h" + +namespace Service::Nvidia::NvCore { +class Container; +class SyncpointManager; +} // namespace Service::Nvidia::NvCore namespace Service::Nvidia::Devices { class nvhost_ctrl final : public nvdevice { public: explicit nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, - SyncpointManager& syncpoint_manager_); + NvCore::Container& core); ~nvhost_ctrl() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -27,7 +35,70 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + + union SyncpointEventValue { + u32 raw; + + union { + BitField<0, 4, u32> partial_slot; + BitField<4, 28, u32> syncpoint_id; + }; + + struct { + u16 slot; + union { + BitField<0, 12, u16> syncpoint_id_for_allocation; + BitField<12, 1, u16> event_allocated; + }; + }; + }; + static_assert(sizeof(SyncpointEventValue) == sizeof(u32)); + private: + struct InternalEvent { + // Mask representing registered events + + // Each kernel event associated to an NV event + Kernel::KEvent* kevent{}; + // The status of the current NVEvent + std::atomic status{}; + + // Tells the NVEvent that it has failed. + u32 fails{}; + // When an NVEvent is waiting on GPU interrupt, this is the sync_point + // associated with it. + u32 assigned_syncpt{}; + // This is the value of the GPU interrupt for which the NVEvent is waiting + // for. + u32 assigned_value{}; + + // Tells if an NVEvent is registered or not + bool registered{}; + + // Used for waiting on a syncpoint & canceling it. + Tegra::Host1x::SyncpointManager::ActionHandle wait_handle{}; + + bool IsBeingUsed() const { + const auto current_status = status.load(std::memory_order_acquire); + return current_status == EventState::Waiting || + current_status == EventState::Cancelling || + current_status == EventState::Signalling; + } + }; + + std::unique_lock NvEventsLock(); + + void CreateNvEvent(u32 event_id); + + void FreeNvEvent(u32 event_id); + + u32 FindFreeNvEvent(u32 syncpoint_id); + + std::array events{}; + std::mutex events_mutex; + u64 events_mask{}; + struct IocSyncptReadParams { u32_le id{}; u32_le value{}; @@ -83,27 +154,18 @@ private: }; static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); - struct IocCtrlEventSignalParams { - u32_le event_id{}; + struct IocCtrlEventClearParams { + SyncpointEventValue event_id{}; }; - static_assert(sizeof(IocCtrlEventSignalParams) == 4, - "IocCtrlEventSignalParams is incorrect size"); + static_assert(sizeof(IocCtrlEventClearParams) == 4, + "IocCtrlEventClearParams is incorrect size"); struct IocCtrlEventWaitParams { - u32_le syncpt_id{}; - u32_le threshold{}; - s32_le timeout{}; - u32_le value{}; - }; - static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size"); - - struct IocCtrlEventWaitAsyncParams { - u32_le syncpt_id{}; - u32_le threshold{}; + NvFence fence{}; u32_le timeout{}; - u32_le value{}; + SyncpointEventValue value{}; }; - static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16, + static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitAsyncParams is incorrect size"); struct IocCtrlEventRegisterParams { @@ -118,19 +180,25 @@ private: static_assert(sizeof(IocCtrlEventUnregisterParams) == 4, "IocCtrlEventUnregisterParams is incorrect size"); - struct IocCtrlEventKill { + struct IocCtrlEventUnregisterBatchParams { u64_le user_events{}; }; - static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); + static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, + "IocCtrlEventKill is incorrect size"); NvResult NvOsGetConfigU32(const std::vector& input, std::vector& output); - NvResult IocCtrlEventWait(const std::vector& input, std::vector& output, bool is_async); + NvResult IocCtrlEventWait(const std::vector& input, std::vector& output, + bool is_allocation); NvResult IocCtrlEventRegister(const std::vector& input, std::vector& output); NvResult IocCtrlEventUnregister(const std::vector& input, std::vector& output); + NvResult IocCtrlEventUnregisterBatch(const std::vector& input, std::vector& output); NvResult IocCtrlClearEventWait(const std::vector& input, std::vector& output); + NvResult FreeEvent(u32 slot); + EventInterface& events_interface; - SyncpointManager& syncpoint_manager; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 2b3b7ef..b97813f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -7,11 +7,19 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" +#include "core/hle/service/nvdrv/nvdrv.h" namespace Service::Nvidia::Devices { -nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_) : nvdevice{system_} {} -nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; +nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_) + : nvdevice{system_}, events_interface{events_interface_} { + error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier"); + unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent"); +} +nvhost_ctrl_gpu::~nvhost_ctrl_gpu() { + events_interface.FreeEvent(error_notifier_event); + events_interface.FreeEvent(unknown_event); +} NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) { @@ -286,4 +294,16 @@ NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector& input, std::vector& input, @@ -27,6 +31,8 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + private: struct IoctlGpuCharacteristics { u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) @@ -160,6 +166,12 @@ private: NvResult ZBCQueryTable(const std::vector& input, std::vector& output); NvResult FlushL2(const std::vector& input, std::vector& output); NvResult GetGpuTime(const std::vector& input, std::vector& output); + + EventInterface& events_interface; + + // Events + Kernel::KEvent* error_notifier_event; + Kernel::KEvent* unknown_event; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index b98e630..e123564 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -5,29 +5,46 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" +#include "core/hle/service/nvdrv/nvdrv.h" #include "core/memory.h" +#include "video_core/control/channel_state.h" +#include "video_core/engines/puller.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" namespace Service::Nvidia::Devices { namespace { -Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoint_id) { - Tegra::GPU::FenceAction result{}; +Tegra::CommandHeader BuildFenceAction(Tegra::Engines::Puller::FenceOperation op, u32 syncpoint_id) { + Tegra::Engines::Puller::FenceAction result{}; result.op.Assign(op); result.syncpoint_id.Assign(syncpoint_id); return {result.raw}; } } // namespace -nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} { - channel_fence.id = syncpoint_manager_.AllocateSyncpoint(); - channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); +nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_, + NvCore::Container& core_) + : nvdevice{system_}, events_interface{events_interface_}, core{core_}, + syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()}, + channel_state{system.GPU().AllocateChannel()} { + channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); + sm_exception_breakpoint_int_report_event = + events_interface.CreateEvent("GpuChannelSMExceptionBreakpointInt"); + sm_exception_breakpoint_pause_report_event = + events_interface.CreateEvent("GpuChannelSMExceptionBreakpointPause"); + error_notifier_event = events_interface.CreateEvent("GpuChannelErrorNotifier"); } -nvhost_gpu::~nvhost_gpu() = default; +nvhost_gpu::~nvhost_gpu() { + events_interface.FreeEvent(sm_exception_breakpoint_int_report_event); + events_interface.FreeEvent(sm_exception_breakpoint_pause_report_event); + events_interface.FreeEvent(error_notifier_event); + syncpoint_manager.FreeSyncpoint(channel_syncpoint); +} NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) { @@ -167,9 +184,14 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector& input, std::vectorinitialized) { + LOG_CRITICAL(Service_NVDRV, "Already allocated!"); + return NvResult::AlreadyAllocated; + } - params.fence_out = channel_fence; + system.GPU().InitChannel(*channel_state); + + params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -188,39 +210,37 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector& input, std::ve static std::vector BuildWaitCommandList(NvFence fence) { return { - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), {fence.value}, - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, Tegra::SubmissionMode::Increasing), - BuildFenceAction(Tegra::GPU::FenceOperation::Acquire, fence.id), + BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Acquire, fence.id), }; } -static std::vector BuildIncrementCommandList(NvFence fence, - u32 add_increment) { +static std::vector BuildIncrementCommandList(NvFence fence) { std::vector result{ - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), {}}; - for (u32 count = 0; count < add_increment; ++count) { - result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, + for (u32 count = 0; count < 2; ++count) { + result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, Tegra::SubmissionMode::Increasing)); - result.emplace_back(BuildFenceAction(Tegra::GPU::FenceOperation::Increment, fence.id)); + result.emplace_back( + BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id)); } return result; } -static std::vector BuildIncrementWithWfiCommandList(NvFence fence, - u32 add_increment) { +static std::vector BuildIncrementWithWfiCommandList(NvFence fence) { std::vector result{ - Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1, Tegra::SubmissionMode::Increasing), {}}; - const std::vector increment{ - BuildIncrementCommandList(fence, add_increment)}; + const std::vector increment{BuildIncrementCommandList(fence)}; result.insert(result.end(), increment.begin(), increment.end()); @@ -234,33 +254,41 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector auto& gpu = system.GPU(); - params.fence_out.id = channel_fence.id; + std::scoped_lock lock(channel_mutex); - if (params.flags.add_wait.Value() && - !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) { - gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)}); - } + const auto bind_id = channel_state->bind_id; - if (params.flags.add_increment.Value() || params.flags.increment.Value()) { - const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0; - params.fence_out.value = syncpoint_manager.IncreaseSyncpoint( - params.fence_out.id, params.AddIncrementValue() + increment_value); - } else { - params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id); - } + auto& flags = params.flags; - gpu.PushGPUEntries(std::move(entries)); + if (flags.fence_wait.Value()) { + if (flags.increment_value.Value()) { + return NvResult::BadParameter; + } - if (params.flags.add_increment.Value()) { - if (params.flags.suppress_wfi) { - gpu.PushGPUEntries(Tegra::CommandList{ - BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())}); - } else { - gpu.PushGPUEntries(Tegra::CommandList{ - BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())}); + if (!syncpoint_manager.IsFenceSignalled(params.fence)) { + gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildWaitCommandList(params.fence)}); } } + params.fence.id = channel_syncpoint; + + u32 increment{(flags.fence_increment.Value() != 0 ? 2 : 0) + + (flags.increment_value.Value() != 0 ? params.fence.value : 0)}; + params.fence.value = syncpoint_manager.IncrementSyncpointMaxExt(channel_syncpoint, increment); + gpu.PushGPUEntries(bind_id, std::move(entries)); + + if (flags.fence_increment.Value()) { + if (flags.suppress_wfi.Value()) { + gpu.PushGPUEntries(bind_id, + Tegra::CommandList{BuildIncrementCommandList(params.fence)}); + } else { + gpu.PushGPUEntries(bind_id, + Tegra::CommandList{BuildIncrementWithWfiCommandList(params.fence)}); + } + } + + flags.raw = 0; + std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); return NvResult::Success; } @@ -328,4 +356,18 @@ NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector& input, std::vect return NvResult::Success; } +Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) { + switch (event_id) { + case 1: + return sm_exception_breakpoint_int_report_event; + case 2: + return sm_exception_breakpoint_pause_report_event; + case 3: + return error_notifier_event; + default: + LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); + return nullptr; + } +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 8a9f777..1e4ecd5 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -13,17 +13,31 @@ #include "core/hle/service/nvdrv/nvdata.h" #include "video_core/dma_pusher.h" -namespace Service::Nvidia { -class SyncpointManager; +namespace Tegra { +namespace Control { +struct ChannelState; } +} // namespace Tegra + +namespace Service::Nvidia { + +namespace NvCore { +class Container; +class NvMap; +class SyncpointManager; +} // namespace NvCore + +class EventInterface; +} // namespace Service::Nvidia namespace Service::Nvidia::Devices { +class nvhost_as_gpu; class nvmap; class nvhost_gpu final : public nvdevice { public: - explicit nvhost_gpu(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_gpu(Core::System& system_, EventInterface& events_interface_, + NvCore::Container& core); ~nvhost_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -36,7 +50,10 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + private: + friend class nvhost_as_gpu; enum class CtxObjects : u32_le { Ctx2D = 0x902D, Ctx3D = 0xB197, @@ -146,17 +163,13 @@ private: u32_le num_entries{}; // number of fence objects being submitted union { u32_le raw; - BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list - BitField<1, 1, u32_le> add_increment; // append an increment to the list - BitField<2, 1, u32_le> new_hw_format; // mostly ignored - BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt - BitField<8, 1, u32_le> increment; // increment the returned fence + BitField<0, 1, u32_le> fence_wait; // append a wait sync_point to the list + BitField<1, 1, u32_le> fence_increment; // append an increment to the list + BitField<2, 1, u32_le> new_hw_format; // mostly ignored + BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt + BitField<8, 1, u32_le> increment_value; // increment the returned fence } flags; - NvFence fence_out{}; // returned new fence object for others to wait on - - u32 AddIncrementValue() const { - return flags.add_increment.Value() << 1; - } + NvFence fence{}; // returned new fence object for others to wait on }; static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence), "IoctlSubmitGpfifo is incorrect size"); @@ -191,9 +204,18 @@ private: NvResult ChannelSetTimeout(const std::vector& input, std::vector& output); NvResult ChannelSetTimeslice(const std::vector& input, std::vector& output); - std::shared_ptr nvmap_dev; - SyncpointManager& syncpoint_manager; - NvFence channel_fence; + EventInterface& events_interface; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; + NvCore::NvMap& nvmap; + std::shared_ptr channel_state; + u32 channel_syncpoint; + std::mutex channel_mutex; + + // Events + Kernel::KEvent* sm_exception_breakpoint_int_report_event; + Kernel::KEvent* sm_exception_breakpoint_pause_report_event; + Kernel::KEvent* error_notifier_event; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index a7385fc..1703f9c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -5,14 +5,14 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" #include "video_core/renderer_base.h" namespace Service::Nvidia::Devices { -nvhost_nvdec::nvhost_nvdec(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} +nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_) + : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {} nvhost_nvdec::~nvhost_nvdec() = default; NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -21,8 +21,9 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& case 0x0: switch (command.cmd) { case 0x1: { - if (!fd_to_id.contains(fd)) { - fd_to_id[fd] = next_id++; + auto& host1x_file = core.Host1xDeviceFile(); + if (!host1x_file.fd_to_id.contains(fd)) { + host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++; } return Submit(fd, input, output); } @@ -73,8 +74,9 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) { void nvhost_nvdec::OnClose(DeviceFD fd) { LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); - const auto iter = fd_to_id.find(fd); - if (iter != fd_to_id.end()) { + auto& host1x_file = core.Host1xDeviceFile(); + const auto iter = host1x_file.fd_to_id.find(fd); + if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } system.AudioCore().SetNVDECActive(false); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 29b3e6a..c1b4e53 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -10,8 +10,7 @@ namespace Service::Nvidia::Devices { class nvhost_nvdec final : public nvhost_nvdec_common { public: - explicit nvhost_nvdec(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core); ~nvhost_nvdec() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -23,9 +22,6 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - -private: - u32 next_id{}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 8b2cd9b..99eede7 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -8,10 +8,12 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/memory.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" #include "video_core/renderer_base.h" @@ -44,10 +46,22 @@ std::size_t WriteVectors(std::vector& dst, const std::vector& src, std::s } } // Anonymous namespace -nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} {} -nvhost_nvdec_common::~nvhost_nvdec_common() = default; +nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_, + NvCore::ChannelType channel_type_) + : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, + nvmap{core.GetNvMapFile()}, channel_type{channel_type_} { + auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated; + if (syncpts_accumulated.empty()) { + channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); + } else { + channel_syncpoint = syncpts_accumulated.front(); + syncpts_accumulated.pop_front(); + } +} + +nvhost_nvdec_common::~nvhost_nvdec_common() { + core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); +} NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector& input) { IoctlSetNvmapFD params{}; @@ -84,16 +98,16 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector& input, for (std::size_t i = 0; i < syncpt_increments.size(); i++) { const SyncptIncr& syncpt_incr = syncpt_increments[i]; fence_thresholds[i] = - syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments); + syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments); } } for (const auto& cmd_buffer : command_buffers) { - const auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); + const auto object = nvmap.GetHandle(cmd_buffer.memory_id); ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); - system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), + system.Memory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), cmdlist.size() * sizeof(u32)); - gpu.PushCommandBuffer(fd_to_id[fd], cmdlist); + gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); } std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); // Some games expect command_buffers to be written back @@ -112,10 +126,8 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector& input, std::ve std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); - if (device_syncpoints[params.param] == 0 && system.GPU().UseNvdec()) { - device_syncpoints[params.param] = syncpoint_manager.AllocateSyncpoint(); - } - params.value = device_syncpoints[params.param]; + // const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast(channel_type)]}; + params.value = channel_syncpoint; std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); return NvResult::Success; @@ -123,6 +135,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector& input, std::ve NvResult nvhost_nvdec_common::GetWaitbase(const std::vector& input, std::vector& output) { IoctlGetWaitbase params{}; + LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); params.value = 0; // Seems to be hard coded at 0 std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); @@ -136,28 +149,8 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector& input, std::vecto SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); - auto& gpu = system.GPU(); - for (auto& cmd_buffer : cmd_buffer_handles) { - auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)}; - if (!object) { - LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle); - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; - } - if (object->dma_map_addr == 0) { - // NVDEC and VIC memory is in the 32-bit address space - // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space - const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size); - object->dma_map_addr = static_cast(low_addr); - // Ensure that the dma_map_addr is indeed in the lower 32-bit address space. - ASSERT(object->dma_map_addr == low_addr); - } - if (!object->dma_map_addr) { - LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size); - } else { - cmd_buffer.map_address = object->dma_map_addr; - } + cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle); } std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), @@ -167,11 +160,16 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector& input, std::vecto } NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector& input, std::vector& output) { - // This is intntionally stubbed. - // Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame - // addresses, and risk invalidating data before the async GPU thread is done with it + IoctlMapBuffer params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); + std::vector cmd_buffer_handles(params.num_entries); + + SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); + for (auto& cmd_buffer : cmd_buffer_handles) { + nvmap.UnpinHandle(cmd_buffer.map_handle); + } + std::memset(output.data(), 0, output.size()); - LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); return NvResult::Success; } @@ -182,4 +180,9 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector& input, return NvResult::Success; } +Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown HOSTX1 Event {}", event_id); + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 12d3994..fe76100 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -3,21 +3,26 @@ #pragma once +#include #include #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" namespace Service::Nvidia { -class SyncpointManager; + +namespace NvCore { +class Container; +class NvMap; +} // namespace NvCore namespace Devices { -class nvmap; class nvhost_nvdec_common : public nvdevice { public: - explicit nvhost_nvdec_common(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_nvdec_common(Core::System& system_, NvCore::Container& core, + NvCore::ChannelType channel_type); ~nvhost_nvdec_common() override; protected: @@ -110,11 +115,15 @@ protected: NvResult UnmapBuffer(const std::vector& input, std::vector& output); NvResult SetSubmitTimeout(const std::vector& input, std::vector& output); - std::unordered_map fd_to_id{}; + Kernel::KEvent* QueryEvent(u32 event_id) override; + + u32 channel_syncpoint; s32_le nvmap_fd{}; u32_le submit_timeout{}; - std::shared_ptr nvmap_dev; - SyncpointManager& syncpoint_manager; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; + NvCore::NvMap& nvmap; + NvCore::ChannelType channel_type; std::array device_syncpoints{}; }; }; // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index f58e8ba..73f9713 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -4,13 +4,14 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "video_core/renderer_base.h" namespace Service::Nvidia::Devices { -nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} + +nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_) + : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::VIC} {} nvhost_vic::~nvhost_vic() = default; @@ -19,11 +20,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& i switch (command.group) { case 0x0: switch (command.cmd) { - case 0x1: - if (!fd_to_id.contains(fd)) { - fd_to_id[fd] = next_id++; + case 0x1: { + auto& host1x_file = core.Host1xDeviceFile(); + if (!host1x_file.fd_to_id.contains(fd)) { + host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++; } return Submit(fd, input, output); + } case 0x2: return GetSyncpoint(input, output); case 0x3: @@ -67,8 +70,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector& i void nvhost_vic::OnOpen(DeviceFD fd) {} void nvhost_vic::OnClose(DeviceFD fd) { - const auto iter = fd_to_id.find(fd); - if (iter != fd_to_id.end()) { + auto& host1x_file = core.Host1xDeviceFile(); + const auto iter = host1x_file.fd_to_id.find(fd); + if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index b41b195..f164caa 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -9,8 +9,7 @@ namespace Service::Nvidia::Devices { class nvhost_vic final : public nvhost_nvdec_common { public: - explicit nvhost_vic(Core::System& system_, std::shared_ptr nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_vic(Core::System& system_, NvCore::Container& core); ~nvhost_vic(); NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, @@ -22,8 +21,5 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - -private: - u32 next_id{}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index d851814..fa29db7 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -2,19 +2,26 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/kernel/k_page_table.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvmap.h" +#include "core/memory.h" + +using Core::Memory::YUZU_PAGESIZE; namespace Service::Nvidia::Devices { -nvmap::nvmap(Core::System& system_) : nvdevice{system_} { - // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to - // represent this. - CreateObject(0); -} +nvmap::nvmap(Core::System& system_, NvCore::Container& container_) + : nvdevice{system_}, container{container_}, file{container.GetNvMapFile()} {} nvmap::~nvmap() = default; @@ -62,39 +69,21 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector& input, void nvmap::OnOpen(DeviceFD fd) {} void nvmap::OnClose(DeviceFD fd) {} -VAddr nvmap::GetObjectAddress(u32 handle) const { - auto object = GetObject(handle); - ASSERT(object); - ASSERT(object->status == Object::Status::Allocated); - return object->addr; -} - -u32 nvmap::CreateObject(u32 size) { - // Create a new nvmap object and obtain a handle to it. - auto object = std::make_shared(); - object->id = next_id++; - object->size = size; - object->status = Object::Status::Created; - object->refcount = 1; - - const u32 handle = next_handle++; - - handles.insert_or_assign(handle, std::move(object)); - - return handle; -} - NvResult nvmap::IocCreate(const std::vector& input, std::vector& output) { IocCreateParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); + LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); - if (!params.size) { - LOG_ERROR(Service_NVDRV, "Size is 0"); - return NvResult::BadValue; + std::shared_ptr handle_description{}; + auto result = + file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Failed to create Object"); + return result; } - - params.handle = CreateObject(params.size); + handle_description->orig_size = params.size; // Orig size is the unaligned size + params.handle = handle_description->id; + LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size); std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; @@ -103,63 +92,71 @@ NvResult nvmap::IocCreate(const std::vector& input, std::vector& output) NvResult nvmap::IocAlloc(const std::vector& input, std::vector& output) { IocAllocParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); + LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); if (!params.handle) { - LOG_ERROR(Service_NVDRV, "Handle is 0"); + LOG_CRITICAL(Service_NVDRV, "Handle is 0"); return NvResult::BadValue; } if ((params.align - 1) & params.align) { - LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); + LOG_CRITICAL(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); return NvResult::BadValue; } - const u32 min_alignment = 0x1000; - if (params.align < min_alignment) { - params.align = min_alignment; + // Force page size alignment at a minimum + if (params.align < YUZU_PAGESIZE) { + params.align = YUZU_PAGESIZE; } - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); return NvResult::BadValue; } - if (object->status == Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); + if (handle_description->allocated) { + LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); return NvResult::InsufficientMemory; } - object->flags = params.flags; - object->align = params.align; - object->kind = params.kind; - object->addr = params.addr; - object->status = Object::Status::Allocated; - + const auto result = + handle_description->Alloc(params.flags, params.align, params.kind, params.address); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle); + return result; + } + bool is_out_io{}; + ASSERT(system.CurrentProcess() + ->PageTable() + .LockForMapDeviceAddressSpace(&is_out_io, handle_description->address, + handle_description->size, + Kernel::KMemoryPermission::None, true, false) + .IsSuccess()); std::memcpy(output.data(), ¶ms, sizeof(params)); - return NvResult::Success; + return result; } NvResult nvmap::IocGetId(const std::vector& input, std::vector& output) { IocGetIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); + // See the comment in FromId for extra info on this function if (!params.handle) { - LOG_ERROR(Service_NVDRV, "Handle is zero"); + LOG_CRITICAL(Service_NVDRV, "Error!"); return NvResult::BadValue; } - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return NvResult::BadValue; + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Error!"); + return NvResult::AccessDenied; // This will always return EPERM irrespective of if the + // handle exists or not } - params.id = object->id; - + params.id = handle_description->id; std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } @@ -168,26 +165,29 @@ NvResult nvmap::IocFromId(const std::vector& input, std::vector& output) IocFromIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id); - auto itr = std::find_if(handles.begin(), handles.end(), - [&](const auto& entry) { return entry.second->id == params.id; }); - if (itr == handles.end()) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + // Handles and IDs are always the same value in nvmap however IDs can be used globally given the + // right permissions. + // Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and + // so this function just does simple validation and passes through the handle id. + if (!params.id) { + LOG_CRITICAL(Service_NVDRV, "Zero Id is invalid!"); return NvResult::BadValue; } - auto& object = itr->second; - if (object->status != Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.id)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); return NvResult::BadValue; } - itr->second->refcount++; - - // Return the existing handle instead of creating a new one. - params.handle = itr->first; - + auto result = handle_description->Duplicate(false); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); + return result; + } + params.handle = handle_description->id; std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } @@ -198,35 +198,43 @@ NvResult nvmap::IocParam(const std::vector& input, std::vector& output) IocParamParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "(STUBBED) called type={}", params.param); + LOG_DEBUG(Service_NVDRV, "called type={}", params.param); - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + if (!params.handle) { + LOG_CRITICAL(Service_NVDRV, "Invalid handle!"); return NvResult::BadValue; } - if (object->status != Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Not registered handle!"); return NvResult::BadValue; } - switch (static_cast(params.param)) { - case ParamTypes::Size: - params.result = object->size; + switch (params.param) { + case HandleParameterType::Size: + params.result = static_cast(handle_description->orig_size); break; - case ParamTypes::Alignment: - params.result = object->align; + case HandleParameterType::Alignment: + params.result = static_cast(handle_description->align); break; - case ParamTypes::Heap: - // TODO(Subv): Seems to be a hardcoded value? - params.result = 0x40000000; + case HandleParameterType::Base: + params.result = static_cast(-22); // posix EINVAL break; - case ParamTypes::Kind: - params.result = object->kind; + case HandleParameterType::Heap: + if (handle_description->allocated) + params.result = 0x40000000; + else + params.result = 0; + break; + case HandleParameterType::Kind: + params.result = handle_description->kind; + break; + case HandleParameterType::IsSharedMemMapped: + params.result = handle_description->is_shared_mem_mapped; break; default: - UNIMPLEMENTED(); + return NvResult::BadValue; } std::memcpy(output.data(), ¶ms, sizeof(params)); @@ -234,46 +242,31 @@ NvResult nvmap::IocParam(const std::vector& input, std::vector& output) } NvResult nvmap::IocFree(const std::vector& input, std::vector& output) { - // TODO(Subv): These flags are unconfirmed. - enum FreeFlags { - Freed = 0, - NotFreedYet = 1, - }; - IocFreeParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called"); - auto itr = handles.find(params.handle); - if (itr == handles.end()) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return NvResult::BadValue; - } - if (!itr->second->refcount) { - LOG_ERROR( - Service_NVDRV, - "There is no references to this object. The object is already freed. handle={:08X}", - params.handle); - return NvResult::BadValue; + if (!params.handle) { + LOG_CRITICAL(Service_NVDRV, "Handle null freed?"); + return NvResult::Success; } - itr->second->refcount--; - - params.size = itr->second->size; - - if (itr->second->refcount == 0) { - params.flags = Freed; - // The address of the nvmap is written to the output if we're finally freeing it, otherwise - // 0 is written. - params.address = itr->second->addr; + if (auto freeInfo{file.FreeHandle(params.handle, false)}) { + if (freeInfo->can_unlock) { + ASSERT(system.CurrentProcess() + ->PageTable() + .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) + .IsSuccess()); + } + params.address = freeInfo->address; + params.size = static_cast(freeInfo->size); + params.flags.raw = 0; + params.flags.map_uncached.Assign(freeInfo->was_uncached); } else { - params.flags = NotFreedYet; - params.address = 0; + // This is possible when there's internel dups or other duplicates. } - handles.erase(params.handle); - std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index d5360d6..e9bfd03 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -9,15 +9,23 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +namespace Service::Nvidia::NvCore { +class Container; +} // namespace Service::Nvidia::NvCore + namespace Service::Nvidia::Devices { class nvmap final : public nvdevice { public: - explicit nvmap(Core::System& system_); + explicit nvmap(Core::System& system_, NvCore::Container& container); ~nvmap() override; + nvmap(const nvmap&) = delete; + nvmap& operator=(const nvmap&) = delete; + NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector& input, @@ -28,31 +36,15 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - /// Returns the allocated address of an nvmap object given its handle. - VAddr GetObjectAddress(u32 handle) const; - - /// Represents an nvmap object. - struct Object { - enum class Status { Created, Allocated }; - u32 id; - u32 size; - u32 flags; - u32 align; - u8 kind; - VAddr addr; - Status status; - u32 refcount; - u32 dma_map_addr; + enum class HandleParameterType : u32_le { + Size = 1, + Alignment = 2, + Base = 3, + Heap = 4, + Kind = 5, + IsSharedMemMapped = 6 }; - std::shared_ptr GetObject(u32 handle) const { - auto itr = handles.find(handle); - if (itr != handles.end()) { - return itr->second; - } - return {}; - } - private: /// Id to use for the next handle that is created. u32 next_handle = 0; @@ -60,9 +52,6 @@ private: /// Id to use for the next object that is created. u32 next_id = 0; - /// Mapping of currently allocated handles to the objects they represent. - std::unordered_map> handles; - struct IocCreateParams { // Input u32_le size{}; @@ -83,11 +72,11 @@ private: // Input u32_le handle{}; u32_le heap_mask{}; - u32_le flags{}; + NvCore::NvMap::Handle::Flags flags{}; u32_le align{}; u8 kind{}; INSERT_PADDING_BYTES(7); - u64_le addr{}; + u64_le address{}; }; static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size"); @@ -96,14 +85,14 @@ private: INSERT_PADDING_BYTES(4); u64_le address{}; u32_le size{}; - u32_le flags{}; + NvCore::NvMap::Handle::Flags flags{}; }; static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); struct IocParamParams { // Input u32_le handle{}; - u32_le param{}; + HandleParameterType param{}; // Output u32_le result{}; }; @@ -117,14 +106,15 @@ private: }; static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); - u32 CreateObject(u32 size); - NvResult IocCreate(const std::vector& input, std::vector& output); NvResult IocAlloc(const std::vector& input, std::vector& output); NvResult IocGetId(const std::vector& input, std::vector& output); NvResult IocFromId(const std::vector& input, std::vector& output); NvResult IocParam(const std::vector& input, std::vector& output); NvResult IocFree(const std::vector& input, std::vector& output); + + NvCore::Container& container; + NvCore::NvMap& file; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index 1d00394..0e2f470 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -1,5 +1,6 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -78,11 +79,15 @@ enum class NvResult : u32 { ModuleNotPresent = 0xA000E, }; +// obtained from +// https://github.com/skyline-emu/skyline/blob/nvdec-dev/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/ctrl.h#L47 enum class EventState { - Free = 0, - Registered = 1, - Waiting = 2, - Busy = 3, + Available = 0, + Waiting = 1, + Cancelling = 2, + Signalling = 3, + Signalled = 4, + Cancelled = 5, }; union Ioctl { diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 756eb74..6fc8565 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -1,5 +1,6 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include @@ -7,7 +8,7 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" @@ -15,17 +16,31 @@ #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" +#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "core/hle/service/nvdrv/devices/nvmap.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv_interface.h" #include "core/hle/service/nvdrv/nvmemp.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "video_core/gpu.h" namespace Service::Nvidia { +EventInterface::EventInterface(Module& module_) : module{module_}, guard{}, on_signal{} {} + +EventInterface::~EventInterface() = default; + +Kernel::KEvent* EventInterface::CreateEvent(std::string name) { + Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name)); + return new_event; +} + +void EventInterface::FreeEvent(Kernel::KEvent* event) { + module.service_context.CloseEvent(event); +} + void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto module_ = std::make_shared(system); @@ -38,34 +53,46 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger } Module::Module(Core::System& system) - : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} { - for (u32 i = 0; i < MaxNvEvents; i++) { - events_interface.events[i].event = - service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); - events_interface.status[i] = EventState::Free; - events_interface.registered[i] = false; - } - auto nvmap_dev = std::make_shared(system); - devices["/dev/nvhost-as-gpu"] = std::make_shared(system, nvmap_dev); - devices["/dev/nvhost-gpu"] = - std::make_shared(system, nvmap_dev, syncpoint_manager); - devices["/dev/nvhost-ctrl-gpu"] = std::make_shared(system); - devices["/dev/nvmap"] = nvmap_dev; - devices["/dev/nvdisp_disp0"] = std::make_shared(system, nvmap_dev); - devices["/dev/nvhost-ctrl"] = - std::make_shared(system, events_interface, syncpoint_manager); - devices["/dev/nvhost-nvdec"] = - std::make_shared(system, nvmap_dev, syncpoint_manager); - devices["/dev/nvhost-nvjpg"] = std::make_shared(system); - devices["/dev/nvhost-vic"] = - std::make_shared(system, nvmap_dev, syncpoint_manager); + : container{system.Host1x()}, service_context{system, "nvdrv"}, events_interface{*this} { + builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { + auto device = std::make_shared(system, *this, container); + return open_files.emplace(fd, std::move(device)).first; + }; + builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) { + auto device = std::make_shared(system, events_interface, container); + return open_files.emplace(fd, std::move(device)).first; + }; + builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) { + auto device = std::make_shared(system, events_interface); + return open_files.emplace(fd, std::move(device)).first; + }; + builders["/dev/nvmap"] = [this, &system](DeviceFD fd) { + auto device = std::make_shared(system, container); + return open_files.emplace(fd, std::move(device)).first; + }; + builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) { + auto device = std::make_shared(system, container); + return open_files.emplace(fd, std::move(device)).first; + }; + builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) { + auto device = std::make_shared(system, events_interface, container); + return open_files.emplace(fd, std::move(device)).first; + }; + builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) { + auto device = std::make_shared(system, container); + return open_files.emplace(fd, std::move(device)).first; + }; + builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) { + auto device = std::make_shared(system); + return open_files.emplace(fd, std::move(device)).first; + }; + builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) { + auto device = std::make_shared(system, container); + return open_files.emplace(fd, std::move(device)).first; + }; } -Module::~Module() { - for (u32 i = 0; i < MaxNvEvents; i++) { - service_context.CloseEvent(events_interface.events[i].event); - } -} +Module::~Module() {} NvResult Module::VerifyFD(DeviceFD fd) const { if (fd < 0) { @@ -82,18 +109,18 @@ NvResult Module::VerifyFD(DeviceFD fd) const { } DeviceFD Module::Open(const std::string& device_name) { - if (devices.find(device_name) == devices.end()) { + auto it = builders.find(device_name); + if (it == builders.end()) { LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); return INVALID_NVDRV_FD; } - auto device = devices[device_name]; const DeviceFD fd = next_fd++; + auto& builder = it->second; + auto device = builder(fd)->second; device->OnOpen(fd); - open_files[fd] = std::move(device); - return fd; } @@ -168,22 +195,24 @@ NvResult Module::Close(DeviceFD fd) { return NvResult::Success; } -void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { - for (u32 i = 0; i < MaxNvEvents; i++) { - if (events_interface.assigned_syncpt[i] == syncpoint_id && - events_interface.assigned_value[i] == value) { - events_interface.LiberateEvent(i); - events_interface.events[i].event->GetWritableEvent().Signal(); - } +NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) { + if (fd < 0) { + LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); + return NvResult::InvalidState; } -} -Kernel::KReadableEvent& Module::GetEvent(const u32 event_id) { - return events_interface.events[event_id].event->GetReadableEvent(); -} + const auto itr = open_files.find(fd); -Kernel::KWritableEvent& Module::GetEventWriteable(const u32 event_id) { - return events_interface.events[event_id].event->GetWritableEvent(); + if (itr == open_files.end()) { + LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); + return NvResult::NotImplemented; + } + + event = itr->second->QueryEvent(event_id); + if (!event) { + return NvResult::BadParameter; + } + return NvResult::Success; } } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index c929e51..f3c81bd 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -1,16 +1,20 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once +#include +#include #include +#include #include #include #include "common/common_types.h" #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/hle/service/nvflinger/ui/fence.h" #include "core/hle/service/service.h" @@ -28,81 +32,31 @@ class NVFlinger; namespace Service::Nvidia { +namespace NvCore { +class Container; class SyncpointManager; +} // namespace NvCore namespace Devices { class nvdevice; -} +class nvhost_ctrl; +} // namespace Devices -/// Represents an Nvidia event -struct NvEvent { - Kernel::KEvent* event{}; - NvFence fence{}; -}; +class Module; -struct EventInterface { - // Mask representing currently busy events - u64 events_mask{}; - // Each kernel event associated to an NV event - std::array events; - // The status of the current NVEvent - std::array status{}; - // Tells if an NVEvent is registered or not - std::array registered{}; - // Tells the NVEvent that it has failed. - std::array failed{}; - // When an NVEvent is waiting on GPU interrupt, this is the sync_point - // associated with it. - std::array assigned_syncpt{}; - // This is the value of the GPU interrupt for which the NVEvent is waiting - // for. - std::array assigned_value{}; - // Constant to denote an unasigned syncpoint. - static constexpr u32 unassigned_syncpt = 0xFFFFFFFF; - std::optional GetFreeEvent() const { - u64 mask = events_mask; - for (u32 i = 0; i < MaxNvEvents; i++) { - const bool is_free = (mask & 0x1) == 0; - if (is_free) { - if (status[i] == EventState::Registered || status[i] == EventState::Free) { - return {i}; - } - } - mask = mask >> 1; - } - return std::nullopt; - } - void SetEventStatus(const u32 event_id, EventState new_status) { - EventState old_status = status[event_id]; - if (old_status == new_status) { - return; - } - status[event_id] = new_status; - if (new_status == EventState::Registered) { - registered[event_id] = true; - } - if (new_status == EventState::Waiting || new_status == EventState::Busy) { - events_mask |= (1ULL << event_id); - } - } - void RegisterEvent(const u32 event_id) { - registered[event_id] = true; - if (status[event_id] == EventState::Free) { - status[event_id] = EventState::Registered; - } - } - void UnregisterEvent(const u32 event_id) { - registered[event_id] = false; - if (status[event_id] == EventState::Registered) { - status[event_id] = EventState::Free; - } - } - void LiberateEvent(const u32 event_id) { - status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free; - events_mask &= ~(1ULL << event_id); - assigned_syncpt[event_id] = unassigned_syncpt; - assigned_value[event_id] = 0; - } +class EventInterface { +public: + explicit EventInterface(Module& module_); + ~EventInterface(); + + Kernel::KEvent* CreateEvent(std::string name); + + void FreeEvent(Kernel::KEvent* event); + +private: + Module& module; + std::mutex guard; + std::list on_signal; }; class Module final { @@ -112,9 +66,9 @@ public: /// Returns a pointer to one of the available devices, identified by its name. template - std::shared_ptr GetDevice(const std::string& name) { - auto itr = devices.find(name); - if (itr == devices.end()) + std::shared_ptr GetDevice(DeviceFD fd) { + auto itr = open_files.find(fd); + if (itr == open_files.end()) return nullptr; return std::static_pointer_cast(itr->second); } @@ -137,28 +91,27 @@ public: /// Closes a device file descriptor and returns operation success. NvResult Close(DeviceFD fd); - void SignalSyncpt(const u32 syncpoint_id, const u32 value); - - Kernel::KReadableEvent& GetEvent(u32 event_id); - - Kernel::KWritableEvent& GetEventWriteable(u32 event_id); + NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event); private: + friend class EventInterface; + friend class Service::NVFlinger::NVFlinger; + /// Manages syncpoints on the host - SyncpointManager syncpoint_manager; + NvCore::Container container; /// Id to use for the next open file descriptor. DeviceFD next_fd = 1; + using FilesContainerType = std::unordered_map>; /// Mapping of file descriptors to the devices they reference. - std::unordered_map> open_files; + FilesContainerType open_files; - /// Mapping of device node names to their implementation. - std::unordered_map> devices; + KernelHelpers::ServiceContext service_context; EventInterface events_interface; - KernelHelpers::ServiceContext service_context; + std::unordered_map> builders; }; /// Registers all NVDRV services with the specified service manager. diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index b5a9803..edbdfee 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -1,10 +1,12 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -12,10 +14,6 @@ namespace Service::Nvidia { -void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { - nvdrv->SignalSyncpt(syncpoint_id, value); -} - void NVDRV::Open(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NVDRV, "called"); IPC::ResponseBuilder rb{ctx, 4}; @@ -164,8 +162,7 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto fd = rp.Pop(); - const auto event_id = rp.Pop() & 0x00FF; - LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); + const auto event_id = rp.Pop(); if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); @@ -173,24 +170,20 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { return; } - const auto nv_result = nvdrv->VerifyFD(fd); - if (nv_result != NvResult::Success) { - LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd); - ServiceError(ctx, nv_result); - return; - } + Kernel::KEvent* event = nullptr; + NvResult result = nvdrv->QueryEvent(fd, event_id, event); - if (event_id < MaxNvEvents) { + if (result == NvResult::Success) { IPC::ResponseBuilder rb{ctx, 3, 1}; rb.Push(ResultSuccess); - auto& event = nvdrv->GetEvent(event_id); - event.Clear(); - rb.PushCopyObjects(event); + auto& readable_event = event->GetReadableEvent(); + rb.PushCopyObjects(readable_event); rb.PushEnum(NvResult::Success); } else { + LOG_ERROR(Service_NVDRV, "Invalid event request!"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushEnum(NvResult::BadParameter); + rb.PushEnum(result); } } diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index cbd37b5..5ac06ee 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -7,10 +7,6 @@ #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/service.h" -namespace Kernel { -class KWritableEvent; -} - namespace Service::Nvidia { class NVDRV final : public ServiceFramework { @@ -18,8 +14,6 @@ public: explicit NVDRV(Core::System& system_, std::shared_ptr nvdrv_, const char* name); ~NVDRV() override; - void SignalGPUInterruptSyncpt(u32 syncpoint_id, u32 value); - private: void Open(Kernel::HLERequestContext& ctx); void Ioctl1(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp deleted file mode 100644 index a6fa943..0000000 --- a/src/core/hle/service/nvdrv/syncpoint_manager.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" -#include "video_core/gpu.h" - -namespace Service::Nvidia { - -SyncpointManager::SyncpointManager(Tegra::GPU& gpu_) : gpu{gpu_} {} - -SyncpointManager::~SyncpointManager() = default; - -u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) { - syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id); - return GetSyncpointMin(syncpoint_id); -} - -u32 SyncpointManager::AllocateSyncpoint() { - for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) { - if (!syncpoints[syncpoint_id].is_allocated) { - syncpoints[syncpoint_id].is_allocated = true; - return syncpoint_id; - } - } - ASSERT_MSG(false, "No more available syncpoints!"); - return {}; -} - -u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) { - for (u32 index = 0; index < value; ++index) { - syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed); - } - - return GetSyncpointMax(syncpoint_id); -} - -} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/syncpoint_manager.h deleted file mode 100644 index 7f080f7..0000000 --- a/src/core/hle/service/nvdrv/syncpoint_manager.h +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#include "common/common_types.h" -#include "core/hle/service/nvdrv/nvdata.h" - -namespace Tegra { -class GPU; -} - -namespace Service::Nvidia { - -class SyncpointManager final { -public: - explicit SyncpointManager(Tegra::GPU& gpu_); - ~SyncpointManager(); - - /** - * Returns true if the specified syncpoint is expired for the given value. - * @param syncpoint_id Syncpoint ID to check. - * @param value Value to check against the specified syncpoint. - * @returns True if the specified syncpoint is expired for the given value, otherwise False. - */ - bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const { - return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value); - } - - /** - * Gets the lower bound for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to get the lower bound for. - * @returns The lower bound for the specified syncpoint. - */ - u32 GetSyncpointMin(u32 syncpoint_id) const { - return syncpoints.at(syncpoint_id).min.load(std::memory_order_relaxed); - } - - /** - * Gets the uper bound for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to get the upper bound for. - * @returns The upper bound for the specified syncpoint. - */ - u32 GetSyncpointMax(u32 syncpoint_id) const { - return syncpoints.at(syncpoint_id).max.load(std::memory_order_relaxed); - } - - /** - * Refreshes the minimum value for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to be refreshed. - * @returns The new syncpoint minimum value. - */ - u32 RefreshSyncpoint(u32 syncpoint_id); - - /** - * Allocates a new syncoint. - * @returns The syncpoint ID for the newly allocated syncpoint. - */ - u32 AllocateSyncpoint(); - - /** - * Increases the maximum value for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to be increased. - * @param value Value to increase the specified syncpoint by. - * @returns The new syncpoint maximum value. - */ - u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value); - -private: - struct Syncpoint { - std::atomic min; - std::atomic max; - std::atomic is_allocated; - }; - - std::array syncpoints{}; - - Tegra::GPU& gpu; -}; - -} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp index 6d2c92a..152bb5b 100644 --- a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp +++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp @@ -39,7 +39,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco return Status::NoError; } -Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) { +Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, const Fence& release_fence) { std::scoped_lock lock{mutex}; if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence); diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.h b/src/core/hle/service/nvflinger/buffer_item_consumer.h index 6904623..a5c655d 100644 --- a/src/core/hle/service/nvflinger/buffer_item_consumer.h +++ b/src/core/hle/service/nvflinger/buffer_item_consumer.h @@ -22,7 +22,7 @@ public: explicit BufferItemConsumer(std::unique_ptr consumer); Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, bool wait_for_fence = true); - Status ReleaseBuffer(const BufferItem& item, Fence& release_fence); + Status ReleaseBuffer(const BufferItem& item, const Fence& release_fence); }; } // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp index 4b3d5ef..0767e54 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp @@ -5,15 +5,18 @@ // https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp #include "common/logging/log.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvflinger/buffer_item.h" #include "core/hle/service/nvflinger/buffer_queue_consumer.h" #include "core/hle/service/nvflinger/buffer_queue_core.h" #include "core/hle/service/nvflinger/producer_listener.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" namespace Service::android { -BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr core_) - : core{std::move(core_)}, slots{core->slots} {} +BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr core_, + Service::Nvidia::NvCore::NvMap& nvmap_) + : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {} BufferQueueConsumer::~BufferQueueConsumer() = default; @@ -133,6 +136,8 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc slots[slot].buffer_state = BufferState::Free; + nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true); + listener = core->connected_producer_listener; LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); @@ -164,7 +169,7 @@ Status BufferQueueConsumer::Connect(std::shared_ptr consumer_ return Status::NoInit; } - core->consumer_listener = consumer_listener; + core->consumer_listener = std::move(consumer_listener); core->consumer_controlled_by_app = controlled_by_app; return Status::NoError; diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h index b598c31..4ec06ca 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h @@ -13,6 +13,10 @@ #include "core/hle/service/nvflinger/buffer_queue_defs.h" #include "core/hle/service/nvflinger/status.h" +namespace Service::Nvidia::NvCore { +class NvMap; +} // namespace Service::Nvidia::NvCore + namespace Service::android { class BufferItem; @@ -21,7 +25,8 @@ class IConsumerListener; class BufferQueueConsumer final { public: - explicit BufferQueueConsumer(std::shared_ptr core_); + explicit BufferQueueConsumer(std::shared_ptr core_, + Service::Nvidia::NvCore::NvMap& nvmap_); ~BufferQueueConsumer(); Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); @@ -32,6 +37,7 @@ public: private: std::shared_ptr core; BufferQueueDefs::SlotsType& slots; + Service::Nvidia::NvCore::NvMap& nvmap; }; } // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp index ea4a14e..3d1338e 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp @@ -23,15 +23,17 @@ void BufferQueueCore::NotifyShutdown() { } void BufferQueueCore::SignalDequeueCondition() { + dequeue_possible.store(true); dequeue_condition.notify_all(); } -bool BufferQueueCore::WaitForDequeueCondition() { +bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock& lk) { if (is_shutting_down) { return false; } - dequeue_condition.wait(mutex); + dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); + dequeue_possible.store(false); return true; } diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h index ca6baef..85b3bc4 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvflinger/buffer_queue_core.h @@ -38,7 +38,7 @@ public: private: void SignalDequeueCondition(); - bool WaitForDequeueCondition(); + bool WaitForDequeueCondition(std::unique_lock& lk); s32 GetMinUndequeuedBufferCountLocked(bool async) const; s32 GetMinMaxBufferCountLocked(bool async) const; @@ -60,7 +60,8 @@ private: BufferQueueDefs::SlotsType slots{}; std::vector queue; s32 override_max_buffer_count{}; - mutable std::condition_variable_any dequeue_condition; + std::condition_variable dequeue_condition; + std::atomic dequeue_possible{}; const bool use_async_buffer{}; // This is always disabled on HOS bool dequeue_buffer_cannot_block{}; PixelFormat default_buffer_format{PixelFormat::Rgba8888}; diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index 3374314..e601b5d 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp @@ -11,10 +11,9 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvflinger/buffer_queue_core.h" #include "core/hle/service/nvflinger/buffer_queue_producer.h" #include "core/hle/service/nvflinger/consumer_listener.h" @@ -26,8 +25,10 @@ namespace Service::android { BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, - std::shared_ptr buffer_queue_core_) - : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) { + std::shared_ptr buffer_queue_core_, + Service::Nvidia::NvCore::NvMap& nvmap_) + : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots), + nvmap(nvmap_) { buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); } @@ -108,7 +109,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { core->override_max_buffer_count = buffer_count; core->SignalDequeueCondition(); - buffer_wait_event->GetWritableEvent().Signal(); + buffer_wait_event->Signal(); listener = core->consumer_listener; } @@ -120,8 +121,8 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { return Status::NoError; } -Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, - Status* return_flags) const { +Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, + std::unique_lock& lk) const { bool try_again = true; while (try_again) { @@ -213,7 +214,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, return Status::WouldBlock; } - if (!core->WaitForDequeueCondition()) { + if (!core->WaitForDequeueCondition(lk)) { // We are no longer running return Status::NoError; } @@ -236,7 +237,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool Status return_flags = Status::NoError; bool attached_by_consumer = false; { - std::scoped_lock lock{core->mutex}; + std::unique_lock lock{core->mutex}; core->WaitWhileAllocatingLocked(); if (format == PixelFormat::NoFormat) { @@ -247,7 +248,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool usage |= core->consumer_usage_bit; s32 found{}; - Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); + Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock); if (status != Status::NoError) { return status; } @@ -399,13 +400,13 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot, return Status::BadValue; } - std::scoped_lock lock{core->mutex}; + std::unique_lock lock{core->mutex}; core->WaitWhileAllocatingLocked(); Status return_flags = Status::NoError; s32 found{}; - const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags); + const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock); if (status != Status::NoError) { return status; } @@ -530,6 +531,8 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, item.is_droppable = core->dequeue_buffer_cannot_block || async; item.swap_interval = swap_interval; + nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true); + sticky_transform = sticky_transform_; if (core->queue.empty()) { @@ -619,7 +622,7 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { slots[slot].fence = fence; core->SignalDequeueCondition(); - buffer_wait_event->GetWritableEvent().Signal(); + buffer_wait_event->Signal(); } Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { @@ -739,6 +742,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { return Status::NoError; } + // HACK: We are not Android. Remove handle for items in queue, and clear queue. + // Allows synchronous destruction of nvmap handles. + for (auto& item : core->queue) { + nvmap.FreeHandle(item.graphic_buffer->BufferId(), true); + } + core->queue.clear(); + switch (api) { case NativeWindowApi::Egl: case NativeWindowApi::Cpu: @@ -749,7 +759,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { core->connected_producer_listener = nullptr; core->connected_api = NativeWindowApi::NoConnectedApi; core->SignalDequeueCondition(); - buffer_wait_event->GetWritableEvent().Signal(); + buffer_wait_event->Signal(); listener = core->consumer_listener; } else { LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})", @@ -798,7 +808,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, } core->SignalDequeueCondition(); - buffer_wait_event->GetWritableEvent().Signal(); + buffer_wait_event->Signal(); return Status::NoError; } diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h index 42d4722..1d38048 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h @@ -24,13 +24,16 @@ namespace Kernel { class KernelCore; class KEvent; class KReadableEvent; -class KWritableEvent; } // namespace Kernel namespace Service::KernelHelpers { class ServiceContext; } // namespace Service::KernelHelpers +namespace Service::Nvidia::NvCore { +class NvMap; +} // namespace Service::Nvidia::NvCore + namespace Service::android { class BufferQueueCore; @@ -39,7 +42,8 @@ class IProducerListener; class BufferQueueProducer final : public IBinder { public: explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, - std::shared_ptr buffer_queue_core_); + std::shared_ptr buffer_queue_core_, + Service::Nvidia::NvCore::NvMap& nvmap_); ~BufferQueueProducer(); void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override; @@ -66,7 +70,8 @@ public: private: BufferQueueProducer(const BufferQueueProducer&) = delete; - Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const; + Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, + std::unique_lock& lk) const; Kernel::KEvent* buffer_wait_event{}; Service::KernelHelpers::ServiceContext& service_context; @@ -78,6 +83,8 @@ private: s32 next_callback_ticket{}; s32 current_callback_ticket{}; std::condition_variable_any callback_condition; + + Service::Nvidia::NvCore::NvMap& nvmap; }; } // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp index 5b99958..982531e 100644 --- a/src/core/hle/service/nvflinger/consumer_base.cpp +++ b/src/core/hle/service/nvflinger/consumer_base.cpp @@ -83,7 +83,7 @@ Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseco } Status ConsumerBase::AddReleaseFenceLocked(s32 slot, - const std::shared_ptr graphic_buffer, + const std::shared_ptr& graphic_buffer, const Fence& fence) { LOG_DEBUG(Service_NVFlinger, "slot={}", slot); @@ -100,7 +100,7 @@ Status ConsumerBase::AddReleaseFenceLocked(s32 slot, } Status ConsumerBase::ReleaseBufferLocked(s32 slot, - const std::shared_ptr graphic_buffer) { + const std::shared_ptr& graphic_buffer) { // If consumer no longer tracks this graphic_buffer (we received a new // buffer on the same slot), the buffer producer is definitely no longer // tracking it. @@ -121,7 +121,7 @@ Status ConsumerBase::ReleaseBufferLocked(s32 slot, } bool ConsumerBase::StillTracking(s32 slot, - const std::shared_ptr graphic_buffer) const { + const std::shared_ptr& graphic_buffer) const { if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { return false; } diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h index 90ba07f..9a8a5f6 100644 --- a/src/core/hle/service/nvflinger/consumer_base.h +++ b/src/core/hle/service/nvflinger/consumer_base.h @@ -27,18 +27,18 @@ public: protected: explicit ConsumerBase(std::unique_ptr consumer_); - virtual ~ConsumerBase(); + ~ConsumerBase() override; - virtual void OnFrameAvailable(const BufferItem& item) override; - virtual void OnFrameReplaced(const BufferItem& item) override; - virtual void OnBuffersReleased() override; - virtual void OnSidebandStreamChanged() override; + void OnFrameAvailable(const BufferItem& item) override; + void OnFrameReplaced(const BufferItem& item) override; + void OnBuffersReleased() override; + void OnSidebandStreamChanged() override; void FreeBufferLocked(s32 slot_index); Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); - Status ReleaseBufferLocked(s32 slot, const std::shared_ptr graphic_buffer); - bool StillTracking(s32 slot, const std::shared_ptr graphic_buffer) const; - Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr graphic_buffer, + Status ReleaseBufferLocked(s32 slot, const std::shared_ptr& graphic_buffer); + bool StillTracking(s32 slot, const std::shared_ptr& graphic_buffer) const; + Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr& graphic_buffer, const Fence& fence); struct Slot final { diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 9b382bf..d1cbadd 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -22,7 +22,10 @@ #include "core/hle/service/nvflinger/ui/graphic_buffer.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" +#include "core/hle/service/vi/vi_results.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/syncpoint_manager.h" namespace Service::NVFlinger { @@ -30,7 +33,7 @@ constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60}; void NVFlinger::SplitVSync(std::stop_token stop_token) { system.RegisterHostThread(); - std::string name = "yuzu:VSyncThread"; + std::string name = "VSyncThread"; MicroProfileOnThreadCreate(name.c_str()); // Cleanup @@ -99,6 +102,14 @@ NVFlinger::~NVFlinger() { system.CoreTiming().UnscheduleEvent(single_composition_event, {}); } + ShutdownLayers(); + + if (nvdrv) { + nvdrv->Close(disp_fd); + } +} + +void NVFlinger::ShutdownLayers() { for (auto& display : displays) { for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { display.GetLayer(layer).Core().NotifyShutdown(); @@ -108,6 +119,7 @@ NVFlinger::~NVFlinger() { void NVFlinger::SetNVDrvInstance(std::shared_ptr instance) { nvdrv = std::move(instance); + disp_fd = nvdrv->Open("/dev/nvdisp_disp0"); } std::optional NVFlinger::OpenDisplay(std::string_view name) { @@ -126,6 +138,19 @@ std::optional NVFlinger::OpenDisplay(std::string_view name) { return itr->GetID(); } +bool NVFlinger::CloseDisplay(u64 display_id) { + const auto lock_guard = Lock(); + auto* const display = FindDisplay(display_id); + + if (display == nullptr) { + return false; + } + + display->Reset(); + + return true; +} + std::optional NVFlinger::CreateLayer(u64 display_id) { const auto lock_guard = Lock(); auto* const display = FindDisplay(display_id); @@ -141,7 +166,7 @@ std::optional NVFlinger::CreateLayer(u64 display_id) { void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { const auto buffer_id = next_buffer_queue_id++; - display.CreateLayer(layer_id, buffer_id); + display.CreateLayer(layer_id, buffer_id, nvdrv->container); } void NVFlinger::CloseLayer(u64 layer_id) { @@ -163,15 +188,15 @@ std::optional NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) { return layer->GetBinderId(); } -Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { +ResultVal NVFlinger::FindVsyncEvent(u64 display_id) { const auto lock_guard = Lock(); auto* const display = FindDisplay(display_id); if (display == nullptr) { - return nullptr; + return VI::ResultNotFound; } - return &display->GetVSyncEvent(); + return display->GetVSyncEvent(); } VI::Display* NVFlinger::FindDisplay(u64 display_id) { @@ -261,35 +286,28 @@ void NVFlinger::Compose() { return; // We are likely shutting down } - auto& gpu = system.GPU(); - const auto& multi_fence = buffer.fence; - guard->unlock(); - for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { - const auto& fence = multi_fence.fences[fence_id]; - gpu.WaitFence(fence.id, fence.value); - } - guard->lock(); - - MicroProfileFlip(); - // Now send the buffer to the GPU for drawing. // TODO(Subv): Support more than just disp0. The display device selection is probably based // on which display we're drawing (Default, Internal, External, etc) - auto nvdisp = nvdrv->GetDevice("/dev/nvdisp_disp0"); + auto nvdisp = nvdrv->GetDevice(disp_fd); ASSERT(nvdisp); + guard->unlock(); Common::Rectangle crop_rect{ static_cast(buffer.crop.Left()), static_cast(buffer.crop.Top()), static_cast(buffer.crop.Right()), static_cast(buffer.crop.Bottom())}; nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), - static_cast(buffer.transform), crop_rect); + static_cast(buffer.transform), crop_rect, + buffer.fence.fences, buffer.fence.num_fences); + + MicroProfileFlip(); + guard->lock(); swap_interval = buffer.swap_interval; - auto fence = android::Fence::NoFence(); - layer.GetConsumer().ReleaseBuffer(buffer, fence); + layer.GetConsumer().ReleaseBuffer(buffer, android::Fence::NoFence()); } } diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 044ac6a..9b22397 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -11,6 +11,8 @@ #include #include "common/common_types.h" +#include "common/polyfill_thread.h" +#include "core/hle/result.h" #include "core/hle/service/kernel_helpers.h" namespace Common { @@ -24,7 +26,6 @@ struct EventType; namespace Kernel { class KReadableEvent; -class KWritableEvent; } // namespace Kernel namespace Service::Nvidia { @@ -48,6 +49,8 @@ public: explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); ~NVFlinger(); + void ShutdownLayers(); + /// Sets the NVDrv module instance to use to send buffers to the GPU. void SetNVDrvInstance(std::shared_ptr instance); @@ -56,6 +59,11 @@ public: /// If an invalid display name is provided, then an empty optional is returned. [[nodiscard]] std::optional OpenDisplay(std::string_view name); + /// Closes the specified display by its ID. + /// + /// Returns false if an invalid display ID is provided. + [[nodiscard]] bool CloseDisplay(u64 display_id); + /// Creates a layer on the specified display and returns the layer ID. /// /// If an invalid display ID is specified, then an empty optional is returned. @@ -71,8 +79,9 @@ public: /// Gets the vsync event for the specified display. /// - /// If an invalid display ID is provided, then nullptr is returned. - [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); + /// If an invalid display ID is provided, then VI::ResultNotFound is returned. + /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned. + [[nodiscard]] ResultVal FindVsyncEvent(u64 display_id); /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when /// finished. @@ -114,6 +123,7 @@ private: void SplitVSync(std::stop_token stop_token); std::shared_ptr nvdrv; + s32 disp_fd; std::list displays; diff --git a/src/core/hle/service/nvflinger/producer_listener.h b/src/core/hle/service/nvflinger/producer_listener.h index 1c4d5db..6bf8aaf 100644 --- a/src/core/hle/service/nvflinger/producer_listener.h +++ b/src/core/hle/service/nvflinger/producer_listener.h @@ -10,6 +10,7 @@ namespace Service::android { class IProducerListener { public: + virtual ~IProducerListener() = default; virtual void OnBufferReleased() = 0; }; diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp index 2c31e94..1ac97fe 100644 --- a/src/core/hle/service/ptm/psm.cpp +++ b/src/core/hle/service/ptm/psm.cpp @@ -37,19 +37,19 @@ public: void SignalChargerTypeChanged() { if (should_signal && should_signal_charger_type) { - state_change_event->GetWritableEvent().Signal(); + state_change_event->Signal(); } } void SignalPowerSupplyChanged() { if (should_signal && should_signal_power_supply) { - state_change_event->GetWritableEvent().Signal(); + state_change_event->Signal(); } } void SignalBatteryVoltageStateChanged() { if (should_signal && should_signal_battery_voltage) { - state_change_event->GetWritableEvent().Signal(); + state_change_event->Signal(); } } diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp index 65c3f13..b1a0a55 100644 --- a/src/core/hle/service/ptm/ts.cpp +++ b/src/core/hle/service/ptm/ts.cpp @@ -15,7 +15,7 @@ TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} { {0, nullptr, "GetTemperatureRange"}, {1, &TS::GetTemperature, "GetTemperature"}, {2, nullptr, "SetMeasurementMode"}, - {3, nullptr, "GetTemperatureMilliC"}, + {3, &TS::GetTemperatureMilliC, "GetTemperatureMilliC"}, {4, nullptr, "OpenSession"}, }; // clang-format on @@ -29,8 +29,6 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto location{rp.PopEnum()}; - LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location); - const s32 temperature = location == Location::Internal ? 35 : 20; IPC::ResponseBuilder rb{ctx, 3}; @@ -38,4 +36,15 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) { rb.Push(temperature); } +void TS::GetTemperatureMilliC(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto location{rp.PopEnum()}; + + const s32 temperature = location == Location::Internal ? 35000 : 20000; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(temperature); +} + } // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h index 39a734e..39d5184 100644 --- a/src/core/hle/service/ptm/ts.h +++ b/src/core/hle/service/ptm/ts.h @@ -20,6 +20,7 @@ private: }; void GetTemperature(Kernel::HLERequestContext& ctx); + void GetTemperatureMilliC(Kernel::HLERequestContext& ctx); }; } // namespace Service::PTM diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index dadaf89..0de67f1 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -99,6 +99,12 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se ServiceFrameworkBase::~ServiceFrameworkBase() { // Wait for other threads to release access before destroying const auto guard = LockService(); + + if (named_port != nullptr) { + named_port->GetClientPort().Close(); + named_port->GetServerPort().Close(); + named_port = nullptr; + } } void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { @@ -113,15 +119,16 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { const auto guard = LockService(); - ASSERT(!service_registered); + if (named_port == nullptr) { + ASSERT(!service_registered); - auto* port = Kernel::KPort::Create(kernel); - port->Initialize(max_sessions, false, service_name); - port->GetServerPort().SetSessionHandler(shared_from_this()); + named_port = Kernel::KPort::Create(kernel); + named_port->Initialize(max_sessions, false, service_name); - service_registered = true; + service_registered = true; + } - return port->GetClientPort(); + return named_port->GetClientPort(); } void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { @@ -199,7 +206,6 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, switch (ctx.GetCommandType()) { case IPC::CommandType::Close: case IPC::CommandType::TIPC_Close: { - session.Close(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); result = IPC::ERR_REMOTE_PROCESS_DEAD; @@ -222,6 +228,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, } UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType()); + break; } // If emulation was shutdown, we are closing service threads, do not write the response back to @@ -244,6 +251,7 @@ Services::Services(std::shared_ptr& sm, Core::System& system system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory); + system.Kernel().RegisterInterfaceForNamedService("sm:", SM::ServiceManager::SessionHandler); Account::InstallInterfaces(system); AM::InstallInterfaces(*sm, *nv_flinger, system); @@ -303,4 +311,8 @@ Services::Services(std::shared_ptr& sm, Core::System& system Services::~Services() = default; +void Services::KillNVNFlinger() { + nv_flinger->ShutdownLayers(); +} + } // namespace Service diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 5bf197c..22e2119 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -20,6 +20,7 @@ class System; namespace Kernel { class HLERequestContext; class KClientPort; +class KPort; class KServerSession; class ServiceThread; } // namespace Kernel @@ -98,6 +99,9 @@ protected: /// Identifier string used to connect to the service. std::string service_name; + /// Port used by ManageNamedPort. + Kernel::KPort* named_port{}; + private: template friend class ServiceFramework; @@ -238,6 +242,8 @@ public: explicit Services(std::shared_ptr& sm, Core::System& system); ~Services(); + void KillNVNFlinger(); + private: std::unique_ptr hos_binder_driver_server; std::unique_ptr nv_flinger; diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index f761c2d..16c5eaf 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -83,7 +83,7 @@ void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_la } void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) { - const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode); + const std::size_t requested_amount = ctx.GetWriteBufferNumElements(); const std::size_t max_amount = std::min(requested_amount, max_entries); const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount); const std::size_t copy_size = copy_amount * sizeof(LanguageCode); @@ -191,6 +191,13 @@ void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) { GetKeyCodeMapImpl(ctx); } +void SET::GetDeviceNickName(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + ctx.WriteBuffer(Settings::values.device_name.GetValue()); +} + SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} { // clang-format off static const FunctionInfo functions[] = { @@ -205,7 +212,7 @@ SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} { {8, &SET::GetQuestFlag, "GetQuestFlag"}, {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, {10, nullptr, "GetFirmwareVersionForDebug"}, - {11, nullptr, "GetDeviceNickName"}, + {11, &SET::GetDeviceNickName, "GetDeviceNickName"}, }; // clang-format on diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 60cad3e..3759757 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -50,6 +50,7 @@ private: void GetRegionCode(Kernel::HLERequestContext& ctx); void GetKeyCodeMap(Kernel::HLERequestContext& ctx); void GetKeyCodeMap2(Kernel::HLERequestContext& ctx); + void GetDeviceNickName(Kernel::HLERequestContext& ctx); }; } // namespace Service::Set diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index 2a0b812..94c20ed 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -3,6 +3,7 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/file_sys/errors.h" #include "core/file_sys/system_archive/system_version.h" #include "core/hle/ipc_helpers.h" @@ -101,6 +102,88 @@ void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } +// FIXME: implement support for the real system_settings.ini + +template +static std::vector ToBytes(const T& value) { + static_assert(std::is_trivially_copyable_v); + + const auto* begin = reinterpret_cast(&value); + const auto* end = begin + sizeof(T); + + return std::vector(begin, end); +} + +using Settings = + std::map, std::less<>>, std::less<>>; + +static Settings GetSettings() { + Settings ret; + + ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0}); + ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000}); + + return ret; +} + +void SET_SYS::GetSettingsItemValueSize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + // The category of the setting. This corresponds to the top-level keys of + // system_settings.ini. + const auto setting_category_buf{ctx.ReadBuffer(0)}; + const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()}; + + // The name of the setting. This corresponds to the second-level keys of + // system_settings.ini. + const auto setting_name_buf{ctx.ReadBuffer(1)}; + const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()}; + + auto settings{GetSettings()}; + u64 response_size{0}; + + if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) { + response_size = settings[setting_category][setting_name].size(); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(response_size == 0 ? ResultUnknown : ResultSuccess); + rb.Push(response_size); +} + +void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + // The category of the setting. This corresponds to the top-level keys of + // system_settings.ini. + const auto setting_category_buf{ctx.ReadBuffer(0)}; + const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()}; + + // The name of the setting. This corresponds to the second-level keys of + // system_settings.ini. + const auto setting_name_buf{ctx.ReadBuffer(1)}; + const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()}; + + auto settings{GetSettings()}; + Result response{ResultUnknown}; + + if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) { + auto setting_value = settings[setting_category][setting_name]; + ctx.WriteBuffer(setting_value.data(), setting_value.size()); + response = ResultSuccess; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(response); +} + +void SET_SYS::GetDeviceNickName(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + ctx.WriteBuffer(::Settings::values.device_name.GetValue()); +} + SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { // clang-format off static const FunctionInfo functions[] = { @@ -138,8 +221,8 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {32, nullptr, "SetAccountNotificationSettings"}, {35, nullptr, "GetVibrationMasterVolume"}, {36, nullptr, "SetVibrationMasterVolume"}, - {37, nullptr, "GetSettingsItemValueSize"}, - {38, nullptr, "GetSettingsItemValue"}, + {37, &SET_SYS::GetSettingsItemValueSize, "GetSettingsItemValueSize"}, + {38, &SET_SYS::GetSettingsItemValue, "GetSettingsItemValue"}, {39, nullptr, "GetTvSettings"}, {40, nullptr, "SetTvSettings"}, {41, nullptr, "GetEdid"}, @@ -178,7 +261,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {74, nullptr, "SetWirelessLanEnableFlag"}, {75, nullptr, "GetInitialLaunchSettings"}, {76, nullptr, "SetInitialLaunchSettings"}, - {77, nullptr, "GetDeviceNickName"}, + {77, &SET_SYS::GetDeviceNickName, "GetDeviceNickName"}, {78, nullptr, "SetDeviceNickName"}, {79, nullptr, "GetProductModel"}, {80, nullptr, "GetLdnChannel"}, diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index ac97772..464ac3d 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -23,10 +23,13 @@ private: BasicBlack = 1, }; + void GetSettingsItemValueSize(Kernel::HLERequestContext& ctx); + void GetSettingsItemValue(Kernel::HLERequestContext& ctx); void GetFirmwareVersion(Kernel::HLERequestContext& ctx); void GetFirmwareVersion2(Kernel::HLERequestContext& ctx); void GetColorSetId(Kernel::HLERequestContext& ctx); void SetColorSetId(Kernel::HLERequestContext& ctx); + void GetDeviceNickName(Kernel::HLERequestContext& ctx); ColorSet color_set = ColorSet::BasicWhite; }; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 246c946..8472009 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -23,7 +23,13 @@ constexpr Result ERR_INVALID_NAME(ErrorModule::SM, 6); constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} -ServiceManager::~ServiceManager() = default; + +ServiceManager::~ServiceManager() { + for (auto& [name, port] : service_ports) { + port->GetClientPort().Close(); + port->GetServerPort().Close(); + } +} void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { controller_interface->InvokeRequest(context); @@ -43,6 +49,10 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core return self.sm_interface->CreatePort(); } +void ServiceManager::SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port) { + self.sm_interface->AcceptSession(server_port); +} + Result ServiceManager::RegisterService(std::string name, u32 max_sessions, Kernel::SessionRequestHandlerPtr handler) { @@ -53,7 +63,11 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions, return ERR_ALREADY_REGISTERED; } - registered_services.emplace(std::move(name), handler); + auto* port = Kernel::KPort::Create(kernel); + port->Initialize(ServerSessionCountMax, false, name); + + service_ports.emplace(name, port); + registered_services.emplace(name, handler); return ResultSuccess; } @@ -68,25 +82,20 @@ Result ServiceManager::UnregisterService(const std::string& name) { } registered_services.erase(iter); + service_ports.erase(name); + return ResultSuccess; } ResultVal ServiceManager::GetServicePort(const std::string& name) { CASCADE_CODE(ValidateServiceName(name)); - auto it = registered_services.find(name); - if (it == registered_services.end()) { + auto it = service_ports.find(name); + if (it == service_ports.end()) { LOG_ERROR(Service_SM, "Server is not registered! service={}", name); return ERR_SERVICE_NOT_REGISTERED; } - auto* port = Kernel::KPort::Create(kernel); - SCOPE_EXIT({ port->Close(); }); - - port->Initialize(ServerSessionCountMax, false, name); - auto handler = it->second; - port->GetServerPort().SetSessionHandler(std::move(handler)); - - return port; + return it->second; } /** @@ -145,22 +154,20 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& // Find the named port. auto port_result = service_manager.GetServicePort(name); - if (port_result.Failed()) { + auto service = service_manager.GetService(name); + if (port_result.Failed() || !service) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); return port_result.Code(); } auto& port = port_result.Unwrap(); - SCOPE_EXIT({ port->GetClientPort().Close(); }); - - kernel.RegisterServerObject(&port->GetServerPort()); // Create a new session. Kernel::KClientSession* session{}; - if (const auto result = port->GetClientPort().CreateSession(std::addressof(session)); - result.IsError()) { + if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); return result; } + service->AcceptSession(&port->GetServerPort()); LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId()); diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 878decc..02a5dde 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -51,6 +51,7 @@ private: class ServiceManager { public: static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system); + static void SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port); explicit ServiceManager(Kernel::KernelCore& kernel_); ~ServiceManager(); @@ -78,6 +79,7 @@ private: /// Map of registered services, retrieved using GetServicePort. std::unordered_map registered_services; + std::unordered_map service_ports; /// Kernel context Kernel::KernelCore& kernel; diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 2a4bd64..1cf9dd1 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -15,9 +15,9 @@ namespace Service::SM { void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { - ASSERT_MSG(!ctx.Session()->IsDomain(), "Session is already a domain"); + ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Session is already a domain"); LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); - ctx.Session()->ConvertToDomain(); + ctx.GetManager()->ConvertToDomainOnRequestEnd(); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -27,23 +27,35 @@ void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service, "called"); - auto& parent_session = *ctx.Session()->GetParent(); - auto& parent_port = parent_session.GetParent()->GetParent()->GetClientPort(); - auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager(); + auto& process = *ctx.GetThread().GetOwnerProcess(); + auto session_manager = ctx.GetManager(); - // Create a session. - Kernel::KClientSession* session{}; - const Result result = parent_port.CreateSession(std::addressof(session), session_manager); - if (result.IsError()) { - LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } + // FIXME: this is duplicated from the SVC, it should just call it instead + // once this is a proper process + + // Reserve a new session from the process resource limit. + Kernel::KScopedResourceReservation session_reservation( + &process, Kernel::LimitableResource::SessionCountMax); + ASSERT(session_reservation.Succeeded()); + + // Create the session. + Kernel::KSession* session = Kernel::KSession::Create(system.Kernel()); + ASSERT(session != nullptr); + + // Initialize the session. + session->Initialize(nullptr, ""); + + // Commit the session reservation. + session_reservation.Commit(); + + // Register with manager. + session_manager->SessionHandler().RegisterSession(&session->GetServerSession(), + session_manager); // We succeeded. IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(ResultSuccess); - rb.PushMoveObjects(session); + rb.PushMoveObjects(session->GetClientSession()); } void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index cc679cc..9e94a46 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -929,7 +929,7 @@ BSD::BSD(Core::System& system_, const char* name) proxy_packet_received = room_member->BindOnProxyPacketReceived( [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); }); } else { - LOG_ERROR(Service, "Network isn't initalized"); + LOG_ERROR(Service, "Network isn't initialized"); } } diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp index a649bed..cafc04e 100644 --- a/src/core/hle/service/time/system_clock_context_update_callback.cpp +++ b/src/core/hle/service/time/system_clock_context_update_callback.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/service/time/errors.h" #include "core/hle/service/time/system_clock_context_update_callback.h" @@ -20,13 +20,13 @@ bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& valu } void SystemClockContextUpdateCallback::RegisterOperationEvent( - std::shared_ptr&& writable_event) { - operation_event_list.emplace_back(std::move(writable_event)); + std::shared_ptr&& event) { + operation_event_list.emplace_back(std::move(event)); } void SystemClockContextUpdateCallback::BroadcastOperationEvent() { - for (const auto& writable_event : operation_event_list) { - writable_event->Signal(); + for (const auto& event : operation_event_list) { + event->Signal(); } } diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h index 9c6caf1..bf657ac 100644 --- a/src/core/hle/service/time/system_clock_context_update_callback.h +++ b/src/core/hle/service/time/system_clock_context_update_callback.h @@ -9,7 +9,7 @@ #include "core/hle/service/time/clock_types.h" namespace Kernel { -class KWritableEvent; +class KEvent; } namespace Service::Time::Clock { @@ -24,7 +24,7 @@ public: bool NeedUpdate(const SystemClockContext& value) const; - void RegisterOperationEvent(std::shared_ptr&& writable_event); + void RegisterOperationEvent(std::shared_ptr&& event); void BroadcastOperationEvent(); @@ -37,7 +37,7 @@ protected: private: bool has_context{}; - std::vector> operation_event_list; + std::vector> operation_event_list; }; } // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp index 2aa675d..f9ada7c 100644 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ b/src/core/hle/service/time/time_zone_manager.cpp @@ -280,6 +280,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) { } default: ASSERT(false); + break; } return value + rule.transition_time + offset; } diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index b34febb..8ef74f1 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -10,8 +10,8 @@ #include "core/core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvflinger/buffer_item_consumer.h" #include "core/hle/service/nvflinger/buffer_queue_consumer.h" #include "core/hle/service/nvflinger/buffer_queue_core.h" @@ -19,6 +19,7 @@ #include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" +#include "core/hle/service/vi/vi_results.h" namespace Service::VI { @@ -28,11 +29,13 @@ struct BufferQueue { std::unique_ptr consumer; }; -static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) { +static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context, + Service::Nvidia::NvCore::NvMap& nvmap) { auto buffer_queue_core = std::make_shared(); - return {buffer_queue_core, - std::make_unique(service_context, buffer_queue_core), - std::make_unique(buffer_queue_core)}; + return { + buffer_queue_core, + std::make_unique(service_context, buffer_queue_core, nvmap), + std::make_unique(buffer_queue_core, nvmap)}; } Display::Display(u64 id, std::string name_, @@ -55,18 +58,29 @@ const Layer& Display::GetLayer(std::size_t index) const { return *layers.at(index); } -Kernel::KReadableEvent& Display::GetVSyncEvent() { - return vsync_event->GetReadableEvent(); +ResultVal Display::GetVSyncEvent() { + if (got_vsync_event) { + return ResultPermissionDenied; + } + + got_vsync_event = true; + + return GetVSyncEventUnchecked(); +} + +Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { + return &vsync_event->GetReadableEvent(); } void Display::SignalVSyncEvent() { - vsync_event->GetWritableEvent().Signal(); + vsync_event->Signal(); } -void Display::CreateLayer(u64 layer_id, u32 binder_id) { +void Display::CreateLayer(u64 layer_id, u32 binder_id, + Service::Nvidia::NvCore::Container& nv_core) { ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); - auto [core, producer, consumer] = CreateBufferQueue(service_context); + auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); auto buffer_item_consumer = std::make_shared(std::move(consumer)); buffer_item_consumer->Connect(false); diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 3838bb5..0b65a65 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -9,6 +9,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" +#include "core/hle/result.h" namespace Kernel { class KEvent; @@ -26,6 +27,11 @@ namespace Service::NVFlinger { class HosBinderDriverServer; } +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore + namespace Service::VI { class Layer; @@ -73,8 +79,16 @@ public: return layers.size(); } - /// Gets the readable vsync event. - Kernel::KReadableEvent& GetVSyncEvent(); + /** + * Gets the internal vsync event. + * + * @returns The internal Vsync event if it has not yet been retrieved, + * VI::ResultPermissionDenied otherwise. + */ + [[nodiscard]] ResultVal GetVSyncEvent(); + + /// Gets the internal vsync event. + Kernel::KReadableEvent* GetVSyncEventUnchecked(); /// Signals the internal vsync event. void SignalVSyncEvent(); @@ -84,7 +98,7 @@ public: /// @param layer_id The ID to assign to the created layer. /// @param binder_id The ID assigned to the buffer queue. /// - void CreateLayer(u64 layer_id, u32 binder_id); + void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core); /// Closes and removes a layer from this display with the given ID. /// @@ -92,6 +106,12 @@ public: /// void CloseLayer(u64 layer_id); + /// Resets the display for a new connection. + void Reset() { + layers.clear(); + got_vsync_event = false; + } + /// Attempts to find a layer with the given ID. /// /// @param layer_id The layer ID. @@ -118,6 +138,7 @@ private: std::vector> layers; Kernel::KEvent* vsync_event{}; + bool got_vsync_event{false}; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 5468796..bb283e7 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -29,16 +29,12 @@ #include "core/hle/service/service.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" +#include "core/hle/service/vi/vi_results.h" #include "core/hle/service/vi/vi_s.h" #include "core/hle/service/vi/vi_u.h" namespace Service::VI { -constexpr Result ERR_OPERATION_FAILED{ErrorModule::VI, 1}; -constexpr Result ERR_PERMISSION_DENIED{ErrorModule::VI, 5}; -constexpr Result ERR_UNSUPPORTED{ErrorModule::VI, 6}; -constexpr Result ERR_NOT_FOUND{ErrorModule::VI, 7}; - struct DisplayInfo { /// The name of this particular display. char display_name[0x40]{"Default"}; @@ -62,6 +58,7 @@ static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); class NativeWindow final { public: constexpr explicit NativeWindow(u32 id_) : id{id_} {} + constexpr explicit NativeWindow(const NativeWindow& other) = default; private: const u32 magic = 2; @@ -327,10 +324,10 @@ private: IPC::RequestParser rp{ctx}; const u64 display = rp.Pop(); - LOG_WARNING(Service_VI, "(STUBBED) called. display=0x{:016X}", display); + const Result rc = nv_flinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown; IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(rc); } void CreateManagedLayer(Kernel::HLERequestContext& ctx) { @@ -348,7 +345,7 @@ private: if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -498,7 +495,7 @@ private: if (!display_id) { LOG_ERROR(Service_VI, "Display not found! display_name={}", name); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -511,10 +508,10 @@ private: IPC::RequestParser rp{ctx}; const u64 display_id = rp.Pop(); - LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); + const Result rc = nv_flinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown; IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(rc); } // This literally does nothing internally in the actual service itself, @@ -554,14 +551,14 @@ private: if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) { LOG_ERROR(Service_VI, "Invalid scaling mode provided."); - rb.Push(ERR_OPERATION_FAILED); + rb.Push(ResultOperationFailed); return; } if (scaling_mode != NintendoScaleMode::ScaleToWindow && scaling_mode != NintendoScaleMode::PreserveAspectRatio) { LOG_ERROR(Service_VI, "Unsupported scaling mode supplied."); - rb.Push(ERR_UNSUPPORTED); + rb.Push(ResultNotSupported); return; } @@ -594,7 +591,7 @@ private: if (!display_id) { LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -602,7 +599,7 @@ private: if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -640,7 +637,7 @@ private: if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -648,7 +645,7 @@ private: if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -675,19 +672,23 @@ private: IPC::RequestParser rp{ctx}; const u64 display_id = rp.Pop(); - LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); + LOG_DEBUG(Service_VI, "called. display_id={}", display_id); const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); - if (!vsync_event) { - LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); + if (vsync_event.Failed()) { + const auto result = vsync_event.Code(); + if (result == ResultNotFound) { + LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(result); return; } IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(vsync_event); + rb.PushCopyObjects(*vsync_event); } void ConvertScalingMode(Kernel::HLERequestContext& ctx) { @@ -764,7 +765,7 @@ private: return ConvertedScaleMode::PreserveAspectRatio; default: LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); - return ERR_OPERATION_FAILED; + return ResultOperationFailed; } } @@ -794,7 +795,7 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& if (!IsValidServiceAccess(permission, policy)) { LOG_ERROR(Service_VI, "Permission denied for policy {}", policy); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_PERMISSION_DENIED); + rb.Push(ResultPermissionDenied); return; } diff --git a/src/core/hle/service/vi/vi_results.h b/src/core/hle/service/vi/vi_results.h new file mode 100644 index 0000000..22bac79 --- /dev/null +++ b/src/core/hle/service/vi/vi_results.h @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::VI { + +constexpr Result ResultOperationFailed{ErrorModule::VI, 1}; +constexpr Result ResultPermissionDenied{ErrorModule::VI, 5}; +constexpr Result ResultNotSupported{ErrorModule::VI, 6}; +constexpr Result ResultNotFound{ErrorModule::VI, 7}; + +} // namespace Service::VI diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index cdf38a2..447fbff 100644 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp @@ -364,7 +364,7 @@ std::pair Poll(std::vector& pollfds, s32 timeout) { std::vector host_pollfds(pollfds.size()); std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { WSAPOLLFD result; - result.fd = fd.socket->fd; + result.fd = fd.socket->GetFD(); result.events = TranslatePollEvents(fd.events); result.revents = 0; return result; @@ -430,12 +430,12 @@ std::pair Socket::Accept() { return {AcceptResult{}, GetAndLogLastError()}; } - AcceptResult result; - result.socket = std::make_unique(); - result.socket->fd = new_socket; - ASSERT(addrlen == sizeof(sockaddr_in)); - result.sockaddr_in = TranslateToSockAddrIn(addr); + + AcceptResult result{ + .socket = std::make_unique(new_socket), + .sockaddr_in = TranslateToSockAddrIn(addr), + }; return {std::move(result), Errno::SUCCESS}; } diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp index 057fd36..4c909a6 100644 --- a/src/core/internal_network/network_interface.cpp +++ b/src/core/internal_network/network_interface.cpp @@ -9,6 +9,7 @@ #include "common/bit_cast.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "common/string_util.h" #include "core/internal_network/network_interface.h" @@ -199,7 +200,7 @@ std::optional GetSelectedNetworkInterface() { }); if (res == network_interfaces.end()) { - LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + LOG_DEBUG(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); return std::nullopt; } diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index 7d5d37b..1e1c42c 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -11,6 +11,10 @@ #include "core/internal_network/network_interface.h" #include "core/internal_network/socket_proxy.h" +#if YUZU_UNIX +#include +#endif + namespace Network { ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {} diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h index a70429b..2e328c6 100644 --- a/src/core/internal_network/sockets.h +++ b/src/core/internal_network/sockets.h @@ -32,6 +32,10 @@ public: std::unique_ptr socket; SockAddrIn sockaddr_in; }; + + SocketBase() = default; + explicit SocketBase(SOCKET fd_) : fd{fd_} {} + virtual ~SocketBase() = default; virtual SocketBase& operator=(const SocketBase&) = delete; @@ -89,12 +93,19 @@ public: virtual void HandleProxyPacket(const ProxyPacket& packet) = 0; + [[nodiscard]] SOCKET GetFD() const { + return fd; + } + +protected: SOCKET fd = INVALID_SOCKET; }; class Socket : public SocketBase { public: Socket() = default; + explicit Socket(SOCKET fd_) : SocketBase{fd_} {} + ~Socket() override; Socket(const Socket&) = delete; diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 104d16e..f24474e 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -244,6 +244,10 @@ static std::unique_ptr GetFileLoader(Core::System& system, FileSys::V std::unique_ptr GetLoader(Core::System& system, FileSys::VirtualFile file, u64 program_id, std::size_t program_index) { + if (!file) { + return nullptr; + } + FileType type = IdentifyFile(file); const FileType filename_type = GuessFromFilename(file->GetName()); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 34ad7ca..26be74d 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -65,7 +65,7 @@ struct Memory::Impl { return {}; } - return system.DeviceMemory().GetPointer(paddr) + vaddr; + return system.DeviceMemory().GetPointer(paddr) + vaddr; } [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const { @@ -75,7 +75,7 @@ struct Memory::Impl { return {}; } - return system.DeviceMemory().GetPointer(paddr) + vaddr; + return system.DeviceMemory().GetPointer(paddr) + vaddr; } u8 Read8(const VAddr addr) { @@ -194,13 +194,11 @@ struct Memory::Impl { break; } case Common::PageType::Memory: { - DEBUG_ASSERT(pointer); u8* mem_ptr = pointer + page_offset + (page_index << YUZU_PAGEBITS); on_memory(copy_amount, mem_ptr); break; } case Common::PageType::DebugMemory: { - DEBUG_ASSERT(pointer); u8* const mem_ptr{GetPointerFromDebugMemory(current_vaddr)}; on_memory(copy_amount, mem_ptr); break; @@ -233,18 +231,17 @@ struct Memory::Impl { current_vaddr, src_addr, size); std::memset(dest_buffer, 0, copy_amount); }, - [&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) { + [&](const std::size_t copy_amount, const u8* const src_ptr) { std::memcpy(dest_buffer, src_ptr, copy_amount); }, - [&system = system, &dest_buffer](const VAddr current_vaddr, - const std::size_t copy_amount, - const u8* const host_ptr) { + [&](const VAddr current_vaddr, const std::size_t copy_amount, + const u8* const host_ptr) { if constexpr (!UNSAFE) { system.GPU().FlushRegion(current_vaddr, copy_amount); } std::memcpy(dest_buffer, host_ptr, copy_amount); }, - [&dest_buffer](const std::size_t copy_amount) { + [&](const std::size_t copy_amount) { dest_buffer = static_cast(dest_buffer) + copy_amount; }); } @@ -267,17 +264,16 @@ struct Memory::Impl { "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", current_vaddr, dest_addr, size); }, - [&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) { + [&](const std::size_t copy_amount, u8* const dest_ptr) { std::memcpy(dest_ptr, src_buffer, copy_amount); }, - [&system = system, &src_buffer](const VAddr current_vaddr, - const std::size_t copy_amount, u8* const host_ptr) { + [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { if constexpr (!UNSAFE) { system.GPU().InvalidateRegion(current_vaddr, copy_amount); } std::memcpy(host_ptr, src_buffer, copy_amount); }, - [&src_buffer](const std::size_t copy_amount) { + [&](const std::size_t copy_amount) { src_buffer = static_cast(src_buffer) + copy_amount; }); } @@ -301,8 +297,7 @@ struct Memory::Impl { [](const std::size_t copy_amount, u8* const dest_ptr) { std::memset(dest_ptr, 0, copy_amount); }, - [&system = system](const VAddr current_vaddr, const std::size_t copy_amount, - u8* const host_ptr) { + [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { system.GPU().InvalidateRegion(current_vaddr, copy_amount); std::memset(host_ptr, 0, copy_amount); }, @@ -313,27 +308,76 @@ struct Memory::Impl { const std::size_t size) { WalkBlock( process, dest_addr, size, - [this, &process, &dest_addr, &src_addr, size](const std::size_t copy_amount, - const VAddr current_vaddr) { + [&](const std::size_t copy_amount, const VAddr current_vaddr) { LOG_ERROR(HW_Memory, "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", current_vaddr, src_addr, size); ZeroBlock(process, dest_addr, copy_amount); }, - [this, &process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) { + [&](const std::size_t copy_amount, const u8* const src_ptr) { WriteBlockImpl(process, dest_addr, src_ptr, copy_amount); }, - [this, &system = system, &process, &dest_addr]( - const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { + [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { system.GPU().FlushRegion(current_vaddr, copy_amount); WriteBlockImpl(process, dest_addr, host_ptr, copy_amount); }, - [&dest_addr, &src_addr](const std::size_t copy_amount) { + [&](const std::size_t copy_amount) { dest_addr += static_cast(copy_amount); src_addr += static_cast(copy_amount); }); } + template + Result PerformCacheOperation(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size, + Callback&& cb) { + class InvalidMemoryException : public std::exception {}; + + try { + WalkBlock( + process, dest_addr, size, + [&](const std::size_t block_size, const VAddr current_vaddr) { + LOG_ERROR(HW_Memory, "Unmapped cache maintenance @ {:#018X}", current_vaddr); + throw InvalidMemoryException(); + }, + [&](const std::size_t block_size, u8* const host_ptr) {}, + [&](const VAddr current_vaddr, const std::size_t block_size, u8* const host_ptr) { + cb(current_vaddr, block_size); + }, + [](const std::size_t block_size) {}); + } catch (InvalidMemoryException&) { + return Kernel::ResultInvalidCurrentMemory; + } + + return ResultSuccess; + } + + Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) { + auto on_rasterizer = [&](const VAddr current_vaddr, const std::size_t block_size) { + // dc ivac: Invalidate to point of coherency + // GPU flush -> CPU invalidate + system.GPU().FlushRegion(current_vaddr, block_size); + }; + return PerformCacheOperation(process, dest_addr, size, on_rasterizer); + } + + Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) { + auto on_rasterizer = [&](const VAddr current_vaddr, const std::size_t block_size) { + // dc cvac: Store to point of coherency + // CPU flush -> GPU invalidate + system.GPU().InvalidateRegion(current_vaddr, block_size); + }; + return PerformCacheOperation(process, dest_addr, size, on_rasterizer); + } + + Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) { + auto on_rasterizer = [&](const VAddr current_vaddr, const std::size_t block_size) { + // dc civac: Store to point of coherency, and invalidate from cache + // CPU flush -> GPU invalidate + system.GPU().InvalidateRegion(current_vaddr, block_size); + }; + return PerformCacheOperation(process, dest_addr, size, on_rasterizer); + } + void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) { if (vaddr == 0) { return; @@ -499,7 +543,7 @@ struct Memory::Impl { } else { while (base != end) { page_table.pointers[base].Store( - system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type); + system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type); page_table.backing_addr[base] = target - (base << YUZU_PAGEBITS); ASSERT_MSG(page_table.pointers[base].Pointer(), @@ -551,6 +595,11 @@ struct Memory::Impl { []() {}); } + [[nodiscard]] u8* GetPointerSilent(const VAddr vaddr) const { + return GetPointerImpl( + vaddr, []() {}, []() {}); + } + /** * Reads a particular data type out of memory at the given virtual address. * @@ -570,7 +619,7 @@ struct Memory::Impl { [vaddr]() { LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr); }, - [&system = system, vaddr]() { system.GPU().FlushRegion(vaddr, sizeof(T)); }); + [&]() { system.GPU().FlushRegion(vaddr, sizeof(T)); }); if (ptr) { std::memcpy(&result, ptr, sizeof(T)); } @@ -594,7 +643,7 @@ struct Memory::Impl { LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, vaddr, static_cast(data)); }, - [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); + [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); if (ptr) { std::memcpy(ptr, &data, sizeof(T)); } @@ -608,7 +657,7 @@ struct Memory::Impl { LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, vaddr, static_cast(data)); }, - [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); + [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); if (ptr) { const auto volatile_pointer = reinterpret_cast(ptr); return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); @@ -623,7 +672,7 @@ struct Memory::Impl { LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}", vaddr, static_cast(data[1]), static_cast(data[0])); }, - [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); }); + [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); }); if (ptr) { const auto volatile_pointer = reinterpret_cast(ptr); return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); @@ -686,6 +735,10 @@ u8* Memory::GetPointer(VAddr vaddr) { return impl->GetPointer(vaddr); } +u8* Memory::GetPointerSilent(VAddr vaddr) { + return impl->GetPointerSilent(vaddr); +} + const u8* Memory::GetPointer(VAddr vaddr) const { return impl->GetPointer(vaddr); } @@ -782,6 +835,21 @@ void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const s impl->ZeroBlock(process, dest_addr, size); } +Result Memory::InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, + const std::size_t size) { + return impl->InvalidateDataCache(process, dest_addr, size); +} + +Result Memory::StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, + const std::size_t size) { + return impl->StoreDataCache(process, dest_addr, size); +} + +Result Memory::FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, + const std::size_t size) { + return impl->FlushDataCache(process, dest_addr, size); +} + void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { impl->RasterizerMarkRegionCached(vaddr, size, cached); } diff --git a/src/core/memory.h b/src/core/memory.h index a11ff87..31fe699 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -7,6 +7,7 @@ #include #include #include "common/common_types.h" +#include "core/hle/result.h" namespace Common { struct PageTable; @@ -114,6 +115,7 @@ public: * If the address is not valid, nullptr will be returned. */ u8* GetPointer(VAddr vaddr); + u8* GetPointerSilent(VAddr vaddr); template T* GetPointer(VAddr vaddr) { @@ -448,6 +450,39 @@ public: */ void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); + /** + * Invalidates a range of bytes within the current process' address space at the specified + * virtual address. + * + * @param process The process that will have data invalidated within its address space. + * @param dest_addr The destination virtual address to invalidate the data from. + * @param size The size of the range to invalidate, in bytes. + * + */ + Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); + + /** + * Stores a range of bytes within the current process' address space at the specified + * virtual address. + * + * @param process The process that will have data stored within its address space. + * @param dest_addr The destination virtual address to store the data from. + * @param size The size of the range to store, in bytes. + * + */ + Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); + + /** + * Flushes a range of bytes within the current process' address space at the specified + * virtual address. + * + * @param process The process that will have data flushed within its address space. + * @param dest_addr The destination virtual address to flush the data from. + * @param size The size of the range to flush, in bytes. + * + */ + Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); + /** * Marks each page within the specified address range as cached or uncached. * diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp index de96fcb..31ffc4f 100644 --- a/src/core/memory/dmnt_cheat_vm.cpp +++ b/src/core/memory/dmnt_cheat_vm.cpp @@ -761,7 +761,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { u64 src_address = GetCheatProcessAddress(metadata, begin_cond->mem_type, begin_cond->rel_address); u64 src_value = 0; - switch (store_static->bit_width) { + switch (begin_cond->bit_width) { case 1: case 2: case 4: diff --git a/src/core/precompiled_headers.h b/src/core/precompiled_headers.h new file mode 100644 index 0000000..30a3100 --- /dev/null +++ b/src/core/precompiled_headers.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include // used by service.h which is heavily included +#include // used by k_auto_object.h which is heavily included + +#include "common/common_precompiled_headers.h" + +#include "core/hle/kernel/k_process.h" diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 6e21296..77821e0 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -38,7 +38,7 @@ std::string GetTimestamp() { using namespace nlohmann; -void SaveToFile(json json, const std::filesystem::path& filename) { +void SaveToFile(const json& json, const std::filesystem::path& filename) { if (!Common::FS::CreateParentDirs(filename)) { LOG_ERROR(Core, "Failed to create path for '{}' to save report!", Common::FS::PathToUTF8String(filename)); @@ -81,8 +81,8 @@ json GetReportCommonData(u64 title_id, Result result, const std::string& timesta } json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 sp, u64 pc, - u64 pstate, std::array registers, - std::optional> backtrace = {}) { + u64 pstate, const std::array& registers, + const std::optional>& backtrace = {}) { auto out = json{ {"entry_point", fmt::format("{:016X}", entry_point)}, {"sp", fmt::format("{:016X}", sp)}, @@ -224,11 +224,11 @@ void Reporter::SaveCrashReport(u64 title_id, Result result, u64 set_flags, u64 e out["processor_state"] = std::move(proc_out); - SaveToFile(std::move(out), GetPath("crash_report", title_id, timestamp)); + SaveToFile(out, GetPath("crash_report", title_id, timestamp)); } void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, - std::optional> resolved_buffer) const { + const std::optional>& resolved_buffer) const { if (!IsReportingEnabled()) { return; } @@ -250,7 +250,7 @@ void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 out["svc_break"] = std::move(break_out); - SaveToFile(std::move(out), GetPath("svc_break_report", title_id, timestamp)); + SaveToFile(out, GetPath("svc_break_report", title_id, timestamp)); } void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, @@ -271,13 +271,13 @@ void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u out["function"] = std::move(function_out); - SaveToFile(std::move(out), GetPath("unimpl_func_report", title_id, timestamp)); + SaveToFile(out, GetPath("unimpl_func_report", title_id, timestamp)); } void Reporter::SaveUnimplementedAppletReport( u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color, - bool startup_sound, u64 system_tick, std::vector> normal_channel, - std::vector> interactive_channel) const { + bool startup_sound, u64 system_tick, const std::vector>& normal_channel, + const std::vector>& interactive_channel) const { if (!IsReportingEnabled()) { return; } @@ -308,10 +308,11 @@ void Reporter::SaveUnimplementedAppletReport( out["applet_normal_data"] = std::move(normal_out); out["applet_interactive_data"] = std::move(interactive_out); - SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp)); + SaveToFile(out, GetPath("unimpl_applet_report", title_id, timestamp)); } -void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector> data, +void Reporter::SavePlayReport(PlayReportType type, u64 title_id, + const std::vector>& data, std::optional process_id, std::optional user_id) const { if (!IsReportingEnabled()) { return; @@ -335,12 +336,12 @@ void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector(type)); out["play_report_data"] = std::move(data_out); - SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp)); + SaveToFile(out, GetPath("play_report", title_id, timestamp)); } void Reporter::SaveErrorReport(u64 title_id, Result result, - std::optional custom_text_main, - std::optional custom_text_detail) const { + const std::optional& custom_text_main, + const std::optional& custom_text_detail) const { if (!IsReportingEnabled()) { return; } @@ -354,11 +355,11 @@ void Reporter::SaveErrorReport(u64 title_id, Result result, out["backtrace"] = GetBacktraceData(system); out["error_custom_text"] = { - {"main", *custom_text_main}, - {"detail", *custom_text_detail}, + {"main", custom_text_main.value_or("")}, + {"detail", custom_text_detail.value_or("")}, }; - SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); + SaveToFile(out, GetPath("error_report", title_id, timestamp)); } void Reporter::SaveFSAccessLog(std::string_view log_message) const { diff --git a/src/core/reporter.h b/src/core/reporter.h index 68755cb..9fdb9d6 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h @@ -36,7 +36,7 @@ public: // Used by syscall svcBreak void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, - std::optional> resolved_buffer = {}) const; + const std::optional>& resolved_buffer = {}) const; // Used by HLE service handler void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, @@ -44,10 +44,10 @@ public: const std::string& service_name) const; // Used by stub applet implementation - void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version, - u32 theme_color, bool startup_sound, u64 system_tick, - std::vector> normal_channel, - std::vector> interactive_channel) const; + void SaveUnimplementedAppletReport( + u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color, + bool startup_sound, u64 system_tick, const std::vector>& normal_channel, + const std::vector>& interactive_channel) const; enum class PlayReportType { Old, @@ -56,13 +56,13 @@ public: System, }; - void SavePlayReport(PlayReportType type, u64 title_id, std::vector> data, + void SavePlayReport(PlayReportType type, u64 title_id, const std::vector>& data, std::optional process_id = {}, std::optional user_id = {}) const; // Used by error applet void SaveErrorReport(u64 title_id, Result result, - std::optional custom_text_main = {}, - std::optional custom_text_detail = {}) const; + const std::optional& custom_text_main = {}, + const std::optional& custom_text_detail = {}) const; void SaveFSAccessLog(std::string_view log_message) const; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index abcf6eb..8d5f2be 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -55,6 +55,8 @@ static const char* TranslateRenderer(Settings::RendererBackend backend) { return "OpenGL"; case Settings::RendererBackend::Vulkan: return "Vulkan"; + case Settings::RendererBackend::Null: + return "Null"; } return "Unknown"; } diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt index 1efdbc1..5bbe1d4 100644 --- a/src/dedicated_room/CMakeLists.txt +++ b/src/dedicated_room/CMakeLists.txt @@ -4,6 +4,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) add_executable(yuzu-room + precompiled_headers.h yuzu_room.cpp yuzu_room.rc ) @@ -23,5 +24,9 @@ endif() target_link_libraries(yuzu-room PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) if(UNIX AND NOT APPLE) - install(TARGETS yuzu-room RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") + install(TARGETS yuzu-room) +endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(yuzu-room PRIVATE precompiled_headers.h) endif() diff --git a/src/dedicated_room/precompiled_headers.h b/src/dedicated_room/precompiled_headers.h new file mode 100644 index 0000000..aabae73 --- /dev/null +++ b/src/dedicated_room/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 4b91b88..f24c89b 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -18,6 +18,10 @@ add_library(input_common STATIC drivers/touch_screen.h drivers/udp_client.cpp drivers/udp_client.h + drivers/virtual_amiibo.cpp + drivers/virtual_amiibo.h + drivers/virtual_gamepad.cpp + drivers/virtual_gamepad.h helpers/stick_from_buttons.cpp helpers/stick_from_buttons.h helpers/touch_from_buttons.cpp @@ -32,26 +36,20 @@ add_library(input_common STATIC input_poller.h main.cpp main.h + precompiled_headers.h ) if (MSVC) target_compile_options(input_common PRIVATE /W4 - /WX /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data - /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data - /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) else() target_compile_options(input_common PRIVATE - -Werror -Werror=conversion - -Werror=ignored-qualifiers - $<$:-Werror=unused-but-set-parameter> - $<$:-Werror=unused-but-set-variable> - -Werror=unused-variable ) endif() @@ -60,11 +58,13 @@ if (ENABLE_SDL2) drivers/sdl_driver.cpp drivers/sdl_driver.h ) - target_link_libraries(input_common PRIVATE SDL2) + target_link_libraries(input_common PRIVATE SDL2::SDL2) target_compile_definitions(input_common PRIVATE HAVE_SDL2) endif() -target_link_libraries(input_common PRIVATE usb) - create_target_directory_groups(input_common) -target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) +target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost libusb::usb) + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(input_common PRIVATE precompiled_headers.h) +endif() diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp index dceea67..fad9177 100644 --- a/src/input_common/drivers/camera.cpp +++ b/src/input_common/drivers/camera.cpp @@ -17,7 +17,7 @@ Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_) PreSetController(identifier); } -void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector data) { +void Camera::SetCameraData(std::size_t width, std::size_t height, std::span data) { const std::size_t desired_width = getImageWidth(); const std::size_t desired_height = getImageHeight(); status.data.resize(desired_width * desired_height); diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h index b8a7c75..ead3e0f 100644 --- a/src/input_common/drivers/camera.h +++ b/src/input_common/drivers/camera.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "input_common/input_engine.h" namespace InputCommon { @@ -15,7 +17,7 @@ class Camera final : public InputEngine { public: explicit Camera(std::string input_engine_); - void SetCameraData(std::size_t width, std::size_t height, std::vector data); + void SetCameraData(std::size_t width, std::size_t height, std::span data); std::size_t getImageWidth() const; std::size_t getImageHeight() const; @@ -23,6 +25,7 @@ public: Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, Common::Input::CameraFormat camera_format) override; +private: Common::Input::CameraStatus status{}; }; diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index 27a0ffb..826fa21 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp @@ -90,7 +90,7 @@ GCAdapter::~GCAdapter() { void GCAdapter::AdapterInputThread(std::stop_token stop_token) { LOG_DEBUG(Input, "Input thread started"); - Common::SetCurrentThreadName("yuzu:input:GCAdapter"); + Common::SetCurrentThreadName("GCAdapter"); s32 payload_size{}; AdapterPayload adapter_payload{}; @@ -214,7 +214,7 @@ void GCAdapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_ } void GCAdapter::AdapterScanThread(std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:input:ScanGCAdapter"); + Common::SetCurrentThreadName("ScanGCAdapter"); usb_adapter_handle = nullptr; pads = {}; while (!stop_token.stop_requested() && !Setup()) { @@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) { return true; } -Common::Input::VibrationError GCAdapter::SetRumble( +Common::Input::VibrationError GCAdapter::SetVibration( const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; const auto processed_amplitude = @@ -338,6 +338,10 @@ Common::Input::VibrationError GCAdapter::SetRumble( return Common::Input::VibrationError::None; } +bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { + return rumble_enabled; +} + void GCAdapter::UpdateVibrations() { // Use 8 states to keep the switching between on/off fast enough for // a human to feel different vibration strenght diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h index 8682da8..b5270fd 100644 --- a/src/input_common/drivers/gc_adapter.h +++ b/src/input_common/drivers/gc_adapter.h @@ -5,10 +5,10 @@ #include #include -#include #include #include +#include "common/polyfill_thread.h" #include "input_common/input_engine.h" struct libusb_context; @@ -25,9 +25,11 @@ public: explicit GCAdapter(std::string input_engine_); ~GCAdapter() override; - Common::Input::VibrationError SetRumble( + Common::Input::VibrationError SetVibration( const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; + bool IsVibrationEnabled(const PadIdentifier& identifier) override; + /// Used for automapping features std::vector GetInputDevices() const override; ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp index 4909fa8..faf9cbd 100644 --- a/src/input_common/drivers/mouse.cpp +++ b/src/input_common/drivers/mouse.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include @@ -37,7 +36,7 @@ Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) } void Mouse::UpdateThread(std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:input:Mouse"); + Common::SetCurrentThreadName("Mouse"); constexpr int update_time = 10; while (!stop_token.stop_requested()) { if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h index 286ce1c..72073cc 100644 --- a/src/input_common/drivers/mouse.h +++ b/src/input_common/drivers/mouse.h @@ -3,9 +3,9 @@ #pragma once -#include #include +#include "common/polyfill_thread.h" #include "common/vector_math.h" #include "input_common/input_engine.h" diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 5cc1ccb..4818bb7 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -16,6 +16,8 @@ Common::UUID GetGUID(SDL_Joystick* joystick) { const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); std::array data{}; std::memcpy(data.data(), guid.data, sizeof(data)); + // Clear controller name crc + std::memset(data.data() + 2, 0, sizeof(u16)); return Common::UUID{data}; } } // Anonymous namespace @@ -40,8 +42,8 @@ public: void EnableMotion() { if (sdl_controller) { SDL_GameController* controller = sdl_controller.get(); - has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL); - has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO); + has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE; + has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE; if (has_accel) { SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); } @@ -114,6 +116,20 @@ public: } return false; } + + void EnableVibration(bool is_enabled) { + has_vibration = is_enabled; + is_vibration_tested = true; + } + + bool HasVibration() const { + return has_vibration; + } + + bool IsVibrationTested() const { + return is_vibration_tested; + } + /** * The Pad identifier of the joystick */ @@ -236,6 +252,8 @@ private: u64 last_motion_update{}; bool has_gyro{false}; bool has_accel{false}; + bool has_vibration{false}; + bool is_vibration_tested{false}; BasicMotion motion; }; @@ -345,6 +363,12 @@ void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) { } } +void SDLDriver::PumpEvents() const { + if (initialized) { + SDL_PumpEvents(); + } +} + void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { switch (event.type) { case SDL_JOYBUTTONUP: { @@ -435,16 +459,8 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en initialized = true; if (start_thread) { - poll_thread = std::thread([this] { - Common::SetCurrentThreadName("yuzu:input:SDL"); - using namespace std::chrono_literals; - while (initialized) { - SDL_PumpEvents(); - std::this_thread::sleep_for(1ms); - } - }); vibration_thread = std::thread([this] { - Common::SetCurrentThreadName("yuzu:input:SDL_Vibration"); + Common::SetCurrentThreadName("SDL_Vibration"); using namespace std::chrono_literals; while (initialized) { SendVibrations(); @@ -465,7 +481,6 @@ SDLDriver::~SDLDriver() { initialized = false; if (start_thread) { - poll_thread.join(); vibration_thread.join(); SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); } @@ -517,7 +532,7 @@ std::vector SDLDriver::GetInputDevices() const { return devices; } -Common::Input::VibrationError SDLDriver::SetRumble( +Common::Input::VibrationError SDLDriver::SetVibration( const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { const auto joystick = GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast(identifier.port)); @@ -546,13 +561,6 @@ Common::Input::VibrationError SDLDriver::SetRumble( .type = Common::Input::VibrationAmplificationType::Exponential, }; - if (vibration.type == Common::Input::VibrationAmplificationType::Test) { - if (!joystick->RumblePlay(new_vibration)) { - return Common::Input::VibrationError::Unknown; - } - return Common::Input::VibrationError::None; - } - vibration_queue.Push(VibrationRequest{ .identifier = identifier, .vibration = new_vibration, @@ -561,6 +569,45 @@ Common::Input::VibrationError SDLDriver::SetRumble( return Common::Input::VibrationError::None; } +bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { + const auto joystick = + GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast(identifier.port)); + + constexpr Common::Input::VibrationStatus test_vibration{ + .low_amplitude = 1, + .low_frequency = 160.0f, + .high_amplitude = 1, + .high_frequency = 320.0f, + .type = Common::Input::VibrationAmplificationType::Exponential, + }; + + constexpr Common::Input::VibrationStatus zero_vibration{ + .low_amplitude = 0, + .low_frequency = 160.0f, + .high_amplitude = 0, + .high_frequency = 320.0f, + .type = Common::Input::VibrationAmplificationType::Exponential, + }; + + if (joystick->IsVibrationTested()) { + return joystick->HasVibration(); + } + + // First vibration might fail + joystick->RumblePlay(test_vibration); + + // Wait for about 15ms to ensure the controller is ready for the stop command + std::this_thread::sleep_for(std::chrono::milliseconds(15)); + + if (!joystick->RumblePlay(zero_vibration)) { + joystick->EnableVibration(false); + return false; + } + + joystick->EnableVibration(true); + return true; +} + void SDLDriver::SendVibrations() { while (!vibration_queue.Empty()) { VibrationRequest request; diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index fc3a445..366bcc4 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h @@ -36,6 +36,8 @@ public: /// Unregisters SDL device factories and shut them down. ~SDLDriver() override; + void PumpEvents() const; + /// Handle SDL_Events for joysticks from SDL_PollEvent void HandleGameControllerEvent(const SDL_Event& event); @@ -61,9 +63,11 @@ public: bool IsStickInverted(const Common::ParamPackage& params) override; - Common::Input::VibrationError SetRumble( + Common::Input::VibrationError SetVibration( const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; + bool IsVibrationEnabled(const PadIdentifier& identifier) override; + private: struct VibrationRequest { PadIdentifier identifier; @@ -126,7 +130,6 @@ private: bool start_thread = false; std::atomic initialized = false; - std::thread poll_thread; std::thread vibration_thread; }; } // namespace InputCommon diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp index 21c6ed4..f3ade90 100644 --- a/src/input_common/drivers/tas_input.cpp +++ b/src/input_common/drivers/tas_input.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include "common/fs/file.h" diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp new file mode 100644 index 0000000..63ffaca --- /dev/null +++ b/src/input_common/drivers/virtual_amiibo.cpp @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "input_common/drivers/virtual_amiibo.h" + +namespace InputCommon { +constexpr PadIdentifier identifier = { + .guid = Common::UUID{}, + .port = 0, + .pad = 0, +}; + +VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} + +VirtualAmiibo::~VirtualAmiibo() = default; + +Common::Input::PollingError VirtualAmiibo::SetPollingMode( + [[maybe_unused]] const PadIdentifier& identifier_, + const Common::Input::PollingMode polling_mode_) { + polling_mode = polling_mode_; + + if (polling_mode == Common::Input::PollingMode::NFC) { + if (state == State::Initialized) { + state = State::WaitingForAmiibo; + } + } else { + if (state == State::AmiiboIsOpen) { + CloseAmiibo(); + } + } + + return Common::Input::PollingError::None; +} + +Common::Input::NfcState VirtualAmiibo::SupportsNfc( + [[maybe_unused]] const PadIdentifier& identifier_) const { + return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::WriteNfcData( + [[maybe_unused]] const PadIdentifier& identifier_, const std::vector& data) { + const Common::FS::IOFile nfc_file{file_path, Common::FS::FileAccessMode::ReadWrite, + Common::FS::FileType::BinaryFile}; + + if (!nfc_file.IsOpen()) { + LOG_ERROR(Core, "Amiibo is already on use"); + return Common::Input::NfcState::WriteFailed; + } + + if (!nfc_file.Write(data)) { + LOG_ERROR(Service_NFP, "Error writting to file"); + return Common::Input::NfcState::WriteFailed; + } + + nfc_data = data; + + return Common::Input::NfcState::Success; +} + +VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { + return state; +} + +VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { + const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; + + if (state != State::WaitingForAmiibo) { + return Info::WrongDeviceState; + } + + if (!nfc_file.IsOpen()) { + return Info::UnableToLoad; + } + + switch (nfc_file.GetSize()) { + case AmiiboSize: + case AmiiboSizeWithoutPassword: + nfc_data.resize(AmiiboSize); + if (nfc_file.Read(nfc_data) < AmiiboSizeWithoutPassword) { + return Info::NotAnAmiibo; + } + break; + case MifareSize: + nfc_data.resize(MifareSize); + if (nfc_file.Read(nfc_data) < MifareSize) { + return Info::NotAnAmiibo; + } + break; + default: + return Info::NotAnAmiibo; + } + + file_path = filename; + state = State::AmiiboIsOpen; + SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); + return Info::Success; +} + +VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { + if (state == State::AmiiboIsOpen) { + SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); + return Info::Success; + } + + return LoadAmiibo(file_path); +} + +VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { + state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo + : State::Initialized; + SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); + return Info::Success; +} + +std::string VirtualAmiibo::GetLastFilePath() const { + return file_path; +} + +} // namespace InputCommon diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h new file mode 100644 index 0000000..0f9dad3 --- /dev/null +++ b/src/input_common/drivers/virtual_amiibo.h @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" +#include "input_common/input_engine.h" + +namespace Common::FS { +class IOFile; +} + +namespace InputCommon { + +class VirtualAmiibo final : public InputEngine { +public: + enum class State { + Initialized, + WaitingForAmiibo, + AmiiboIsOpen, + }; + + enum class Info { + Success, + UnableToLoad, + NotAnAmiibo, + WrongDeviceState, + Unknown, + }; + + explicit VirtualAmiibo(std::string input_engine_); + ~VirtualAmiibo() override; + + // Sets polling mode to a controller + Common::Input::PollingError SetPollingMode( + const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; + + Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; + + Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, + const std::vector& data) override; + + State GetCurrentState() const; + + Info LoadAmiibo(const std::string& amiibo_file); + Info ReloadAmiibo(); + Info CloseAmiibo(); + + std::string GetLastFilePath() const; + +private: + static constexpr std::size_t AmiiboSize = 0x21C; + static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8; + static constexpr std::size_t MifareSize = 0x400; + + std::string file_path{}; + State state{State::Initialized}; + std::vector nfc_data; + Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive}; +}; +} // namespace InputCommon diff --git a/src/input_common/drivers/virtual_gamepad.cpp b/src/input_common/drivers/virtual_gamepad.cpp new file mode 100644 index 0000000..7db945a --- /dev/null +++ b/src/input_common/drivers/virtual_gamepad.cpp @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "input_common/drivers/virtual_gamepad.h" + +namespace InputCommon { +constexpr std::size_t PlayerIndexCount = 10; + +VirtualGamepad::VirtualGamepad(std::string input_engine_) : InputEngine(std::move(input_engine_)) { + for (std::size_t i = 0; i < PlayerIndexCount; i++) { + PreSetController(GetIdentifier(i)); + } +} + +void VirtualGamepad::SetButtonState(std::size_t player_index, int button_id, bool value) { + if (player_index > PlayerIndexCount) { + return; + } + const auto identifier = GetIdentifier(player_index); + SetButton(identifier, button_id, value); +} + +void VirtualGamepad::SetButtonState(std::size_t player_index, VirtualButton button_id, bool value) { + SetButtonState(player_index, static_cast(button_id), value); +} + +void VirtualGamepad::SetStickPosition(std::size_t player_index, int axis_id, float x_value, + float y_value) { + if (player_index > PlayerIndexCount) { + return; + } + const auto identifier = GetIdentifier(player_index); + SetAxis(identifier, axis_id * 2, x_value); + SetAxis(identifier, (axis_id * 2) + 1, y_value); +} + +void VirtualGamepad::SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value, + float y_value) { + SetStickPosition(player_index, static_cast(axis_id), x_value, y_value); +} + +void VirtualGamepad::ResetControllers() { + for (std::size_t i = 0; i < PlayerIndexCount; i++) { + SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f); + SetStickPosition(i, VirtualStick::Right, 0.0f, 0.0f); + + SetButtonState(i, VirtualButton::ButtonA, false); + SetButtonState(i, VirtualButton::ButtonB, false); + SetButtonState(i, VirtualButton::ButtonX, false); + SetButtonState(i, VirtualButton::ButtonY, false); + SetButtonState(i, VirtualButton::StickL, false); + SetButtonState(i, VirtualButton::StickR, false); + SetButtonState(i, VirtualButton::TriggerL, false); + SetButtonState(i, VirtualButton::TriggerR, false); + SetButtonState(i, VirtualButton::TriggerZL, false); + SetButtonState(i, VirtualButton::TriggerZR, false); + SetButtonState(i, VirtualButton::ButtonPlus, false); + SetButtonState(i, VirtualButton::ButtonMinus, false); + SetButtonState(i, VirtualButton::ButtonLeft, false); + SetButtonState(i, VirtualButton::ButtonUp, false); + SetButtonState(i, VirtualButton::ButtonRight, false); + SetButtonState(i, VirtualButton::ButtonDown, false); + SetButtonState(i, VirtualButton::ButtonSL, false); + SetButtonState(i, VirtualButton::ButtonSR, false); + SetButtonState(i, VirtualButton::ButtonHome, false); + SetButtonState(i, VirtualButton::ButtonCapture, false); + } +} + +PadIdentifier VirtualGamepad::GetIdentifier(std::size_t player_index) const { + return { + .guid = Common::UUID{}, + .port = player_index, + .pad = 0, + }; +} + +} // namespace InputCommon diff --git a/src/input_common/drivers/virtual_gamepad.h b/src/input_common/drivers/virtual_gamepad.h new file mode 100644 index 0000000..3df91cc --- /dev/null +++ b/src/input_common/drivers/virtual_gamepad.h @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "input_common/input_engine.h" + +namespace InputCommon { + +/** + * A virtual controller that is always assigned to the game input + */ +class VirtualGamepad final : public InputEngine { +public: + enum class VirtualButton { + ButtonA, + ButtonB, + ButtonX, + ButtonY, + StickL, + StickR, + TriggerL, + TriggerR, + TriggerZL, + TriggerZR, + ButtonPlus, + ButtonMinus, + ButtonLeft, + ButtonUp, + ButtonRight, + ButtonDown, + ButtonSL, + ButtonSR, + ButtonHome, + ButtonCapture, + }; + + enum class VirtualStick { + Left = 0, + Right = 1, + }; + + explicit VirtualGamepad(std::string input_engine_); + + /** + * Sets the status of all buttons bound with the key to pressed + * @param player_index the player number that will take this action + * @param button_id the id of the button + * @param value indicates if the button is pressed or not + */ + void SetButtonState(std::size_t player_index, int button_id, bool value); + void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value); + + /** + * Sets the status of all buttons bound with the key to released + * @param player_index the player number that will take this action + * @param axis_id the id of the axis to move + * @param x_value the position of the stick in the x axis + * @param y_value the position of the stick in the y axis + */ + void SetStickPosition(std::size_t player_index, int axis_id, float x_value, float y_value); + void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value, + float y_value); + + /// Restores all inputs into the neutral position + void ResetControllers(); + +private: + /// Returns the correct identifier corresponding to the player index + PadIdentifier GetIdentifier(std::size_t player_index) const; +}; + +} // namespace InputCommon diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp index 536d413..82aa6ac 100644 --- a/src/input_common/helpers/stick_from_buttons.cpp +++ b/src/input_common/helpers/stick_from_buttons.cpp @@ -294,6 +294,15 @@ public: } private: + static constexpr Common::Input::AnalogProperties properties{ + .deadzone = 0.0f, + .range = 1.0f, + .threshold = 0.5f, + .offset = 0.0f, + .inverted = false, + .toggle = false, + }; + Button up; Button down; Button left; @@ -311,23 +320,17 @@ private: float last_x_axis_value{}; float last_y_axis_value{}; Common::Input::ButtonStatus modifier_status{}; - const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; std::chrono::time_point last_update; }; std::unique_ptr StickFromButton::Create( const Common::ParamPackage& params) { const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); - auto up = Common::Input::CreateDeviceFromString( - params.Get("up", null_engine)); - auto down = Common::Input::CreateDeviceFromString( - params.Get("down", null_engine)); - auto left = Common::Input::CreateDeviceFromString( - params.Get("left", null_engine)); - auto right = Common::Input::CreateDeviceFromString( - params.Get("right", null_engine)); - auto modifier = Common::Input::CreateDeviceFromString( - params.Get("modifier", null_engine)); + auto up = Common::Input::CreateInputDeviceFromString(params.Get("up", null_engine)); + auto down = Common::Input::CreateInputDeviceFromString(params.Get("down", null_engine)); + auto left = Common::Input::CreateInputDeviceFromString(params.Get("left", null_engine)); + auto right = Common::Input::CreateInputDeviceFromString(params.Get("right", null_engine)); + auto modifier = Common::Input::CreateInputDeviceFromString(params.Get("modifier", null_engine)); auto modifier_scale = params.Get("modifier_scale", 0.5f); auto modifier_angle = params.Get("modifier_angle", 5.5f); return std::make_unique(std::move(up), std::move(down), std::move(left), diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp index da4a3dc..e064b13 100644 --- a/src/input_common/helpers/touch_from_buttons.cpp +++ b/src/input_common/helpers/touch_from_buttons.cpp @@ -10,8 +10,8 @@ namespace InputCommon { class TouchFromButtonDevice final : public Common::Input::InputDevice { public: using Button = std::unique_ptr; - TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_) - : button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) { + TouchFromButtonDevice(Button button_, float x_, float y_) + : button(std::move(button_)), x(x_), y(y_) { last_button_value = false; button->SetCallback({ .on_change = @@ -34,7 +34,6 @@ public: .pressed = button_status, .x = {}, .y = {}, - .id = touch_id, }; status.x.properties = properties; status.y.properties = properties; @@ -60,23 +59,28 @@ public: } private: + static constexpr Common::Input::AnalogProperties properties{ + .deadzone = 0.0f, + .range = 1.0f, + .threshold = 0.5f, + .offset = 0.0f, + .inverted = false, + .toggle = false, + }; + Button button; bool last_button_value; - const int touch_id; const float x; const float y; - const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; }; std::unique_ptr TouchFromButton::Create( const Common::ParamPackage& params) { const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); - auto button = Common::Input::CreateDeviceFromString( - params.Get("button", null_engine)); - const auto touch_id = params.Get("touch_id", 0); + auto button = Common::Input::CreateInputDeviceFromString(params.Get("button", null_engine)); const float x = params.Get("x", 0.0f) / 1280.0f; const float y = params.Get("y", 0.0f) / 720.0f; - return std::make_unique(std::move(button), touch_id, x, y); + return std::make_unique(std::move(button), x, y); } } // namespace InputCommon diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp index 6ede0e4..61cfd09 100644 --- a/src/input_common/input_engine.cpp +++ b/src/input_common/input_engine.cpp @@ -102,6 +102,17 @@ void InputEngine::SetCamera(const PadIdentifier& identifier, TriggerOnCameraChange(identifier, value); } +void InputEngine::SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value) { + { + std::scoped_lock lock{mutex}; + ControllerData& controller = controller_list.at(identifier); + if (!configuring) { + controller.nfc = value; + } + } + TriggerOnNfcChange(identifier, value); +} + bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); @@ -189,6 +200,18 @@ Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifi return controller.camera; } +Common::Input::NfcStatus InputEngine::GetNfc(const PadIdentifier& identifier) const { + std::scoped_lock lock{mutex}; + const auto controller_iter = controller_list.find(identifier); + if (controller_iter == controller_list.cend()) { + LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), + identifier.pad, identifier.port); + return {}; + } + const ControllerData& controller = controller_iter->second; + return controller.nfc; +} + void InputEngine::ResetButtonState() { for (const auto& controller : controller_list) { for (const auto& button : controller.second.buttons) { @@ -355,6 +378,20 @@ void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier, } } +void InputEngine::TriggerOnNfcChange(const PadIdentifier& identifier, + [[maybe_unused]] const Common::Input::NfcStatus& value) { + std::scoped_lock lock{mutex_callback}; + for (const auto& poller_pair : callback_list) { + const InputIdentifier& poller = poller_pair.second; + if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Nfc, 0)) { + continue; + } + if (poller.callback.on_change) { + poller.callback.on_change(); + } + } +} + bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier, const PadIdentifier& identifier, EngineInputType type, int index) const { diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index f6b3c46..6cbcf52 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -42,6 +42,7 @@ enum class EngineInputType { Camera, HatButton, Motion, + Nfc, }; namespace std { @@ -107,12 +108,17 @@ public: [[maybe_unused]] const Common::Input::LedStatus& led_status) {} // Sets rumble to a controller - virtual Common::Input::VibrationError SetRumble( + virtual Common::Input::VibrationError SetVibration( [[maybe_unused]] const PadIdentifier& identifier, [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { return Common::Input::VibrationError::NotSupported; } + // Returns true if device supports vibrations + virtual bool IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { + return false; + } + // Sets polling mode to a controller virtual Common::Input::PollingError SetPollingMode( [[maybe_unused]] const PadIdentifier& identifier, @@ -127,6 +133,18 @@ public: return Common::Input::CameraError::NotSupported; } + // Returns success if nfc is supported + virtual Common::Input::NfcState SupportsNfc( + [[maybe_unused]] const PadIdentifier& identifier) const { + return Common::Input::NfcState::NotSupported; + } + + // Writes data to an nfc tag + virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier, + [[maybe_unused]] const std::vector& data) { + return Common::Input::NfcState::NotSupported; + } + // Returns the engine name [[nodiscard]] const std::string& GetEngineName() const; @@ -183,6 +201,7 @@ public: Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; + Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const; int SetCallback(InputIdentifier input_identifier); void SetMappingCallback(MappingCallback callback); @@ -195,6 +214,7 @@ protected: void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); + void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { return "Unknown"; @@ -208,6 +228,7 @@ private: std::unordered_map motions; Common::Input::BatteryLevel battery{}; Common::Input::CameraStatus camera{}; + Common::Input::NfcStatus nfc{}; }; void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); @@ -218,6 +239,7 @@ private: const BasicMotion& value); void TriggerOnCameraChange(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); + void TriggerOnNfcChange(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); bool IsInputIdentifierEqual(const InputIdentifier& input_identifier, const PadIdentifier& identifier, EngineInputType type, diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index ffb9b94..fb8be42 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -229,13 +229,12 @@ private: class InputFromTouch final : public Common::Input::InputDevice { public: - explicit InputFromTouch(PadIdentifier identifier_, int touch_id_, int button_, bool toggle_, - bool inverted_, int axis_x_, int axis_y_, - Common::Input::AnalogProperties properties_x_, + explicit InputFromTouch(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_, + int axis_x_, int axis_y_, Common::Input::AnalogProperties properties_x_, Common::Input::AnalogProperties properties_y_, InputEngine* input_engine_) - : identifier(identifier_), touch_id(touch_id_), button(button_), toggle(toggle_), - inverted(inverted_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_), + : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_), + axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_), properties_y(properties_y_), input_engine(input_engine_) { UpdateCallback engine_callback{[this]() { OnChange(); }}; const InputIdentifier button_input_identifier{ @@ -271,8 +270,7 @@ public: } Common::Input::TouchStatus GetStatus() const { - Common::Input::TouchStatus status; - status.id = touch_id; + Common::Input::TouchStatus status{}; status.pressed = { .value = input_engine->GetButton(identifier, button), .inverted = inverted, @@ -307,7 +305,6 @@ public: private: const PadIdentifier identifier; - const int touch_id; const int button; const bool toggle; const bool inverted; @@ -691,9 +688,56 @@ public: } void OnChange() { + const auto camera_status = GetStatus(); + const Common::Input::CallbackStatus status{ .type = Common::Input::InputType::IrSensor, - .camera_status = GetStatus(), + .camera_status = camera_status.format, + .raw_data = camera_status.data, + }; + + TriggerOnChange(status); + } + +private: + const PadIdentifier identifier; + int callback_key; + InputEngine* input_engine; +}; + +class InputFromNfc final : public Common::Input::InputDevice { +public: + explicit InputFromNfc(PadIdentifier identifier_, InputEngine* input_engine_) + : identifier(identifier_), input_engine(input_engine_) { + UpdateCallback engine_callback{[this]() { OnChange(); }}; + const InputIdentifier input_identifier{ + .identifier = identifier, + .type = EngineInputType::Nfc, + .index = 0, + .callback = engine_callback, + }; + callback_key = input_engine->SetCallback(input_identifier); + } + + ~InputFromNfc() override { + input_engine->DeleteCallback(callback_key); + } + + Common::Input::NfcStatus GetStatus() const { + return input_engine->GetNfc(identifier); + } + + void ForceUpdate() override { + OnChange(); + } + + void OnChange() { + const auto nfc_status = GetStatus(); + + const Common::Input::CallbackStatus status{ + .type = Common::Input::InputType::Nfc, + .nfc_status = nfc_status.state, + .raw_data = nfc_status.data, }; TriggerOnChange(status); @@ -716,7 +760,11 @@ public: Common::Input::VibrationError SetVibration( const Common::Input::VibrationStatus& vibration_status) override { - return input_engine->SetRumble(identifier, vibration_status); + return input_engine->SetVibration(identifier, vibration_status); + } + + bool IsVibrationEnabled() override { + return input_engine->IsVibrationEnabled(identifier); } Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { @@ -727,6 +775,14 @@ public: return input_engine->SetCameraFormat(identifier, camera_format); } + Common::Input::NfcState SupportsNfc() const override { + return input_engine->SupportsNfc(identifier); + } + + Common::Input::NfcState WriteNfcData(const std::vector& data) override { + return input_engine->WriteNfcData(identifier, data); + } + private: const PadIdentifier identifier; InputEngine* input_engine; @@ -742,8 +798,8 @@ std::unique_ptr InputFactory::CreateButtonDevice( const auto button_id = params.Get("button", 0); const auto keyboard_key = params.Get("code", 0); - const auto toggle = params.Get("toggle", false); - const auto inverted = params.Get("inverted", false); + const auto toggle = params.Get("toggle", false) != 0; + const auto inverted = params.Get("inverted", false) != 0; input_engine->PreSetController(identifier); input_engine->PreSetButton(identifier, button_id); input_engine->PreSetButton(identifier, keyboard_key); @@ -765,8 +821,8 @@ std::unique_ptr InputFactory::CreateHatButtonDevice( const auto button_id = params.Get("hat", 0); const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); - const auto toggle = params.Get("toggle", false); - const auto inverted = params.Get("inverted", false); + const auto toggle = params.Get("toggle", false) != 0; + const auto inverted = params.Get("inverted", false) != 0; input_engine->PreSetController(identifier); input_engine->PreSetHatButton(identifier, button_id); @@ -824,7 +880,7 @@ std::unique_ptr InputFactory::CreateAnalogDevice( .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), .inverted = params.Get("invert", "+") == "-", - .toggle = static_cast(params.Get("toggle", false)), + .toggle = params.Get("toggle", false) != 0, }; input_engine->PreSetController(identifier); input_engine->PreSetAxis(identifier, axis); @@ -840,8 +896,8 @@ std::unique_ptr InputFactory::CreateTriggerDevice( }; const auto button = params.Get("button", 0); - const auto toggle = params.Get("toggle", false); - const auto inverted = params.Get("inverted", false); + const auto toggle = params.Get("toggle", false) != 0; + const auto inverted = params.Get("inverted", false) != 0; const auto axis = params.Get("axis", 0); const Common::Input::AnalogProperties properties = { @@ -860,7 +916,6 @@ std::unique_ptr InputFactory::CreateTriggerDevice( std::unique_ptr InputFactory::CreateTouchDevice( const Common::ParamPackage& params) { - const auto touch_id = params.Get("touch_id", 0); const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f); const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f); @@ -871,8 +926,8 @@ std::unique_ptr InputFactory::CreateTouchDevice( }; const auto button = params.Get("button", 0); - const auto toggle = params.Get("toggle", false); - const auto inverted = params.Get("inverted", false); + const auto toggle = params.Get("toggle", false) != 0; + const auto inverted = params.Get("inverted", false) != 0; const auto axis_x = params.Get("axis_x", 0); const Common::Input::AnalogProperties properties_x = { @@ -895,8 +950,8 @@ std::unique_ptr InputFactory::CreateTouchDevice( input_engine->PreSetAxis(identifier, axis_x); input_engine->PreSetAxis(identifier, axis_y); input_engine->PreSetButton(identifier, button); - return std::make_unique(identifier, touch_id, button, toggle, inverted, axis_x, - axis_y, properties_x, properties_y, input_engine.get()); + return std::make_unique(identifier, button, toggle, inverted, axis_x, axis_y, + properties_x, properties_y, input_engine.get()); } std::unique_ptr InputFactory::CreateBatteryDevice( @@ -978,6 +1033,18 @@ std::unique_ptr InputFactory::CreateCameraDevice( return std::make_unique(identifier, input_engine.get()); } +std::unique_ptr InputFactory::CreateNfcDevice( + const Common::ParamPackage& params) { + const PadIdentifier identifier = { + .guid = Common::UUID{params.Get("guid", "")}, + .port = static_cast(params.Get("port", 0)), + .pad = static_cast(params.Get("pad", 0)), + }; + + input_engine->PreSetController(identifier); + return std::make_unique(identifier, input_engine.get()); +} + InputFactory::InputFactory(std::shared_ptr input_engine_) : input_engine(std::move(input_engine_)) {} @@ -989,6 +1056,9 @@ std::unique_ptr InputFactory::Create( if (params.Has("camera")) { return CreateCameraDevice(params); } + if (params.Has("nfc")) { + return CreateNfcDevice(params); + } if (params.Has("button") && params.Has("axis")) { return CreateTriggerDevice(params); } diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h index 4410a84..d7db13c 100644 --- a/src/input_common/input_poller.h +++ b/src/input_common/input_poller.h @@ -222,6 +222,16 @@ private: std::unique_ptr CreateCameraDevice( const Common::ParamPackage& params); + /** + * Creates a nfc device from the parameters given. + * @param params contains parameters for creating the device: + * - "guid": text string for identifying controllers + * - "port": port of the connected device + * - "pad": slot of the connected controller + * @returns a unique input device with the parameters specified + */ + std::unique_ptr CreateNfcDevice(const Common::ParamPackage& params); + std::shared_ptr input_engine; }; } // namespace InputCommon diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 75a57b9..75b856c 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -11,6 +11,8 @@ #include "input_common/drivers/tas_input.h" #include "input_common/drivers/touch_screen.h" #include "input_common/drivers/udp_client.h" +#include "input_common/drivers/virtual_amiibo.h" +#include "input_common/drivers/virtual_gamepad.h" #include "input_common/helpers/stick_from_buttons.h" #include "input_common/helpers/touch_from_buttons.h" #include "input_common/input_engine.h" @@ -32,110 +34,122 @@ struct InputSubsystem::Impl { keyboard->SetMappingCallback(mapping_callback); keyboard_factory = std::make_shared(keyboard); keyboard_output_factory = std::make_shared(keyboard); - Common::Input::RegisterFactory(keyboard->GetEngineName(), - keyboard_factory); - Common::Input::RegisterFactory(keyboard->GetEngineName(), - keyboard_output_factory); + Common::Input::RegisterInputFactory(keyboard->GetEngineName(), keyboard_factory); + Common::Input::RegisterOutputFactory(keyboard->GetEngineName(), keyboard_output_factory); mouse = std::make_shared("mouse"); mouse->SetMappingCallback(mapping_callback); mouse_factory = std::make_shared(mouse); mouse_output_factory = std::make_shared(mouse); - Common::Input::RegisterFactory(mouse->GetEngineName(), - mouse_factory); - Common::Input::RegisterFactory(mouse->GetEngineName(), - mouse_output_factory); + Common::Input::RegisterInputFactory(mouse->GetEngineName(), mouse_factory); + Common::Input::RegisterOutputFactory(mouse->GetEngineName(), mouse_output_factory); touch_screen = std::make_shared("touch"); touch_screen_factory = std::make_shared(touch_screen); - Common::Input::RegisterFactory(touch_screen->GetEngineName(), - touch_screen_factory); + Common::Input::RegisterInputFactory(touch_screen->GetEngineName(), touch_screen_factory); gcadapter = std::make_shared("gcpad"); gcadapter->SetMappingCallback(mapping_callback); gcadapter_input_factory = std::make_shared(gcadapter); gcadapter_output_factory = std::make_shared(gcadapter); - Common::Input::RegisterFactory(gcadapter->GetEngineName(), - gcadapter_input_factory); - Common::Input::RegisterFactory(gcadapter->GetEngineName(), - gcadapter_output_factory); + Common::Input::RegisterInputFactory(gcadapter->GetEngineName(), gcadapter_input_factory); + Common::Input::RegisterOutputFactory(gcadapter->GetEngineName(), gcadapter_output_factory); udp_client = std::make_shared("cemuhookudp"); udp_client->SetMappingCallback(mapping_callback); udp_client_input_factory = std::make_shared(udp_client); udp_client_output_factory = std::make_shared(udp_client); - Common::Input::RegisterFactory(udp_client->GetEngineName(), - udp_client_input_factory); - Common::Input::RegisterFactory(udp_client->GetEngineName(), - udp_client_output_factory); + Common::Input::RegisterInputFactory(udp_client->GetEngineName(), udp_client_input_factory); + Common::Input::RegisterOutputFactory(udp_client->GetEngineName(), + udp_client_output_factory); tas_input = std::make_shared("tas"); tas_input->SetMappingCallback(mapping_callback); tas_input_factory = std::make_shared(tas_input); tas_output_factory = std::make_shared(tas_input); - Common::Input::RegisterFactory(tas_input->GetEngineName(), - tas_input_factory); - Common::Input::RegisterFactory(tas_input->GetEngineName(), - tas_output_factory); + Common::Input::RegisterInputFactory(tas_input->GetEngineName(), tas_input_factory); + Common::Input::RegisterOutputFactory(tas_input->GetEngineName(), tas_output_factory); camera = std::make_shared("camera"); camera->SetMappingCallback(mapping_callback); camera_input_factory = std::make_shared(camera); camera_output_factory = std::make_shared(camera); - Common::Input::RegisterFactory(camera->GetEngineName(), - camera_input_factory); - Common::Input::RegisterFactory(camera->GetEngineName(), - camera_output_factory); + Common::Input::RegisterInputFactory(camera->GetEngineName(), camera_input_factory); + Common::Input::RegisterOutputFactory(camera->GetEngineName(), camera_output_factory); + + virtual_amiibo = std::make_shared("virtual_amiibo"); + virtual_amiibo->SetMappingCallback(mapping_callback); + virtual_amiibo_input_factory = std::make_shared(virtual_amiibo); + virtual_amiibo_output_factory = std::make_shared(virtual_amiibo); + Common::Input::RegisterInputFactory(virtual_amiibo->GetEngineName(), + virtual_amiibo_input_factory); + Common::Input::RegisterOutputFactory(virtual_amiibo->GetEngineName(), + virtual_amiibo_output_factory); + + virtual_gamepad = std::make_shared("virtual_gamepad"); + virtual_gamepad->SetMappingCallback(mapping_callback); + virtual_gamepad_input_factory = std::make_shared(virtual_gamepad); + Common::Input::RegisterInputFactory(virtual_gamepad->GetEngineName(), + virtual_gamepad_input_factory); #ifdef HAVE_SDL2 sdl = std::make_shared("sdl"); sdl->SetMappingCallback(mapping_callback); sdl_input_factory = std::make_shared(sdl); sdl_output_factory = std::make_shared(sdl); - Common::Input::RegisterFactory(sdl->GetEngineName(), - sdl_input_factory); - Common::Input::RegisterFactory(sdl->GetEngineName(), - sdl_output_factory); + Common::Input::RegisterInputFactory(sdl->GetEngineName(), sdl_input_factory); + Common::Input::RegisterOutputFactory(sdl->GetEngineName(), sdl_output_factory); #endif - Common::Input::RegisterFactory( - "touch_from_button", std::make_shared()); - Common::Input::RegisterFactory( - "analog_from_button", std::make_shared()); + Common::Input::RegisterInputFactory("touch_from_button", + std::make_shared()); + Common::Input::RegisterInputFactory("analog_from_button", + std::make_shared()); } void Shutdown() { - Common::Input::UnregisterFactory(keyboard->GetEngineName()); - Common::Input::UnregisterFactory(keyboard->GetEngineName()); + Common::Input::UnregisterInputFactory(keyboard->GetEngineName()); + Common::Input::UnregisterOutputFactory(keyboard->GetEngineName()); keyboard.reset(); - Common::Input::UnregisterFactory(mouse->GetEngineName()); - Common::Input::UnregisterFactory(mouse->GetEngineName()); + Common::Input::UnregisterInputFactory(mouse->GetEngineName()); + Common::Input::UnregisterOutputFactory(mouse->GetEngineName()); mouse.reset(); - Common::Input::UnregisterFactory(touch_screen->GetEngineName()); + Common::Input::UnregisterInputFactory(touch_screen->GetEngineName()); touch_screen.reset(); - Common::Input::UnregisterFactory(gcadapter->GetEngineName()); - Common::Input::UnregisterFactory(gcadapter->GetEngineName()); + Common::Input::UnregisterInputFactory(gcadapter->GetEngineName()); + Common::Input::UnregisterOutputFactory(gcadapter->GetEngineName()); gcadapter.reset(); - Common::Input::UnregisterFactory(udp_client->GetEngineName()); - Common::Input::UnregisterFactory(udp_client->GetEngineName()); + Common::Input::UnregisterInputFactory(udp_client->GetEngineName()); + Common::Input::UnregisterOutputFactory(udp_client->GetEngineName()); udp_client.reset(); - Common::Input::UnregisterFactory(tas_input->GetEngineName()); - Common::Input::UnregisterFactory(tas_input->GetEngineName()); + Common::Input::UnregisterInputFactory(tas_input->GetEngineName()); + Common::Input::UnregisterOutputFactory(tas_input->GetEngineName()); tas_input.reset(); + Common::Input::UnregisterInputFactory(camera->GetEngineName()); + Common::Input::UnregisterOutputFactory(camera->GetEngineName()); + camera.reset(); + + Common::Input::UnregisterInputFactory(virtual_amiibo->GetEngineName()); + Common::Input::UnregisterOutputFactory(virtual_amiibo->GetEngineName()); + virtual_amiibo.reset(); + + Common::Input::UnregisterInputFactory(virtual_gamepad->GetEngineName()); + virtual_gamepad.reset(); + #ifdef HAVE_SDL2 - Common::Input::UnregisterFactory(sdl->GetEngineName()); - Common::Input::UnregisterFactory(sdl->GetEngineName()); + Common::Input::UnregisterInputFactory(sdl->GetEngineName()); + Common::Input::UnregisterOutputFactory(sdl->GetEngineName()); sdl.reset(); #endif - Common::Input::UnregisterFactory("touch_from_button"); - Common::Input::UnregisterFactory("analog_from_button"); + Common::Input::UnregisterInputFactory("touch_from_button"); + Common::Input::UnregisterInputFactory("analog_from_button"); } [[nodiscard]] std::vector GetInputDevices() const { @@ -286,6 +300,9 @@ struct InputSubsystem::Impl { if (engine == tas_input->GetEngineName()) { return true; } + if (engine == virtual_gamepad->GetEngineName()) { + return true; + } #ifdef HAVE_SDL2 if (engine == sdl->GetEngineName()) { return true; @@ -314,6 +331,12 @@ struct InputSubsystem::Impl { #endif } + void PumpEvents() const { +#ifdef HAVE_SDL2 + sdl->PumpEvents(); +#endif + } + void RegisterInput(const MappingData& data) { mapping_factory->RegisterInput(data); } @@ -327,6 +350,8 @@ struct InputSubsystem::Impl { std::shared_ptr tas_input; std::shared_ptr udp_client; std::shared_ptr camera; + std::shared_ptr virtual_amiibo; + std::shared_ptr virtual_gamepad; std::shared_ptr keyboard_factory; std::shared_ptr mouse_factory; @@ -335,6 +360,8 @@ struct InputSubsystem::Impl { std::shared_ptr udp_client_input_factory; std::shared_ptr tas_input_factory; std::shared_ptr camera_input_factory; + std::shared_ptr virtual_amiibo_input_factory; + std::shared_ptr virtual_gamepad_input_factory; std::shared_ptr keyboard_output_factory; std::shared_ptr mouse_output_factory; @@ -342,6 +369,7 @@ struct InputSubsystem::Impl { std::shared_ptr udp_client_output_factory; std::shared_ptr tas_output_factory; std::shared_ptr camera_output_factory; + std::shared_ptr virtual_amiibo_output_factory; #ifdef HAVE_SDL2 std::shared_ptr sdl; @@ -402,6 +430,22 @@ const Camera* InputSubsystem::GetCamera() const { return impl->camera.get(); } +VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() { + return impl->virtual_amiibo.get(); +} + +const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const { + return impl->virtual_amiibo.get(); +} + +VirtualGamepad* InputSubsystem::GetVirtualGamepad() { + return impl->virtual_gamepad.get(); +} + +const VirtualGamepad* InputSubsystem::GetVirtualGamepad() const { + return impl->virtual_gamepad.get(); +} + std::vector InputSubsystem::GetInputDevices() const { return impl->GetInputDevices(); } @@ -451,6 +495,10 @@ void InputSubsystem::StopMapping() const { impl->mapping_factory->StopMapping(); } +void InputSubsystem::PumpEvents() const { + impl->PumpEvents(); +} + std::string GenerateKeyboardParam(int key_code) { Common::ParamPackage param; param.Set("engine", "keyboard"); diff --git a/src/input_common/main.h b/src/input_common/main.h index 9a969e7..1207d78 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -33,6 +33,8 @@ class Camera; class Keyboard; class Mouse; class TouchScreen; +class VirtualAmiibo; +class VirtualGamepad; struct MappingData; } // namespace InputCommon @@ -101,6 +103,18 @@ public: /// Retrieves the underlying camera input device. [[nodiscard]] const Camera* GetCamera() const; + /// Retrieves the underlying virtual amiibo input device. + [[nodiscard]] VirtualAmiibo* GetVirtualAmiibo(); + + /// Retrieves the underlying virtual amiibo input device. + [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const; + + /// Retrieves the underlying virtual gamepad input device. + [[nodiscard]] VirtualGamepad* GetVirtualGamepad(); + + /// Retrieves the underlying virtual gamepad input device. + [[nodiscard]] const VirtualGamepad* GetVirtualGamepad() const; + /** * Returns all available input devices that this Factory can create a new device with. * Each returned ParamPackage should have a `display` field used for display, a `engine` field @@ -140,6 +154,9 @@ public: /// Stop polling from all backends. void StopMapping() const; + /// Signals SDL driver for new input events + void PumpEvents() const; + private: struct Impl; std::unique_ptr impl; diff --git a/src/input_common/precompiled_headers.h b/src/input_common/precompiled_headers.h new file mode 100644 index 0000000..aabae73 --- /dev/null +++ b/src/input_common/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 6f8ca4b..1ab52da 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(network STATIC network.h packet.cpp packet.h + precompiled_headers.h room.cpp room.h room_member.cpp @@ -18,8 +19,12 @@ add_library(network STATIC create_target_directory_groups(network) -target_link_libraries(network PRIVATE common enet Boost::boost) +target_link_libraries(network PRIVATE common enet::enet Boost::boost) if (ENABLE_WEB_SERVICE) target_compile_definitions(network PRIVATE -DENABLE_WEB_SERVICE) target_link_libraries(network PRIVATE web_service) endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(network PRIVATE precompiled_headers.h) +endif() diff --git a/src/network/network.cpp b/src/network/network.cpp index 0841e41..6652a18 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -15,7 +15,7 @@ RoomNetwork::RoomNetwork() { bool RoomNetwork::Init() { if (enet_initialize() != 0) { - LOG_ERROR(Network, "Error initalizing ENet"); + LOG_ERROR(Network, "Error initializing ENet"); return false; } m_room = std::make_shared(); diff --git a/src/network/precompiled_headers.h b/src/network/precompiled_headers.h new file mode 100644 index 0000000..aabae73 --- /dev/null +++ b/src/network/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index af8e51f..525b236 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -221,14 +221,17 @@ add_library(shader_recompiler STATIC ir_opt/dual_vertex_pass.cpp ir_opt/global_memory_to_storage_buffer_pass.cpp ir_opt/identity_removal_pass.cpp + ir_opt/layer_pass.cpp ir_opt/lower_fp16_to_fp32.cpp ir_opt/lower_int64_to_int32.cpp ir_opt/passes.h + ir_opt/position_pass.cpp ir_opt/rescaling_pass.cpp ir_opt/ssa_rewrite_pass.cpp ir_opt/texture_pass.cpp ir_opt/verification_pass.cpp object_pool.h + precompiled_headers.h profile.h program_header.h runtime_info.h @@ -241,29 +244,24 @@ target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit) if (MSVC) target_compile_options(shader_recompiler PRIVATE /W4 - /WX - /we4018 # 'expression' : signed/unsigned mismatch - /we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) - /we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch + + /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data - /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data - /we4305 # 'context' : truncation from 'type1' to 'type2' /we4800 # Implicit conversion from 'type' to bool. Possible information loss - /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. ) else() target_compile_options(shader_recompiler PRIVATE - -Werror -Werror=conversion - -Werror=ignored-qualifiers - $<$:-Werror=unused-but-set-parameter> - $<$:-Werror=unused-but-set-variable> - -Werror=unused-variable # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. # And this in turns limits the size of a std::array. $<$:-fbracket-depth=1024> + $<$:-fbracket-depth=1024> ) endif() create_target_directory_groups(shader_recompiler) + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(shader_recompiler PRIVATE precompiled_headers.h) +endif() diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp index 97a6b38..0cb1e19 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp @@ -175,7 +175,7 @@ bool IsReference(IR::Inst& inst) { } void PrecolorInst(IR::Inst& phi) { - // Insert phi moves before references to avoid overwritting other phis + // Insert phi moves before references to avoid overwriting other phis const size_t num_args{phi.NumArgs()}; for (size_t i = 0; i < num_args; ++i) { IR::Block& phi_block{*phi.PhiBlock(i)}; @@ -320,6 +320,7 @@ void SetupOptions(const IR::Program& program, const Profile& profile, } if (stage == Stage::Fragment) { header += "OPTION ARB_draw_buffers;"; + header += "OPTION ARB_fragment_layer_viewport;"; } } @@ -450,6 +451,9 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I if (program.info.uses_rescaling_uniform) { header += "PARAM scaling[1]={program.local[0..0]};"; } + if (program.info.uses_render_area) { + header += "PARAM render_area[1]={program.local[1..1]};"; + } header += "TEMP "; for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) { header += fmt::format("R{},", index); diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp index 2fc2a0a..5bfdecc 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp @@ -43,6 +43,10 @@ void EmitBitCastU64F64(EmitContext&, IR::Inst& inst, const IR::Value& value) { Alias(inst, value); } +void EmitBitCastS32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) { + Alias(inst, value); +} + void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) { Alias(inst, value); } diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp index b5c08d6..f0bd84a 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp @@ -13,9 +13,6 @@ namespace Shader::Backend::GLASM { namespace { void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset, std::string_view size) { - if (!binding.IsImmediate()) { - throw NotImplementedException("Indirect constant buffer loading"); - } const Register ret{ctx.reg_alloc.Define(inst)}; if (offset.type == Type::U32) { // Avoid reading arrays out of bounds, matching hardware's behavior @@ -24,7 +21,27 @@ void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU return; } } - ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset); + + if (binding.IsImmediate()) { + ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset); + return; + } + + const ScalarU32 idx{ctx.reg_alloc.Consume(binding)}; + for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) { + ctx.Add("SEQ.S.CC RC.x,{},{};" + "IF NE.x;" + "LDC.{} {},c{}[{}];", + idx, i, size, ret, i, offset); + + if (i != Info::MAX_INDIRECT_CBUFS - 1) { + ctx.Add("ELSE;"); + } + } + + for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) { + ctx.Add("ENDIF;"); + } } bool IsInputArray(Stage stage) { @@ -87,6 +104,9 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal case IR::Attribute::PrimitiveId: ctx.Add("MOV.F {}.x,primitive.id;", inst); break; + case IR::Attribute::Layer: + ctx.Add("MOV.F {}.x,fragment.layer;", inst); + break; case IR::Attribute::PositionX: case IR::Attribute::PositionY: case IR::Attribute::PositionZ: @@ -362,6 +382,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) { ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst); } +void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) { + switch (ctx.stage) { + case Stage::TessellationControl: + case Stage::TessellationEval: + ctx.Add("SHL.U {}.x,primitive.vertexcount,16;", inst); + break; + default: + LOG_WARNING(Shader, "(STUBBED) called"); + ctx.Add("MOV.S {}.x,0x00ff0000;", inst); + } +} + void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst); } @@ -379,6 +411,10 @@ void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) { ctx.Add("MOV.F {}.x,scaling[0].z;", inst); } +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) { + ctx.Add("MOV.F {},render_area[0];", inst); +} + void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) { ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset); } diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h index 8b0ac30..eaaf9ba 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h +++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h @@ -69,10 +69,12 @@ void EmitSetOFlag(EmitContext& ctx); void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst); void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst); void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); +void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst); void EmitSampleId(EmitContext& ctx, IR::Inst& inst); void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); void EmitYDirection(EmitContext& ctx, IR::Inst& inst); void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst); void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset); void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value); void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); @@ -195,6 +197,7 @@ void EmitSelectF64(EmitContext& ctx, ScalarS32 cond, Register true_value, Regist void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); +void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp index 7094d8e..1f4ffdd 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp @@ -5,10 +5,6 @@ #include "shader_recompiler/backend/glasm/glasm_emit_context.h" #include "shader_recompiler/frontend/ir/value.h" -#ifdef _MSC_VER -#pragma warning(disable : 4100) -#endif - namespace Shader::Backend::GLASM { #define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__) diff --git a/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp index 89603c1..333a91c 100644 --- a/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp +++ b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp @@ -95,6 +95,10 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile if (info.uses_invocation_id) { Add("ATTRIB primitive_invocation=primitive.invocation;"); } + if (info.uses_invocation_info && + (stage == Stage::TessellationControl || stage == Stage::TessellationEval)) { + Add("ATTRIB primitive_vertexcount = primitive.vertexcount;"); + } if (info.stores_tess_level_outer) { Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};"); } diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp index 76c18e4..e8a4390 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp @@ -101,7 +101,7 @@ bool IsReference(IR::Inst& inst) { } void PrecolorInst(IR::Inst& phi) { - // Insert phi moves before references to avoid overwritting other phis + // Insert phi moves before references to avoid overwriting other phis const size_t num_args{phi.NumArgs()}; for (size_t i = 0; i < num_args; ++i) { IR::Block& phi_block{*phi.PhiBlock(i)}; diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp index 1be4a0f..8e5e6cf 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp @@ -48,6 +48,10 @@ void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) ctx.AddU64("{}=doubleBitsToUint64({});", inst, value); } +void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { + ctx.AddF32("{}=ftoi({});", inst, value); +} + void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) { NotImplemented(); } diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index fad8d1e..39579cf 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp @@ -205,6 +205,9 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, case IR::Attribute::PrimitiveId: ctx.AddF32("{}=itof(gl_PrimitiveID);", inst); break; + case IR::Attribute::Layer: + ctx.AddF32("{}=itof(gl_Layer);", inst); + break; case IR::Attribute::PositionX: case IR::Attribute::PositionY: case IR::Attribute::PositionZ: @@ -399,6 +402,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) { ctx.AddU32("{}=uint(gl_InvocationID);", inst); } +void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) { + switch (ctx.stage) { + case Stage::TessellationControl: + case Stage::TessellationEval: + ctx.AddU32("{}=uint(gl_PatchVerticesIn)<<16;", inst); + break; + default: + LOG_WARNING(Shader, "(STUBBED) called"); + ctx.AddU32("{}=uint(0x00ff0000);", inst); + } +} + void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { ctx.AddU32("{}=uint(gl_SampleID);", inst); } @@ -416,6 +431,10 @@ void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) { ctx.AddF32("{}=scaling.z;", inst); } +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) { + ctx.AddF32x4("{}=render_area;", inst); +} + void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) { ctx.AddU32("{}=lmem[{}];", inst, word_offset); } diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h index 639691b..4151c89 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h +++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h @@ -83,10 +83,12 @@ void EmitSetOFlag(EmitContext& ctx); void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst); void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst); void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); +void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst); void EmitSampleId(EmitContext& ctx, IR::Inst& inst); void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); void EmitYDirection(EmitContext& ctx, IR::Inst& inst); void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); +void EmitRenderArea(EmitContext& ctx, IR::Inst& inst); void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset); void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value); void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); @@ -229,6 +231,7 @@ void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond, void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst); void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); +void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst); void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp index b03a8ba..9f1ed95 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp @@ -7,10 +7,6 @@ #include "shader_recompiler/backend/glsl/glsl_emit_context.h" #include "shader_recompiler/frontend/ir/value.h" -#ifdef _MSC_VER -#pragma warning(disable : 4100) -#endif - namespace Shader::Backend::GLSL { void EmitGetRegister(EmitContext& ctx) { diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index c767a9d..5d01ec0 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -358,6 +358,9 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile if (info.uses_rescaling_uniform) { header += "layout(location=0) uniform vec4 scaling;"; } + if (info.uses_render_area) { + header += "layout(location=1) uniform vec4 render_area;"; + } DefineConstantBuffers(bindings); DefineConstantBufferIndirect(); DefineStorageBuffers(bindings); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 265ac9c..0f86a80 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -402,8 +402,10 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct ctx.AddCapability(spv::Capability::SparseResidency); } if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) { - ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); - ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); + if (profile.supported_spirv < 0x00010600) { + ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); + } + ctx.AddCapability(spv::Capability::DemoteToHelperInvocation); } if (info.stores[IR::Attribute::ViewportIndex]) { ctx.AddCapability(spv::Capability::MultiViewport); @@ -426,12 +428,11 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles) && profile.support_vote) { - ctx.AddExtension("SPV_KHR_shader_ballot"); - ctx.AddCapability(spv::Capability::SubgroupBallotKHR); + ctx.AddCapability(spv::Capability::GroupNonUniformBallot); + ctx.AddCapability(spv::Capability::GroupNonUniformShuffle); if (!profile.warp_size_potentially_larger_than_guest) { // vote ops are only used when not taking the long path - ctx.AddExtension("SPV_KHR_subgroup_vote"); - ctx.AddCapability(spv::Capability::SubgroupVoteKHR); + ctx.AddCapability(spv::Capability::GroupNonUniformVote); } } if (info.uses_int64_bit_atomics && profile.support_int64_atomics) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index 7567b6f..9378814 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h @@ -23,8 +23,12 @@ struct RescalingLayout { alignas(16) std::array rescaling_images; u32 down_factor; }; +struct RenderAreaLayout { + std::array render_area; +}; constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); +constexpr u32 RENDERAREA_LAYOUT_OFFSET = offsetof(RenderAreaLayout, render_area); [[nodiscard]] std::vector EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, Bindings& bindings); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp index c4ca28d..50daacd 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp @@ -18,6 +18,10 @@ void EmitBitCastU64F64(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } +void EmitBitCastS32F32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); +} + void EmitBitCastF16U16(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 2c68aba..73b67f0 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -315,6 +315,8 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { switch (attr) { case IR::Attribute::PrimitiveId: return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id)); + case IR::Attribute::Layer: + return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.layer)); case IR::Attribute::PositionX: case IR::Attribute::PositionY: case IR::Attribute::PositionZ: @@ -353,7 +355,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { case IR::Attribute::TessellationEvaluationPointV: return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U))); - default: throw NotImplementedException("Read attribute {}", attr); } @@ -460,7 +461,7 @@ void EmitSetSampleMask(EmitContext& ctx, Id value) { } void EmitSetFragDepth(EmitContext& ctx, Id value) { - if (!ctx.runtime_info.convert_depth_mode) { + if (!ctx.runtime_info.convert_depth_mode || ctx.profile.support_native_ndc) { ctx.OpStore(ctx.frag_depth, value); return; } @@ -513,6 +514,18 @@ Id EmitInvocationId(EmitContext& ctx) { return ctx.OpLoad(ctx.U32[1], ctx.invocation_id); } +Id EmitInvocationInfo(EmitContext& ctx) { + switch (ctx.stage) { + case Stage::TessellationControl: + case Stage::TessellationEval: + return ctx.OpShiftLeftLogical(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.patch_vertices_in), + ctx.Const(16u)); + default: + LOG_WARNING(Shader, "(STUBBED) called"); + return ctx.Const(0x00ff0000u); + } +} + Id EmitSampleId(EmitContext& ctx) { return ctx.OpLoad(ctx.U32[1], ctx.sample_id); } @@ -537,6 +550,17 @@ Id EmitResolutionDownFactor(EmitContext& ctx) { } } +Id EmitRenderArea(EmitContext& ctx) { + if (ctx.profile.unified_descriptor_binding) { + const Id pointer_type{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.F32[4])}; + const Id index{ctx.Const(ctx.render_are_member_index)}; + const Id pointer{ctx.OpAccessChain(pointer_type, ctx.render_area_push_constant, index)}; + return ctx.OpLoad(ctx.F32[4], pointer); + } else { + throw NotImplementedException("SPIR-V Instruction"); + } +} + Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)}; return ctx.OpLoad(ctx.U32[1], pointer); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp index 7ad0b08..fb2c792 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp @@ -12,7 +12,7 @@ void EmitJoin(EmitContext&) { void EmitDemoteToHelperInvocation(EmitContext& ctx) { if (ctx.profile.support_demote_to_helper_invocation) { - ctx.OpDemoteToHelperInvocationEXT(); + ctx.OpDemoteToHelperInvocation(); } else { const Id kill_label{ctx.OpLabel()}; const Id impossible_label{ctx.OpLabel()}; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 984d072..e31cdc5 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -72,10 +72,12 @@ void EmitSetOFlag(EmitContext& ctx); Id EmitWorkgroupId(EmitContext& ctx); Id EmitLocalInvocationId(EmitContext& ctx); Id EmitInvocationId(EmitContext& ctx); +Id EmitInvocationInfo(EmitContext& ctx); Id EmitSampleId(EmitContext& ctx); Id EmitIsHelperInvocation(EmitContext& ctx); Id EmitYDirection(EmitContext& ctx); Id EmitResolutionDownFactor(EmitContext& ctx); +Id EmitRenderArea(EmitContext& ctx); Id EmitLoadLocal(EmitContext& ctx, Id word_offset); void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value); Id EmitUndefU1(EmitContext& ctx); @@ -177,7 +179,8 @@ Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value); void EmitBitCastU16F16(EmitContext& ctx); Id EmitBitCastU32F32(EmitContext& ctx, Id value); void EmitBitCastU64F64(EmitContext& ctx); -void EmitBitCastF16U16(EmitContext& ctx); +void EmitBitCastS32F32(EmitContext& ctx); +void EmitBitCastF16U16(EmitContext&); Id EmitBitCastF32U32(EmitContext& ctx, Id value); void EmitBitCastF64U64(EmitContext& ctx); Id EmitPackUint2x32(EmitContext& ctx, Id value); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index 00be1f1..9f7b6bb 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp @@ -116,7 +116,8 @@ void EmitPrologue(EmitContext& ctx) { } void EmitEpilogue(EmitContext& ctx) { - if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) { + if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode && + !ctx.profile.support_native_ndc) { ConvertDepthMode(ctx); } if (ctx.stage == Stage::Fragment) { @@ -125,7 +126,7 @@ void EmitEpilogue(EmitContext& ctx) { } void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) { - if (ctx.runtime_info.convert_depth_mode) { + if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) { ConvertDepthMode(ctx); } if (stream.IsImmediate()) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp index 7cbbbfa..2c90f23 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp @@ -6,6 +6,10 @@ namespace Shader::Backend::SPIRV { namespace { +Id SubgroupScope(EmitContext& ctx) { + return ctx.Const(static_cast(spv::Scope::Subgroup)); +} + Id GetThreadId(EmitContext& ctx) { return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); } @@ -49,8 +53,9 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask } Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { - return ctx.OpSelect(ctx.U32[1], in_range, - ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value); + return ctx.OpSelect( + ctx.U32[1], in_range, + ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value); } Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) { @@ -71,40 +76,46 @@ Id EmitLaneId(EmitContext& ctx) { Id EmitVoteAll(EmitContext& ctx, Id pred) { if (!ctx.profile.warp_size_potentially_larger_than_guest) { - return ctx.OpSubgroupAllKHR(ctx.U1, pred); + return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred); } - const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; + const Id mask_ballot{ + ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)}; const Id active_mask{WarpExtract(ctx, mask_ballot)}; - const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; + const Id ballot{ + WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))}; const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; return ctx.OpIEqual(ctx.U1, lhs, active_mask); } Id EmitVoteAny(EmitContext& ctx, Id pred) { if (!ctx.profile.warp_size_potentially_larger_than_guest) { - return ctx.OpSubgroupAnyKHR(ctx.U1, pred); + return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred); } - const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; + const Id mask_ballot{ + ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)}; const Id active_mask{WarpExtract(ctx, mask_ballot)}; - const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; + const Id ballot{ + WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))}; const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value); } Id EmitVoteEqual(EmitContext& ctx, Id pred) { if (!ctx.profile.warp_size_potentially_larger_than_guest) { - return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred); + return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred); } - const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; + const Id mask_ballot{ + ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)}; const Id active_mask{WarpExtract(ctx, mask_ballot)}; - const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; + const Id ballot{ + WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))}; const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)}; return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value), ctx.OpIEqual(ctx.U1, lhs, active_mask)); } Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { - const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)}; + const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)}; if (!ctx.profile.warp_size_potentially_larger_than_guest) { return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index aecc4c6..41dc6d0 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -473,6 +473,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf DefineAttributeMemAccess(program.info); DefineGlobalMemoryFunctions(program.info); DefineRescalingInput(program.info); + DefineRenderArea(program.info); } EmitContext::~EmitContext() = default; @@ -982,6 +983,36 @@ void EmitContext::DefineRescalingInputUniformConstant() { } } +void EmitContext::DefineRenderArea(const Info& info) { + if (!info.uses_render_area) { + return; + } + + if (profile.unified_descriptor_binding) { + boost::container::static_vector members{}; + u32 member_index{0}; + + members.push_back(F32[4]); + render_are_member_index = member_index++; + + const Id push_constant_struct{TypeStruct(std::span(members.data(), members.size()))}; + Decorate(push_constant_struct, spv::Decoration::Block); + Name(push_constant_struct, "RenderAreaInfo"); + + MemberDecorate(push_constant_struct, render_are_member_index, spv::Decoration::Offset, 0); + MemberName(push_constant_struct, render_are_member_index, "render_area"); + + const Id pointer_type{TypePointer(spv::StorageClass::PushConstant, push_constant_struct)}; + render_area_push_constant = + AddGlobalVariable(pointer_type, spv::StorageClass::PushConstant); + Name(render_area_push_constant, "render_area_push_constants"); + + if (profile.supported_spirv >= 0x00010400) { + interfaces.push_back(render_area_push_constant); + } + } +} + void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { if (info.constant_buffer_descriptors.empty()) { return; @@ -1294,6 +1325,10 @@ void EmitContext::DefineInputs(const IR::Program& program) { if (info.uses_invocation_id) { invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId); } + if (info.uses_invocation_info && + (stage == Shader::Stage::TessellationControl || stage == Shader::Stage::TessellationEval)) { + patch_vertices_in = DefineInput(*this, U32[1], false, spv::BuiltIn::PatchVertices); + } if (info.uses_sample_id) { sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId); } @@ -1310,8 +1345,10 @@ void EmitContext::DefineInputs(const IR::Program& program) { if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || (profile.warp_size_potentially_larger_than_guest && (info.uses_subgroup_vote || info.uses_subgroup_mask))) { + AddCapability(spv::Capability::GroupNonUniform); subgroup_local_invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); + Decorate(subgroup_local_invocation_id, spv::Decoration::Flat); } if (info.uses_fswzadd) { const Id f32_one{Const(1.0f)}; @@ -1324,6 +1361,11 @@ void EmitContext::DefineInputs(const IR::Program& program) { if (loads[IR::Attribute::PrimitiveId]) { primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId); } + if (loads[IR::Attribute::Layer]) { + AddCapability(spv::Capability::Geometry); + layer = DefineInput(*this, U32[1], false, spv::BuiltIn::Layer); + Decorate(layer, spv::Decoration::Flat); + } if (loads.AnyComponent(IR::Attribute::PositionX)) { const bool is_fragment{stage != Stage::Fragment}; const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord}; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index bc25b8b..dde45b4 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -204,6 +204,7 @@ public: Id workgroup_id{}; Id local_invocation_id{}; Id invocation_id{}; + Id patch_vertices_in{}; Id sample_id{}; Id is_helper_invocation{}; Id subgroup_local_invocation_id{}; @@ -243,6 +244,9 @@ public: u32 texture_rescaling_index{}; u32 image_rescaling_index{}; + Id render_area_push_constant{}; + u32 render_are_member_index{}; + Id local_memory{}; Id shared_memory_u8{}; @@ -318,6 +322,7 @@ private: void DefineRescalingInput(const Info& info); void DefineRescalingInputPushConstant(); void DefineRescalingInputUniformConstant(); + void DefineRenderArea(const Info& info); void DefineInputs(const IR::Program& program); void DefineOutputs(const IR::Program& program); diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h index 9729d48..402f266 100644 --- a/src/shader_recompiler/environment.h +++ b/src/shader_recompiler/environment.h @@ -22,6 +22,10 @@ public: [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0; + [[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0; + + [[nodiscard]] virtual u32 ReadViewportTransformState() = 0; + [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0; [[nodiscard]] virtual u32 LocalMemorySize() const = 0; diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index 11086ed..0cdac0e 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp @@ -362,6 +362,10 @@ U32 IREmitter::InvocationId() { return Inst(Opcode::InvocationId); } +U32 IREmitter::InvocationInfo() { + return Inst(Opcode::InvocationInfo); +} + U32 IREmitter::SampleId() { return Inst(Opcode::SampleId); } @@ -378,6 +382,14 @@ F32 IREmitter::ResolutionDownFactor() { return Inst(Opcode::ResolutionDownFactor); } +F32 IREmitter::RenderAreaWidth() { + return F32(CompositeExtract(Inst(Opcode::RenderArea), 0)); +} + +F32 IREmitter::RenderAreaHeight() { + return F32(CompositeExtract(Inst(Opcode::RenderArea), 1)); +} + U32 IREmitter::LaneId() { return Inst(Opcode::LaneId); } @@ -683,6 +695,11 @@ IR::U32 IREmitter::BitCast(const IR::F32& value) { return Inst(Opcode::BitCastU32F32, value); } +template <> +IR::S32 IREmitter::BitCast(const IR::F32& value) { + return Inst(Opcode::BitCastS32F32, value); +} + template <> IR::F32 IREmitter::BitCast(const IR::U32& value) { return Inst(Opcode::BitCastF32U32, value); diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index 25839a3..2df992f 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h @@ -97,12 +97,16 @@ public: [[nodiscard]] U32 LocalInvocationIdZ(); [[nodiscard]] U32 InvocationId(); + [[nodiscard]] U32 InvocationInfo(); [[nodiscard]] U32 SampleId(); [[nodiscard]] U1 IsHelperInvocation(); [[nodiscard]] F32 YDirection(); [[nodiscard]] F32 ResolutionDownFactor(); + [[nodiscard]] F32 RenderAreaWidth(); + [[nodiscard]] F32 RenderAreaHeight(); + [[nodiscard]] U32 LaneId(); [[nodiscard]] U32 LoadGlobalU8(const U64& address); diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp index 468782e..8441798 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.cpp +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp @@ -325,11 +325,6 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) { phi_args.emplace_back(predecessor, value); } -void Inst::ErasePhiOperand(size_t index) { - const auto operand_it{phi_args.begin() + static_cast(index)}; - phi_args.erase(operand_it); -} - void Inst::OrderPhiArgs() { if (op != Opcode::Phi) { throw LogicError("{} is not a Phi instruction", op); diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h index 752879a..d155afd 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.h +++ b/src/shader_recompiler/frontend/ir/opcodes.h @@ -8,6 +8,7 @@ #include +#include "common/polyfill_ranges.h" #include "shader_recompiler/frontend/ir/type.h" namespace Shader::IR { @@ -37,6 +38,7 @@ constexpr Type U8{Type::U8}; constexpr Type U16{Type::U16}; constexpr Type U32{Type::U32}; constexpr Type U64{Type::U64}; +constexpr Type S32{Type::S32}; constexpr Type F16{Type::F16}; constexpr Type F32{Type::F32}; constexpr Type F64{Type::F64}; diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 86410dd..1fe3749 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc @@ -59,10 +59,12 @@ OPCODE(SetOFlag, Void, U1, OPCODE(WorkgroupId, U32x3, ) OPCODE(LocalInvocationId, U32x3, ) OPCODE(InvocationId, U32, ) +OPCODE(InvocationInfo, U32, ) OPCODE(SampleId, U32, ) OPCODE(IsHelperInvocation, U1, ) OPCODE(YDirection, F32, ) OPCODE(ResolutionDownFactor, F32, ) +OPCODE(RenderArea, F32x4, ) // Undefined OPCODE(UndefU1, U1, ) @@ -173,6 +175,7 @@ OPCODE(SelectF64, F64, U1, OPCODE(BitCastU16F16, U16, F16, ) OPCODE(BitCastU32F32, U32, F32, ) OPCODE(BitCastU64F64, U64, F64, ) +OPCODE(BitCastS32F32, S32, F32, ) OPCODE(BitCastF16U16, F16, U16, ) OPCODE(BitCastF32U32, F32, U32, ) OPCODE(BitCastF64U64, F64, U64, ) diff --git a/src/shader_recompiler/frontend/ir/patch.h b/src/shader_recompiler/frontend/ir/patch.h index 1e37c8e..5077e56 100644 --- a/src/shader_recompiler/frontend/ir/patch.h +++ b/src/shader_recompiler/frontend/ir/patch.h @@ -14,8 +14,6 @@ enum class Patch : u64 { TessellationLodBottom, TessellationLodInteriorU, TessellationLodInteriorV, - ComponentPadding0, - ComponentPadding1, Component0, Component1, Component2, @@ -137,7 +135,7 @@ enum class Patch : u64 { Component118, Component119, }; -static_assert(static_cast(Patch::Component119) == 127); +static_assert(static_cast(Patch::Component119) == 125); [[nodiscard]] bool IsGeneric(Patch patch) noexcept; diff --git a/src/shader_recompiler/frontend/ir/type.h b/src/shader_recompiler/frontend/ir/type.h index 04c8c4d..5a7c706 100644 --- a/src/shader_recompiler/frontend/ir/type.h +++ b/src/shader_recompiler/frontend/ir/type.h @@ -24,21 +24,22 @@ enum class Type { U16 = 1 << 7, U32 = 1 << 8, U64 = 1 << 9, - F16 = 1 << 10, - F32 = 1 << 11, - F64 = 1 << 12, - U32x2 = 1 << 13, - U32x3 = 1 << 14, - U32x4 = 1 << 15, - F16x2 = 1 << 16, - F16x3 = 1 << 17, - F16x4 = 1 << 18, - F32x2 = 1 << 19, - F32x3 = 1 << 20, - F32x4 = 1 << 21, - F64x2 = 1 << 22, - F64x3 = 1 << 23, - F64x4 = 1 << 24, + S32 = 1 << 10, + F16 = 1 << 11, + F32 = 1 << 12, + F64 = 1 << 13, + U32x2 = 1 << 14, + U32x3 = 1 << 15, + U32x4 = 1 << 16, + F16x2 = 1 << 17, + F16x3 = 1 << 18, + F16x4 = 1 << 19, + F32x2 = 1 << 20, + F32x3 = 1 << 21, + F32x4 = 1 << 22, + F64x2 = 1 << 23, + F64x3 = 1 << 24, + F64x4 = 1 << 25, }; DECLARE_ENUM_FLAG_OPERATORS(Type) diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp index 3461693..30ba123 100644 --- a/src/shader_recompiler/frontend/ir/value.cpp +++ b/src/shader_recompiler/frontend/ir/value.cpp @@ -23,6 +23,8 @@ Value::Value(u16 value) noexcept : type{Type::U16}, imm_u16{value} {} Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {} +Value::Value(s32 value) noexcept : type{Type::S32}, imm_s32{value} {} + Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {} Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} @@ -69,6 +71,7 @@ bool Value::operator==(const Value& other) const { return imm_u16 == other.imm_u16; case Type::U32: case Type::F32: + case Type::S32: return imm_u32 == other.imm_u32; case Type::U64: case Type::F64: diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h index 1a2e4cc..8b34356 100644 --- a/src/shader_recompiler/frontend/ir/value.h +++ b/src/shader_recompiler/frontend/ir/value.h @@ -23,7 +23,6 @@ #include "shader_recompiler/frontend/ir/pred.h" #include "shader_recompiler/frontend/ir/reg.h" #include "shader_recompiler/frontend/ir/type.h" -#include "shader_recompiler/frontend/ir/value.h" namespace Shader::IR { @@ -44,6 +43,7 @@ public: explicit Value(u8 value) noexcept; explicit Value(u16 value) noexcept; explicit Value(u32 value) noexcept; + explicit Value(s32 value) noexcept; explicit Value(f32 value) noexcept; explicit Value(u64 value) noexcept; explicit Value(f64 value) noexcept; @@ -66,6 +66,7 @@ public: [[nodiscard]] u8 U8() const; [[nodiscard]] u16 U16() const; [[nodiscard]] u32 U32() const; + [[nodiscard]] s32 S32() const; [[nodiscard]] f32 F32() const; [[nodiscard]] u64 U64() const; [[nodiscard]] f64 F64() const; @@ -85,6 +86,7 @@ private: u8 imm_u8; u16 imm_u16; u32 imm_u32; + s32 imm_s32; f32 imm_f32; u64 imm_u64; f64 imm_f64; @@ -178,13 +180,9 @@ public: /// Get a pointer to the block of a phi argument. [[nodiscard]] Block* PhiBlock(size_t index) const; - /// Add phi operand to a phi instruction. void AddPhiOperand(Block* predecessor, const Value& value); - // Erase the phi operand at the given index. - void ErasePhiOperand(size_t index); - /// Orders the Phi arguments from farthest away to nearest. void OrderPhiArgs(); @@ -270,6 +268,7 @@ using U8 = TypedValue; using U16 = TypedValue; using U32 = TypedValue; using U64 = TypedValue; +using S32 = TypedValue; using F16 = TypedValue; using F32 = TypedValue; using F64 = TypedValue; @@ -381,6 +380,14 @@ inline u32 Value::U32() const { return imm_u32; } +inline s32 Value::S32() const { + if (IsIdentity()) { + return inst->Arg(0).S32(); + } + DEBUG_ASSERT(type == Type::S32); + return imm_s32; +} + inline f32 Value::F32() const { if (IsIdentity()) { return inst->Arg(0).F32(); diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp index 6939692..dce414c 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp @@ -9,6 +9,7 @@ #include +#include "common/polyfill_ranges.h" #include "shader_recompiler/exception.h" #include "shader_recompiler/frontend/maxwell/control_flow.h" #include "shader_recompiler/frontend/maxwell/decode.h" diff --git a/src/shader_recompiler/frontend/maxwell/decode.cpp b/src/shader_recompiler/frontend/maxwell/decode.cpp index 455c914..774f65b 100644 --- a/src/shader_recompiler/frontend/maxwell/decode.cpp +++ b/src/shader_recompiler/frontend/maxwell/decode.cpp @@ -7,6 +7,7 @@ #include #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "shader_recompiler/exception.h" #include "shader_recompiler/frontend/maxwell/decode.h" #include "shader_recompiler/frontend/maxwell/opcodes.h" diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp index 578bc8c..80c90fe 100644 --- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp @@ -12,6 +12,7 @@ #include +#include "common/polyfill_ranges.h" #include "shader_recompiler/environment.h" #include "shader_recompiler/frontend/ir/basic_block.h" #include "shader_recompiler/frontend/ir/ir_emitter.h" @@ -964,9 +965,9 @@ private: demote_endif_node.type = Type::EndIf; demote_endif_node.data.end_if.merge = return_block_it->data.block; - asl.insert(return_block_it, demote_endif_node); - asl.insert(return_block_it, demote_node); - asl.insert(return_block_it, demote_if_node); + const auto next_it_1 = asl.insert(return_block_it, demote_endif_node); + const auto next_it_2 = asl.insert(next_it_1, demote_node); + asl.insert(next_it_2, demote_if_node); } ObjectPool& stmt_pool; diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp index 4942878..85c18d9 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp @@ -176,12 +176,13 @@ void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::F16F32F64& src_a) { (f2i.src_format == SrcFormat::F64) != (f2i.dest_format == DestFormat::I64); if (special_nan_cases) { if (f2i.dest_format == DestFormat::I32) { + constexpr u32 nan_value = 0x8000'0000U; handled_special_case = true; - result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(0x8000'0000U), result)}; + result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(nan_value), result)}; } else if (f2i.dest_format == DestFormat::I64) { + constexpr u64 nan_value = 0x8000'0000'0000'0000ULL; handled_special_case = true; - result = IR::U64{ - v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(0x8000'0000'0000'0000UL), result)}; + result = IR::U64{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(nan_value), result)}; } } if (!handled_special_case && is_signed) { diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp index 52be12f..753c620 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp @@ -117,8 +117,7 @@ enum class SpecialRegister : u64 { case SpecialRegister::SR_THREAD_KILL: return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))}; case SpecialRegister::SR_INVOCATION_INFO: - LOG_WARNING(Shader, "(STUBBED) SR_INVOCATION_INFO"); - return ir.Imm32(0x00ff'0000); + return ir.InvocationInfo(); case SpecialRegister::SR_TID: { const IR::Value tid{ir.LocalInvocationId()}; return ir.BitFieldInsert(ir.BitFieldInsert(IR::U32{ir.CompositeExtract(tid, 0)}, diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 77efb4f..3adbd2b 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -9,6 +9,7 @@ #include "common/settings.h" #include "shader_recompiler/exception.h" #include "shader_recompiler/frontend/ir/basic_block.h" +#include "shader_recompiler/frontend/ir/ir_emitter.h" #include "shader_recompiler/frontend/ir/post_order.h" #include "shader_recompiler/frontend/maxwell/structured_control_flow.h" #include "shader_recompiler/frontend/maxwell/translate/translate.h" @@ -137,28 +138,35 @@ bool IsLegacyAttribute(IR::Attribute attribute) { } std::map GenerateLegacyToGenericMappings( - const VaryingState& state, std::queue ununsed_generics) { + const VaryingState& state, std::queue unused_generics, + const std::map& previous_stage_mapping) { std::map mapping; + auto update_mapping = [&mapping, &unused_generics, previous_stage_mapping](IR::Attribute attr, + size_t count) { + if (previous_stage_mapping.find(attr) != previous_stage_mapping.end()) { + for (size_t i = 0; i < count; ++i) { + mapping.insert({attr + i, previous_stage_mapping.at(attr + i)}); + } + } else { + for (size_t i = 0; i < count; ++i) { + mapping.insert({attr + i, unused_generics.front() + i}); + } + unused_generics.pop(); + } + }; for (size_t index = 0; index < 4; ++index) { auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4; if (state.AnyComponent(attr)) { - for (size_t i = 0; i < 4; ++i) { - mapping.insert({attr + i, ununsed_generics.front() + i}); - } - ununsed_generics.pop(); + update_mapping(attr, 4); } } if (state[IR::Attribute::FogCoordinate]) { - mapping.insert({IR::Attribute::FogCoordinate, ununsed_generics.front()}); - ununsed_generics.pop(); + update_mapping(IR::Attribute::FogCoordinate, 1); } for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) { auto attr = IR::Attribute::FixedFncTexture0S + index * 4; if (state.AnyComponent(attr)) { - for (size_t i = 0; i < 4; ++i) { - mapping.insert({attr + i, ununsed_generics.front() + i}); - } - ununsed_generics.pop(); + update_mapping(attr, 4); } } return mapping; @@ -213,8 +221,10 @@ IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& inst_pool, ObjectPool ununsed_output_generics{}; + std::queue unused_output_generics{}; for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { if (!stores.Generic(index)) { - ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4); + unused_output_generics.push(IR::Attribute::Generic0X + index * 4); } } - auto mappings = GenerateLegacyToGenericMappings(stores, ununsed_output_generics); + program.info.legacy_stores_mapping = + GenerateLegacyToGenericMappings(stores, unused_output_generics, {}); for (IR::Block* const block : program.post_order_blocks) { for (IR::Inst& inst : block->Instructions()) { switch (inst.GetOpcode()) { case IR::Opcode::SetAttribute: { const auto attr = inst.Arg(0).Attribute(); if (IsLegacyAttribute(attr)) { - stores.Set(mappings[attr], true); - inst.SetArg(0, Shader::IR::Value(mappings[attr])); + stores.Set(program.info.legacy_stores_mapping[attr], true); + inst.SetArg(0, Shader::IR::Value(program.info.legacy_stores_mapping[attr])); } break; } @@ -292,15 +305,16 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run auto& loads = program.info.loads; if (loads.Legacy()) { - std::queue ununsed_input_generics{}; + std::queue unused_input_generics{}; for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { const AttributeType input_type{runtime_info.generic_input_types[index]}; if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) || input_type == AttributeType::Disabled) { - ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4); + unused_input_generics.push(IR::Attribute::Generic0X + index * 4); } } - auto mappings = GenerateLegacyToGenericMappings(loads, ununsed_input_generics); + auto mappings = GenerateLegacyToGenericMappings( + loads, unused_input_generics, runtime_info.previous_stage_legacy_stores_mapping); for (IR::Block* const block : program.post_order_blocks) { for (IR::Inst& inst : block->Instructions()) { switch (inst.GetOpcode()) { @@ -320,4 +334,82 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run } } +IR::Program GenerateGeometryPassthrough(ObjectPool& inst_pool, + ObjectPool& block_pool, + const HostTranslateInfo& host_info, + IR::Program& source_program, + Shader::OutputTopology output_topology) { + IR::Program program; + program.stage = Stage::Geometry; + program.output_topology = output_topology; + switch (output_topology) { + case OutputTopology::PointList: + program.output_vertices = 1; + break; + case OutputTopology::LineStrip: + program.output_vertices = 2; + break; + default: + program.output_vertices = 3; + break; + } + + program.is_geometry_passthrough = false; + program.info.loads.mask = source_program.info.stores.mask; + program.info.stores.mask = source_program.info.stores.mask; + program.info.stores.Set(IR::Attribute::Layer, true); + program.info.stores.Set(source_program.info.emulated_layer, false); + + IR::Block* current_block = block_pool.Create(inst_pool); + auto& node{program.syntax_list.emplace_back()}; + node.type = IR::AbstractSyntaxNode::Type::Block; + node.data.block = current_block; + + IR::IREmitter ir{*current_block}; + for (u32 i = 0; i < program.output_vertices; i++) { + // Assign generics from input + for (u32 j = 0; j < 32; j++) { + if (!program.info.stores.Generic(j)) { + continue; + } + + const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4); + ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0)); + } + + // Assign position from input + const IR::Attribute attr = IR::Attribute::PositionX; + ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0)); + + // Assign layer + ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(source_program.info.emulated_layer), + ir.Imm32(0)); + + // Emit vertex + ir.EmitVertex(ir.Imm32(0)); + } + ir.EndPrimitive(ir.Imm32(0)); + + IR::Block* return_block{block_pool.Create(inst_pool)}; + IR::IREmitter{*return_block}.Epilogue(); + current_block->AddBranch(return_block); + + auto& merge{program.syntax_list.emplace_back()}; + merge.type = IR::AbstractSyntaxNode::Type::Block; + merge.data.block = return_block; + program.syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return; + + program.blocks = GenerateBlocks(program.syntax_list); + program.post_order_blocks = PostOrder(program.syntax_list.front()); + Optimization::SsaRewritePass(program); + + return program; +} + } // namespace Shader::Maxwell diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h index 02ede8c..497afe7 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.h +++ b/src/shader_recompiler/frontend/maxwell/translate_program.h @@ -25,4 +25,13 @@ namespace Shader::Maxwell { void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info); +// Maxwell v1 and older Nvidia cards don't support setting gl_Layer from non-geometry stages. +// This creates a workaround by setting the layer as a generic output and creating a +// passthrough geometry shader that reads the generic and sets the layer. +[[nodiscard]] IR::Program GenerateGeometryPassthrough(ObjectPool& inst_pool, + ObjectPool& block_pool, + const HostTranslateInfo& host_info, + IR::Program& source_program, + Shader::OutputTopology output_topology); + } // namespace Shader::Maxwell diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h index 8818743..d5d2795 100644 --- a/src/shader_recompiler/host_translate_info.h +++ b/src/shader_recompiler/host_translate_info.h @@ -13,6 +13,8 @@ struct HostTranslateInfo { bool support_float16{}; ///< True when the device supports 16-bit floats bool support_int64{}; ///< True when the device supports 64-bit integers bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered + bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers + bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS }; } // namespace Shader diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp index 7cff8ec..5a41952 100644 --- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp +++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp @@ -468,6 +468,9 @@ void VisitUsages(Info& info, IR::Inst& inst) { case IR::Opcode::InvocationId: info.uses_invocation_id = true; break; + case IR::Opcode::InvocationInfo: + info.uses_invocation_info = true; + break; case IR::Opcode::SampleId: info.uses_sample_id = true; break; diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp index 9a7d473..1bd8afd 100644 --- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp +++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp @@ -1,104 +1,24 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include - -#include - #include "shader_recompiler/frontend/ir/basic_block.h" #include "shader_recompiler/frontend/ir/value.h" #include "shader_recompiler/ir_opt/passes.h" namespace Shader::Optimization { -namespace { -template -void DeadInstElimination(IR::Block* const block) { - // We iterate over the instructions in reverse order. - // This is because removing an instruction reduces the number of uses for earlier instructions. - auto it{block->end()}; - while (it != block->begin()) { - --it; - if constexpr (TEST_USES) { - if (it->HasUses() || it->MayHaveSideEffects()) { - continue; - } - } - it->Invalidate(); - it = block->Instructions().erase(it); - } -} - -void DeletedPhiArgElimination(IR::Program& program, std::span dead_blocks) { - for (IR::Block* const block : program.blocks) { - for (IR::Inst& phi : *block) { - if (!IR::IsPhi(phi)) { - continue; - } - for (size_t i = 0; i < phi.NumArgs(); ++i) { - if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) { - continue; - } - // Phi operand at this index is an unreachable block - phi.ErasePhiOperand(i); - --i; - } - } - } -} - -void DeadBranchElimination(IR::Program& program) { - boost::container::small_vector dead_blocks; - const auto begin_it{program.syntax_list.begin()}; - for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) { - if (node_it->type != IR::AbstractSyntaxNode::Type::If) { - continue; - } - IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()}; - const IR::U1 cond{cond_ref->Arg(0)}; - if (!cond.IsImmediate()) { - continue; - } - if (cond.U1()) { - continue; - } - // False immediate condition. Remove condition ref, erase the entire branch. - cond_ref->Invalidate(); - // Account for nested if-statements within the if(false) branch - u32 nested_ifs{1u}; - while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) { - node_it = program.syntax_list.erase(node_it); - switch (node_it->type) { - case IR::AbstractSyntaxNode::Type::If: - ++nested_ifs; - break; - case IR::AbstractSyntaxNode::Type::EndIf: - --nested_ifs; - break; - case IR::AbstractSyntaxNode::Type::Block: { - IR::Block* const block{node_it->data.block}; - DeadInstElimination(block); - dead_blocks.push_back(block); - break; - } - default: - break; - } - } - // Erase EndIf node of the if(false) branch - node_it = program.syntax_list.erase(node_it); - // Account for loop increment - --node_it; - } - if (!dead_blocks.empty()) { - DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size())); - } -} -} // namespace void DeadCodeEliminationPass(IR::Program& program) { - DeadBranchElimination(program); + // We iterate over the instructions in reverse order. + // This is because removing an instruction reduces the number of uses for earlier instructions. for (IR::Block* const block : program.post_order_blocks) { - DeadInstElimination(block); + auto it{block->end()}; + while (it != block->begin()) { + --it; + if (!it->HasUses() && !it->MayHaveSideEffects()) { + it->Invalidate(); + it = block->Instructions().erase(it); + } + } } } diff --git a/src/shader_recompiler/ir_opt/layer_pass.cpp b/src/shader_recompiler/ir_opt/layer_pass.cpp new file mode 100644 index 0000000..4574f7c --- /dev/null +++ b/src/shader_recompiler/ir_opt/layer_pass.cpp @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +#include + +#include "shader_recompiler/environment.h" +#include "shader_recompiler/frontend/ir/basic_block.h" +#include "shader_recompiler/frontend/ir/breadth_first_search.h" +#include "shader_recompiler/frontend/ir/ir_emitter.h" +#include "shader_recompiler/host_translate_info.h" +#include "shader_recompiler/ir_opt/passes.h" +#include "shader_recompiler/shader_info.h" + +namespace Shader::Optimization { + +static IR::Attribute EmulatedLayerAttribute(VaryingState& stores) { + for (u32 i = 0; i < 32; i++) { + if (!stores.Generic(i)) { + return IR::Attribute::Generic0X + (i * 4); + } + } + return IR::Attribute::Layer; +} + +static bool PermittedProgramStage(Stage stage) { + switch (stage) { + case Stage::VertexA: + case Stage::VertexB: + case Stage::TessellationControl: + case Stage::TessellationEval: + return true; + default: + return false; + } +} + +void LayerPass(IR::Program& program, const HostTranslateInfo& host_info) { + if (host_info.support_viewport_index_layer || !PermittedProgramStage(program.stage)) { + return; + } + + const auto end{program.post_order_blocks.end()}; + const auto layer_attribute = EmulatedLayerAttribute(program.info.stores); + bool requires_layer_emulation = false; + + for (auto block = program.post_order_blocks.begin(); block != end; ++block) { + for (IR::Inst& inst : (*block)->Instructions()) { + if (inst.GetOpcode() == IR::Opcode::SetAttribute && + inst.Arg(0).Attribute() == IR::Attribute::Layer) { + requires_layer_emulation = true; + inst.SetArg(0, IR::Value{layer_attribute}); + } + } + } + + if (requires_layer_emulation) { + program.info.requires_layer_emulation = true; + program.info.emulated_layer = layer_attribute; + program.info.stores.Set(IR::Attribute::Layer, false); + program.info.stores.Set(layer_attribute, true); + } +} + +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index 6ff8e42..11bfe80 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h @@ -6,6 +6,10 @@ #include "shader_recompiler/environment.h" #include "shader_recompiler/frontend/ir/program.h" +namespace Shader { +struct HostTranslateInfo; +} + namespace Shader::Optimization { void CollectShaderInfoPass(Environment& env, IR::Program& program); @@ -17,7 +21,9 @@ void LowerFp16ToFp32(IR::Program& program); void LowerInt64ToInt32(IR::Program& program); void RescalingPass(IR::Program& program); void SsaRewritePass(IR::Program& program); -void TexturePass(Environment& env, IR::Program& program); +void PositionPass(Environment& env, IR::Program& program); +void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info); +void LayerPass(IR::Program& program, const HostTranslateInfo& host_info); void VerificationPass(const IR::Program& program); // Dual Vertex diff --git a/src/shader_recompiler/ir_opt/position_pass.cpp b/src/shader_recompiler/ir_opt/position_pass.cpp new file mode 100644 index 0000000..3c20b71 --- /dev/null +++ b/src/shader_recompiler/ir_opt/position_pass.cpp @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "shader_recompiler/frontend/ir/basic_block.h" +#include "shader_recompiler/frontend/ir/ir_emitter.h" +#include "shader_recompiler/frontend/ir/value.h" +#include "shader_recompiler/ir_opt/passes.h" + +namespace Shader::Optimization { + +namespace { +struct PositionInst { + IR::Inst* inst; + IR::Block* block; + IR::Attribute attr; +}; +using PositionInstVector = boost::container::small_vector; +} // Anonymous namespace + +void PositionPass(Environment& env, IR::Program& program) { + if (env.ShaderStage() != Stage::VertexB || env.ReadViewportTransformState()) { + return; + } + + Info& info{program.info}; + info.uses_render_area = true; + + PositionInstVector to_replace; + for (IR::Block* const block : program.post_order_blocks) { + for (IR::Inst& inst : block->Instructions()) { + switch (inst.GetOpcode()) { + case IR::Opcode::SetAttribute: { + const IR::Attribute attr{inst.Arg(0).Attribute()}; + switch (attr) { + case IR::Attribute::PositionX: + case IR::Attribute::PositionY: { + to_replace.push_back(PositionInst{.inst = &inst, .block = block, .attr = attr}); + break; + } + default: + break; + } + break; + } + default: + break; + } + } + } + + for (PositionInst& position_inst : to_replace) { + IR::IREmitter ir{*position_inst.block, + IR::Block::InstructionList::s_iterator_to(*position_inst.inst)}; + const IR::F32 value(position_inst.inst->Arg(1)); + const IR::F32F64 scale(ir.Imm32(2.f)); + const IR::F32 negative_one{ir.Imm32(-1.f)}; + switch (position_inst.attr) { + case IR::Attribute::PositionX: { + position_inst.inst->SetArg( + 1, + ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaWidth()), scale), negative_one)); + break; + } + case IR::Attribute::PositionY: { + position_inst.inst->SetArg( + 1, + ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaHeight()), scale), negative_one)); + break; + } + default: + break; + } + } +} +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 597112b..f5c86fc 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -11,6 +11,7 @@ #include "shader_recompiler/frontend/ir/basic_block.h" #include "shader_recompiler/frontend/ir/breadth_first_search.h" #include "shader_recompiler/frontend/ir/ir_emitter.h" +#include "shader_recompiler/host_translate_info.h" #include "shader_recompiler/ir_opt/passes.h" #include "shader_recompiler/shader_info.h" @@ -19,8 +20,10 @@ namespace { struct ConstBufferAddr { u32 index; u32 offset; + u32 shift_left; u32 secondary_index; u32 secondary_offset; + u32 secondary_shift_left; IR::U32 dynamic_offset; u32 count; bool has_secondary; @@ -172,19 +175,41 @@ bool IsTextureInstruction(const IR::Inst& inst) { return IndexedInstruction(inst) != IR::Opcode::Void; } -std::optional TryGetConstBuffer(const IR::Inst* inst); +std::optional TryGetConstBuffer(const IR::Inst* inst, Environment& env); -std::optional Track(const IR::Value& value) { - return IR::BreadthFirstSearch(value, TryGetConstBuffer); +std::optional Track(const IR::Value& value, Environment& env) { + return IR::BreadthFirstSearch( + value, [&env](const IR::Inst* inst) { return TryGetConstBuffer(inst, env); }); } -std::optional TryGetConstBuffer(const IR::Inst* inst) { +std::optional TryGetConstant(IR::Value& value, Environment& env) { + const IR::Inst* inst = value.InstRecursive(); + if (inst->GetOpcode() != IR::Opcode::GetCbufU32) { + return std::nullopt; + } + const IR::Value index{inst->Arg(0)}; + const IR::Value offset{inst->Arg(1)}; + if (!index.IsImmediate()) { + return std::nullopt; + } + if (!offset.IsImmediate()) { + return std::nullopt; + } + const auto index_number = index.U32(); + if (index_number != 1) { + return std::nullopt; + } + const auto offset_number = offset.U32(); + return env.ReadCbufValue(index_number, offset_number); +} + +std::optional TryGetConstBuffer(const IR::Inst* inst, Environment& env) { switch (inst->GetOpcode()) { default: return std::nullopt; case IR::Opcode::BitwiseOr32: { - std::optional lhs{Track(inst->Arg(0))}; - std::optional rhs{Track(inst->Arg(1))}; + std::optional lhs{Track(inst->Arg(0), env)}; + std::optional rhs{Track(inst->Arg(1), env)}; if (!lhs || !rhs) { return std::nullopt; } @@ -194,19 +219,62 @@ std::optional TryGetConstBuffer(const IR::Inst* inst) { if (lhs->count > 1 || rhs->count > 1) { return std::nullopt; } - if (lhs->index > rhs->index || lhs->offset > rhs->offset) { + if (lhs->shift_left > 0 || lhs->index > rhs->index || lhs->offset > rhs->offset) { std::swap(lhs, rhs); } return ConstBufferAddr{ .index = lhs->index, .offset = lhs->offset, + .shift_left = lhs->shift_left, .secondary_index = rhs->index, .secondary_offset = rhs->offset, + .secondary_shift_left = rhs->shift_left, .dynamic_offset = {}, .count = 1, .has_secondary = true, }; } + case IR::Opcode::ShiftLeftLogical32: { + const IR::Value shift{inst->Arg(1)}; + if (!shift.IsImmediate()) { + return std::nullopt; + } + std::optional lhs{Track(inst->Arg(0), env)}; + if (lhs) { + lhs->shift_left = shift.U32(); + } + return lhs; + break; + } + case IR::Opcode::BitwiseAnd32: { + IR::Value op1{inst->Arg(0)}; + IR::Value op2{inst->Arg(1)}; + if (op1.IsImmediate()) { + std::swap(op1, op2); + } + if (!op2.IsImmediate() && !op1.IsImmediate()) { + do { + auto try_index = TryGetConstant(op1, env); + if (try_index) { + op1 = op2; + op2 = IR::Value{*try_index}; + break; + } + auto try_index_2 = TryGetConstant(op2, env); + if (try_index_2) { + op2 = IR::Value{*try_index_2}; + break; + } + return std::nullopt; + } while (false); + } + std::optional lhs{Track(op1, env)}; + if (lhs) { + lhs->shift_left = static_cast(std::countr_zero(op2.U32())); + } + return lhs; + break; + } case IR::Opcode::GetCbufU32x2: case IR::Opcode::GetCbufU32: break; @@ -222,8 +290,10 @@ std::optional TryGetConstBuffer(const IR::Inst* inst) { return ConstBufferAddr{ .index = index.U32(), .offset = offset.U32(), + .shift_left = 0, .secondary_index = 0, .secondary_offset = 0, + .secondary_shift_left = 0, .dynamic_offset = {}, .count = 1, .has_secondary = false, @@ -247,8 +317,10 @@ std::optional TryGetConstBuffer(const IR::Inst* inst) { return ConstBufferAddr{ .index = index.U32(), .offset = base_offset, + .shift_left = 0, .secondary_index = 0, .secondary_offset = 0, + .secondary_shift_left = 0, .dynamic_offset = dynamic_offset, .count = 8, .has_secondary = false, @@ -258,7 +330,7 @@ std::optional TryGetConstBuffer(const IR::Inst* inst) { TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { ConstBufferAddr addr; if (IsBindless(inst)) { - const std::optional track_addr{Track(inst.Arg(0))}; + const std::optional track_addr{Track(inst.Arg(0), env)}; if (!track_addr) { throw NotImplementedException("Failed to track bindless texture constant buffer"); } @@ -267,8 +339,10 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { addr = ConstBufferAddr{ .index = env.TextureBoundBuffer(), .offset = inst.Arg(0).U32(), + .shift_left = 0, .secondary_index = 0, .secondary_offset = 0, + .secondary_shift_left = 0, .dynamic_offset = {}, .count = 1, .has_secondary = false, @@ -282,11 +356,20 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { } TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { + const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; + const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset}; + const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left}; + const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset) + << cbuf.secondary_shift_left}; + return env.ReadTextureType(lhs_raw | rhs_raw); +} + +TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) { const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset}; const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)}; const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)}; - return env.ReadTextureType(lhs_raw | rhs_raw); + return env.ReadTexturePixelFormat(lhs_raw | rhs_raw); } class Descriptors { @@ -377,9 +460,41 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) { ir.FPMul(IR::F32(ir.CompositeExtract(coord, 1)), ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1)))))); } + +void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) { + const auto it{IR::Block::InstructionList::s_iterator_to(inst)}; + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + auto get_max_value = [pixel_format]() -> float { + switch (pixel_format) { + case TexturePixelFormat::A8B8G8R8_SNORM: + case TexturePixelFormat::R8G8_SNORM: + case TexturePixelFormat::R8_SNORM: + return 1.f / std::numeric_limits::max(); + case TexturePixelFormat::R16G16B16A16_SNORM: + case TexturePixelFormat::R16G16_SNORM: + case TexturePixelFormat::R16_SNORM: + return 1.f / std::numeric_limits::max(); + default: + throw InvalidArgument("Invalid texture pixel format"); + } + }; + + const IR::Value new_inst{&*block.PrependNewInst(it, inst)}; + const IR::F32 x(ir.CompositeExtract(new_inst, 0)); + const IR::F32 y(ir.CompositeExtract(new_inst, 1)); + const IR::F32 z(ir.CompositeExtract(new_inst, 2)); + const IR::F32 w(ir.CompositeExtract(new_inst, 3)); + const IR::F16F32F64 max_value(ir.Imm32(get_max_value())); + const IR::Value converted = + ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast(x)), max_value), + ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast(y)), max_value), + ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast(z)), max_value), + ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast(w)), max_value)); + inst.ReplaceUsesWith(converted); +} } // Anonymous namespace -void TexturePass(Environment& env, IR::Program& program) { +void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info) { TextureInstVector to_replace; for (IR::Block* const block : program.post_order_blocks) { for (IR::Inst& inst : block->Instructions()) { @@ -487,8 +602,10 @@ void TexturePass(Environment& env, IR::Program& program) { .has_secondary = cbuf.has_secondary, .cbuf_index = cbuf.index, .cbuf_offset = cbuf.offset, + .shift_left = cbuf.shift_left, .secondary_cbuf_index = cbuf.secondary_index, .secondary_cbuf_offset = cbuf.secondary_offset, + .secondary_shift_left = cbuf.secondary_shift_left, .count = cbuf.count, .size_shift = DESCRIPTOR_SIZE_SHIFT, }); @@ -499,8 +616,10 @@ void TexturePass(Environment& env, IR::Program& program) { .has_secondary = cbuf.has_secondary, .cbuf_index = cbuf.index, .cbuf_offset = cbuf.offset, + .shift_left = cbuf.shift_left, .secondary_cbuf_index = cbuf.secondary_index, .secondary_cbuf_offset = cbuf.secondary_offset, + .secondary_shift_left = cbuf.secondary_shift_left, .count = cbuf.count, .size_shift = DESCRIPTOR_SIZE_SHIFT, }); @@ -519,6 +638,14 @@ void TexturePass(Environment& env, IR::Program& program) { } else { inst->SetArg(0, IR::Value{}); } + + if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch && + flags.type == TextureType::Buffer) { + const auto pixel_format = ReadTexturePixelFormat(env, cbuf); + if (pixel_format != TexturePixelFormat::OTHER) { + PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format); + } + } } } diff --git a/src/shader_recompiler/precompiled_headers.h b/src/shader_recompiler/precompiled_headers.h new file mode 100644 index 0000000..5dd6b7e --- /dev/null +++ b/src/shader_recompiler/precompiled_headers.h @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" +#include "frontend/maxwell/translate/impl/impl.h" diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 21d3d23..b8841a5 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -35,6 +35,7 @@ struct Profile { bool support_int64_atomics{}; bool support_derivative_control{}; bool support_geometry_shader_passthrough{}; + bool support_native_ndc{}; bool support_gl_nv_gpu_shader_5{}; bool support_gl_amd_gpu_shader_half_float{}; bool support_gl_texture_shadow_lod{}; diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index dcb5ab1..549b81e 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include @@ -60,6 +61,7 @@ struct TransformFeedbackVarying { struct RuntimeInfo { std::array generic_input_types{}; VaryingState previous_stage_stores; + std::map previous_stage_legacy_stores_mapping; bool convert_depth_mode{}; bool force_early_z{}; diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index f569080..d9c6e92 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -5,6 +5,7 @@ #include #include +#include #include "common/common_types.h" #include "shader_recompiler/frontend/ir/type.h" @@ -28,6 +29,16 @@ enum class TextureType : u32 { }; constexpr u32 NUM_TEXTURE_TYPES = 9; +enum class TexturePixelFormat : u32 { + A8B8G8R8_SNORM, + R8_SNORM, + R8G8_SNORM, + R16G16B16A16_SNORM, + R16G16_SNORM, + R16_SNORM, + OTHER +}; + enum class ImageFormat : u32 { Typeless, R8_UINT, @@ -61,8 +72,10 @@ struct TextureBufferDescriptor { bool has_secondary; u32 cbuf_index; u32 cbuf_offset; + u32 shift_left; u32 secondary_cbuf_index; u32 secondary_cbuf_offset; + u32 secondary_shift_left; u32 count; u32 size_shift; }; @@ -85,8 +98,10 @@ struct TextureDescriptor { bool has_secondary; u32 cbuf_index; u32 cbuf_offset; + u32 shift_left; u32 secondary_cbuf_index; u32 secondary_cbuf_offset; + u32 secondary_shift_left; u32 count; u32 size_shift; }; @@ -112,6 +127,7 @@ struct Info { bool uses_workgroup_id{}; bool uses_local_invocation_id{}; bool uses_invocation_id{}; + bool uses_invocation_info{}; bool uses_sample_id{}; bool uses_is_helper_invocation{}; bool uses_subgroup_invocation_id{}; @@ -123,6 +139,8 @@ struct Info { VaryingState stores; VaryingState passthrough; + std::map legacy_stores_mapping; + bool loads_indexed_attributes{}; std::array stores_frag_color{}; @@ -175,6 +193,7 @@ struct Info { bool uses_shadow_lod{}; bool uses_rescaling_uniform{}; bool uses_cbuf_indirect{}; + bool uses_render_area{}; IR::Type used_constant_buffer_types{}; IR::Type used_storage_buffer_types{}; @@ -185,6 +204,9 @@ struct Info { u32 nvn_buffer_base{}; std::bitset<16> nvn_buffer_used{}; + bool requires_layer_emulation{}; + IR::Attribute emulated_layer{}; + boost::container::static_vector constant_buffer_descriptors; boost::container::static_vector storage_buffers_descriptors; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 43ad2c7..348d1ed 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable(tests common/unique_function.cpp core/core_timing.cpp core/internal_network/network.cpp + precompiled_headers.h tests.cpp video_core/buffer_base.cpp input_common/calibration_configuration_job.cpp @@ -22,3 +23,7 @@ target_link_libraries(tests PRIVATE common core input_common) target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads) add_test(NAME tests COMMAND tests) + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(tests PRIVATE precompiled_headers.h) +endif() diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 7c432a6..284b2ae 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -40,9 +40,6 @@ struct ScopeInit final { core_timing.SetMulticore(true); core_timing.Initialize([]() {}); } - ~ScopeInit() { - core_timing.Shutdown(); - } Core::Timing::CoreTiming core_timing; }; diff --git a/src/tests/precompiled_headers.h b/src/tests/precompiled_headers.h new file mode 100644 index 0000000..aabae73 --- /dev/null +++ b/src/tests/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp index 71121e4..f7236af 100644 --- a/src/tests/video_core/buffer_base.cpp +++ b/src/tests/video_core/buffer_base.cpp @@ -44,7 +44,7 @@ public: [[nodiscard]] unsigned Count() const noexcept { unsigned count = 0; - for (const auto [index, value] : page_table) { + for (const auto& [index, value] : page_table) { count += value; } return count; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 5b38083..fd71bf1 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -4,7 +4,7 @@ add_subdirectory(host_shaders) if(LIBVA_FOUND) - set_source_files_properties(command_classes/codecs/codec.cpp + set_source_files_properties(host1x/codecs/codec.cpp PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES}) endif() @@ -15,32 +15,26 @@ add_library(video_core STATIC buffer_cache/buffer_cache.h cdma_pusher.cpp cdma_pusher.h - command_classes/codecs/codec.cpp - command_classes/codecs/codec.h - command_classes/codecs/h264.cpp - command_classes/codecs/h264.h - command_classes/codecs/vp8.cpp - command_classes/codecs/vp8.h - command_classes/codecs/vp9.cpp - command_classes/codecs/vp9.h - command_classes/codecs/vp9_types.h - command_classes/host1x.cpp - command_classes/host1x.h - command_classes/nvdec.cpp - command_classes/nvdec.h - command_classes/nvdec_common.h - command_classes/sync_manager.cpp - command_classes/sync_manager.h - command_classes/vic.cpp - command_classes/vic.h compatible_formats.cpp compatible_formats.h + control/channel_state.cpp + control/channel_state.h + control/channel_state_cache.cpp + control/channel_state_cache.h + control/scheduler.cpp + control/scheduler.h delayed_destruction_ring.h dirty_flags.cpp dirty_flags.h dma_pusher.cpp dma_pusher.h + engines/sw_blitter/blitter.cpp + engines/sw_blitter/blitter.h + engines/sw_blitter/converter.cpp + engines/sw_blitter/converter.h engines/const_buffer_info.h + engines/draw_manager.cpp + engines/draw_manager.h engines/engine_interface.h engines/engine_upload.cpp engines/engine_upload.h @@ -54,15 +48,37 @@ add_library(video_core STATIC engines/maxwell_3d.h engines/maxwell_dma.cpp engines/maxwell_dma.h + engines/puller.cpp + engines/puller.h framebuffer_config.h + host1x/codecs/codec.cpp + host1x/codecs/codec.h + host1x/codecs/h264.cpp + host1x/codecs/h264.h + host1x/codecs/vp8.cpp + host1x/codecs/vp8.h + host1x/codecs/vp9.cpp + host1x/codecs/vp9.h + host1x/codecs/vp9_types.h + host1x/control.cpp + host1x/control.h + host1x/host1x.cpp + host1x/host1x.h + host1x/nvdec.cpp + host1x/nvdec.h + host1x/nvdec_common.h + host1x/sync_manager.cpp + host1x/sync_manager.h + host1x/syncpoint_manager.cpp + host1x/syncpoint_manager.h + host1x/vic.cpp + host1x/vic.h macro/macro.cpp macro/macro.h macro/macro_hle.cpp macro/macro_hle.h macro/macro_interpreter.cpp macro/macro_interpreter.h - macro/macro_jit_x64.cpp - macro/macro_jit_x64.h fence_manager.h gpu.cpp gpu.h @@ -70,12 +86,18 @@ add_library(video_core STATIC gpu_thread.h memory_manager.cpp memory_manager.h + precompiled_headers.h + pte_kind.h query_cache.h rasterizer_accelerated.cpp rasterizer_accelerated.h rasterizer_interface.h renderer_base.cpp renderer_base.h + renderer_null/null_rasterizer.cpp + renderer_null/null_rasterizer.h + renderer_null/renderer_null.cpp + renderer_null/renderer_null.h renderer_opengl/gl_buffer_cache.cpp renderer_opengl/gl_buffer_cache.h renderer_opengl/gl_compute_pipeline.cpp @@ -156,6 +178,8 @@ add_library(video_core STATIC renderer_vulkan/vk_scheduler.h renderer_vulkan/vk_shader_util.cpp renderer_vulkan/vk_shader_util.h + renderer_vulkan/vk_smaa.cpp + renderer_vulkan/vk_smaa.h renderer_vulkan/vk_staging_buffer_pool.cpp renderer_vulkan/vk_staging_buffer_pool.h renderer_vulkan/vk_state_tracker.cpp @@ -173,6 +197,8 @@ add_library(video_core STATIC shader_environment.h shader_notify.cpp shader_notify.h + smaa_area_tex.h + smaa_search_tex.h surface.cpp surface.h texture_cache/accelerated_swizzle.cpp @@ -195,6 +221,7 @@ add_library(video_core STATIC texture_cache/render_targets.h texture_cache/samples_helper.h texture_cache/slot_vector.h + texture_cache/texture_cache.cpp texture_cache/texture_cache.h texture_cache/texture_cache_base.h texture_cache/types.h @@ -231,7 +258,7 @@ add_library(video_core STATIC create_target_directory_groups(video_core) target_link_libraries(video_core PUBLIC common core) -target_link_libraries(video_core PUBLIC glad shader_recompiler xbyak) +target_link_libraries(video_core PUBLIC glad shader_recompiler) if (YUZU_USE_BUNDLED_FFMPEG AND NOT WIN32) add_dependencies(video_core ffmpeg-build) @@ -243,8 +270,7 @@ target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS}) add_dependencies(video_core host_shaders) target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) -target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) -target_link_libraries(video_core PRIVATE sirit) +target_link_libraries(video_core PRIVATE sirit Vulkan::Headers) if (ENABLE_NSIGHT_AFTERMATH) if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK}) @@ -263,19 +289,34 @@ if (MSVC) /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data ) else() - target_compile_options(video_core PRIVATE - -Werror=conversion - -Wno-error=sign-conversion - -Werror=pessimizing-move - -Werror=redundant-move - -Werror=type-limits + if (APPLE) + # error: declaration shadows a typedef in 'interval_base_set' + # error: implicit conversion loses integer precision: 'int' to 'boost::icl::bound_type' (aka 'unsigned char') + target_compile_options(video_core PRIVATE -Wno-shadow -Wno-unused-local-typedef) + else() + target_compile_options(video_core PRIVATE -Werror=conversion) + endif() - $<$:-Werror=class-memaccess> - $<$:-Werror=unused-but-set-parameter> - $<$:-Werror=unused-but-set-variable> + target_compile_options(video_core PRIVATE + -Wno-sign-conversion ) + + # xbyak + set_source_files_properties(macro/macro_jit_x64.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-shadow") endif() if (ARCHITECTURE_x86_64) - target_link_libraries(video_core PRIVATE dynarmic) + target_sources(video_core PRIVATE + macro/macro_jit_x64.cpp + macro/macro_jit_x64.h + ) + target_link_libraries(video_core PUBLIC xbyak::xbyak) +endif() + +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) + target_link_libraries(video_core PRIVATE dynarmic::dynarmic) +endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(video_core PRIVATE precompiled_headers.h) endif() diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index f9a6472..92d77ee 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -535,7 +535,7 @@ private: const u64* const state_words = Array(); const u64 num_query_words = size / BYTES_PER_WORD + 1; const u64 word_begin = offset / BYTES_PER_WORD; - const u64 word_end = std::min(word_begin + num_query_words, NumWords()); + const u64 word_end = std::min(word_begin + num_query_words, NumWords()); const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD; for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index f015dae..502b4d9 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -20,11 +19,14 @@ #include "common/literals.h" #include "common/lru_cache.h" #include "common/microprofile.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "core/memory.h" #include "video_core/buffer_cache/buffer_base.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/dirty_flags.h" +#include "video_core/engines/draw_manager.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/memory_manager.h" @@ -56,7 +58,7 @@ using UniformBufferSizes = std::array; template -class BufferCache { +class BufferCache : public VideoCommon::ChannelSetupCaches { // Page size for caching purposes. // This is unrelated to the CPU page size and it can be changed as it seems optimal. @@ -116,10 +118,7 @@ public: static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast(4_KiB); explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, - Runtime& runtime_); + Core::Memory::Memory& cpu_memory_, Runtime& runtime_); void TickFrame(); @@ -129,7 +128,7 @@ public: void DownloadMemory(VAddr cpu_addr, u64 size); - bool InlineMemory(VAddr dest_address, size_t copy_size, std::span inlined_buffer); + bool InlineMemory(VAddr dest_address, size_t copy_size, std::span inlined_buffer); void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size); @@ -353,7 +352,7 @@ private: void NotifyBufferDeletion(); - [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const; + [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr, bool is_written = false) const; [[nodiscard]] TextureBufferBinding GetTextureBufferBinding(GPUVAddr gpu_addr, u32 size, PixelFormat format); @@ -367,9 +366,6 @@ private: void ClearDownload(IntervalType subtract_interval); VideoCore::RasterizerInterface& rasterizer; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; - Tegra::MemoryManager& gpu_memory; Core::Memory::Memory& cpu_memory; SlotVector slot_buffers; @@ -444,12 +440,8 @@ private: template BufferCache

    ::BufferCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, - Runtime& runtime_) - : runtime{runtime_}, rasterizer{rasterizer_}, maxwell3d{maxwell3d_}, - kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_} { + Core::Memory::Memory& cpu_memory_, Runtime& runtime_) + : runtime{runtime_}, rasterizer{rasterizer_}, cpu_memory{cpu_memory_} { // Ensure the first slot is used for the null buffer void(slot_buffers.insert(runtime, NullBufferParams{})); common_ranges.clear(); @@ -552,8 +544,8 @@ void BufferCache

    ::ClearDownload(IntervalType subtract_interval) { template bool BufferCache

    ::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { - const std::optional cpu_src_address = gpu_memory.GpuToCpuAddress(src_address); - const std::optional cpu_dest_address = gpu_memory.GpuToCpuAddress(dest_address); + const std::optional cpu_src_address = gpu_memory->GpuToCpuAddress(src_address); + const std::optional cpu_dest_address = gpu_memory->GpuToCpuAddress(dest_address); if (!cpu_src_address || !cpu_dest_address) { return false; } @@ -611,7 +603,7 @@ bool BufferCache

    ::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am template bool BufferCache

    ::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) { - const std::optional cpu_dst_address = gpu_memory.GpuToCpuAddress(dst_address); + const std::optional cpu_dst_address = gpu_memory->GpuToCpuAddress(dst_address); if (!cpu_dst_address) { return false; } @@ -635,7 +627,7 @@ bool BufferCache

    ::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) { template void BufferCache

    ::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) { - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); const Binding binding{ .cpu_addr = *cpu_addr, .size = size, @@ -673,9 +665,10 @@ void BufferCache

    ::BindHostGeometryBuffers(bool is_indexed) { if (is_indexed) { BindHostIndexBuffer(); } else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { - const auto& regs = maxwell3d.regs; - if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) { - runtime.BindQuadArrayIndexBuffer(regs.vertex_buffer.first, regs.vertex_buffer.count); + const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + if (draw_state.topology == Maxwell::PrimitiveTopology::Quads) { + runtime.BindQuadArrayIndexBuffer(draw_state.vertex_buffer.first, + draw_state.vertex_buffer.count); } } BindHostVertexBuffers(); @@ -733,9 +726,9 @@ void BufferCache

    ::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, enabled_storage_buffers[stage] |= 1U << ssbo_index; written_storage_buffers[stage] |= (is_written ? 1U : 0U) << ssbo_index; - const auto& cbufs = maxwell3d.state.shader_stages[stage]; + const auto& cbufs = maxwell3d->state.shader_stages[stage]; const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset; - storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr); + storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr, is_written); } template @@ -770,12 +763,12 @@ void BufferCache

    ::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, enabled_compute_storage_buffers |= 1U << ssbo_index; written_compute_storage_buffers |= (is_written ? 1U : 0U) << ssbo_index; - const auto& launch_desc = kepler_compute.launch_description; + const auto& launch_desc = kepler_compute->launch_description; ASSERT(((launch_desc.const_buffer_enable_mask >> cbuf_index) & 1) != 0); const auto& cbufs = launch_desc.const_buffer_config; const GPUVAddr ssbo_addr = cbufs[cbuf_index].Address() + cbuf_offset; - compute_storage_buffers[ssbo_index] = StorageBufferBinding(ssbo_addr); + compute_storage_buffers[ssbo_index] = StorageBufferBinding(ssbo_addr, is_written); } template @@ -836,6 +829,19 @@ void BufferCache

    ::CommitAsyncFlushesHigh() { const bool is_accuracy_normal = Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::Normal; + auto it = committed_ranges.begin(); + while (it != committed_ranges.end()) { + auto& current_intervals = *it; + auto next_it = std::next(it); + while (next_it != committed_ranges.end()) { + for (auto& interval : *next_it) { + current_intervals.subtract(interval); + } + next_it++; + } + it++; + } + boost::container::small_vector, 1> downloads; u64 total_size_bytes = 0; u64 largest_copy = 0; @@ -989,21 +995,35 @@ void BufferCache

    ::BindHostIndexBuffer() { TouchBuffer(buffer, index_buffer.buffer_id); const u32 offset = buffer.Offset(index_buffer.cpu_addr); const u32 size = index_buffer.size; - SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); + const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + if (!draw_state.inline_index_draw_indexes.empty()) { + if constexpr (USE_MEMORY_MAPS) { + auto upload_staging = runtime.UploadStagingBuffer(size); + std::array copies{ + {BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}}; + std::memcpy(upload_staging.mapped_span.data(), + draw_state.inline_index_draw_indexes.data(), size); + runtime.CopyBuffer(buffer, upload_staging.buffer, copies); + } else { + buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes); + } + } else { + SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); + } if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { - const u32 new_offset = offset + maxwell3d.regs.index_array.first * - maxwell3d.regs.index_array.FormatSizeInBytes(); + const u32 new_offset = + offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes(); runtime.BindIndexBuffer(buffer, new_offset, size); } else { - runtime.BindIndexBuffer(maxwell3d.regs.draw.topology, maxwell3d.regs.index_array.format, - maxwell3d.regs.index_array.first, maxwell3d.regs.index_array.count, + runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, + draw_state.index_buffer.first, draw_state.index_buffer.count, buffer, offset, size); } } template void BufferCache

    ::BindHostVertexBuffers() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { const Binding& binding = vertex_buffers[index]; Buffer& buffer = slot_buffers[binding.buffer_id]; @@ -1014,7 +1034,7 @@ void BufferCache

    ::BindHostVertexBuffers() { } flags[Dirty::VertexBuffer0 + index] = false; - const u32 stride = maxwell3d.regs.vertex_array[index].stride; + const u32 stride = maxwell3d->regs.vertex_streams[index].stride; const u32 offset = buffer.Offset(binding.cpu_addr); runtime.BindVertexBuffer(index, buffer, offset, binding.size, stride); } @@ -1154,7 +1174,7 @@ void BufferCache

    ::BindHostGraphicsTextureBuffers(size_t stage) { template void BufferCache

    ::BindHostTransformFeedbackBuffers() { - if (maxwell3d.regs.tfb_enabled == 0) { + if (maxwell3d->regs.transform_feedback_enabled == 0) { return; } for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { @@ -1239,16 +1259,19 @@ void BufferCache

    ::BindHostComputeTextureBuffers() { template void BufferCache

    ::DoUpdateGraphicsBuffers(bool is_indexed) { - if (is_indexed) { - UpdateIndexBuffer(); - } - UpdateVertexBuffers(); - UpdateTransformFeedbackBuffers(); - for (size_t stage = 0; stage < NUM_STAGES; ++stage) { - UpdateUniformBuffers(stage); - UpdateStorageBuffers(stage); - UpdateTextureBuffers(stage); - } + do { + has_deleted_buffers = false; + if (is_indexed) { + UpdateIndexBuffer(); + } + UpdateVertexBuffers(); + UpdateTransformFeedbackBuffers(); + for (size_t stage = 0; stage < NUM_STAGES; ++stage) { + UpdateUniformBuffers(stage); + UpdateStorageBuffers(stage); + UpdateTextureBuffers(stage); + } + } while (has_deleted_buffers); } template @@ -1262,17 +1285,26 @@ template void BufferCache

    ::UpdateIndexBuffer() { // We have to check for the dirty flags and index count // The index count is currently changed without updating the dirty flags - const auto& index_array = maxwell3d.regs.index_array; - auto& flags = maxwell3d.dirty.flags; + const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + const auto& index_array = draw_state.index_buffer; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) { return; } flags[Dirty::IndexBuffer] = false; last_index_count = index_array.count; - + if (!draw_state.inline_index_draw_indexes.empty()) { + auto inline_index_size = static_cast(draw_state.inline_index_draw_indexes.size()); + index_buffer = Binding{ + .cpu_addr = 0, + .size = inline_index_size, + .buffer_id = CreateBuffer(0, inline_index_size), + }; + return; + } const GPUVAddr gpu_addr_begin = index_array.StartAddress(); const GPUVAddr gpu_addr_end = index_array.EndAddress(); - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); const u32 address_size = static_cast(gpu_addr_end - gpu_addr_begin); const u32 draw_size = (index_array.count + index_array.first) * index_array.FormatSizeInBytes(); const u32 size = std::min(address_size, draw_size); @@ -1289,8 +1321,8 @@ void BufferCache

    ::UpdateIndexBuffer() { template void BufferCache

    ::UpdateVertexBuffers() { - auto& flags = maxwell3d.dirty.flags; - if (!maxwell3d.dirty.flags[Dirty::VertexBuffers]) { + auto& flags = maxwell3d->dirty.flags; + if (!maxwell3d->dirty.flags[Dirty::VertexBuffers]) { return; } flags[Dirty::VertexBuffers] = false; @@ -1302,33 +1334,25 @@ void BufferCache

    ::UpdateVertexBuffers() { template void BufferCache

    ::UpdateVertexBuffer(u32 index) { - if (!maxwell3d.dirty.flags[Dirty::VertexBuffer0 + index]) { + if (!maxwell3d->dirty.flags[Dirty::VertexBuffer0 + index]) { return; } - const auto& array = maxwell3d.regs.vertex_array[index]; - const auto& limit = maxwell3d.regs.vertex_array_limit[index]; - const GPUVAddr gpu_addr_begin = array.StartAddress(); - const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1; - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); - u32 address_size = static_cast(gpu_addr_end - gpu_addr_begin); - if (address_size >= 64_MiB) { - // Reported vertex buffer size is very large, cap to mapped buffer size - GPUVAddr submapped_addr_end = gpu_addr_begin; - - const auto ranges{gpu_memory.GetSubmappedRange(gpu_addr_begin, address_size)}; - if (ranges.size() > 0) { - const auto& [addr, size] = *ranges.begin(); - submapped_addr_end = addr + size; - } - - address_size = - std::min(address_size, static_cast(submapped_addr_end - gpu_addr_begin)); - } - const u32 size = address_size; // TODO: Analyze stride and number of vertices - if (array.enable == 0 || size == 0 || !cpu_addr) { + const auto& array = maxwell3d->regs.vertex_streams[index]; + const auto& limit = maxwell3d->regs.vertex_stream_limits[index]; + const GPUVAddr gpu_addr_begin = array.Address(); + const GPUVAddr gpu_addr_end = limit.Address() + 1; + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); + u32 address_size = static_cast( + std::min(gpu_addr_end - gpu_addr_begin, static_cast(std::numeric_limits::max()))); + if (array.enable == 0 || address_size == 0 || !cpu_addr) { vertex_buffers[index] = NULL_BINDING; return; } + if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { + address_size = + static_cast(gpu_memory->MaxContinousRange(gpu_addr_begin, address_size)); + } + const u32 size = address_size; // TODO: Analyze stride and number of vertices vertex_buffers[index] = Binding{ .cpu_addr = *cpu_addr, .size = size, @@ -1382,7 +1406,7 @@ void BufferCache

    ::UpdateTextureBuffers(size_t stage) { template void BufferCache

    ::UpdateTransformFeedbackBuffers() { - if (maxwell3d.regs.tfb_enabled == 0) { + if (maxwell3d->regs.transform_feedback_enabled == 0) { return; } for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { @@ -1392,11 +1416,11 @@ void BufferCache

    ::UpdateTransformFeedbackBuffers() { template void BufferCache

    ::UpdateTransformFeedbackBuffer(u32 index) { - const auto& binding = maxwell3d.regs.tfb_bindings[index]; - const GPUVAddr gpu_addr = binding.Address() + binding.buffer_offset; - const u32 size = binding.buffer_size; - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); - if (binding.buffer_enable == 0 || size == 0 || !cpu_addr) { + const auto& binding = maxwell3d->regs.transform_feedback.buffers[index]; + const GPUVAddr gpu_addr = binding.Address() + binding.start_offset; + const u32 size = binding.size; + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); + if (binding.enable == 0 || size == 0 || !cpu_addr) { transform_feedback_buffers[index] = NULL_BINDING; return; } @@ -1414,10 +1438,10 @@ void BufferCache

    ::UpdateComputeUniformBuffers() { ForEachEnabledBit(enabled_compute_uniform_buffer_mask, [&](u32 index) { Binding& binding = compute_uniform_buffers[index]; binding = NULL_BINDING; - const auto& launch_desc = kepler_compute.launch_description; + const auto& launch_desc = kepler_compute->launch_description; if (((launch_desc.const_buffer_enable_mask >> index) & 1) != 0) { const auto& cbuf = launch_desc.const_buffer_config[index]; - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(cbuf.Address()); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(cbuf.Address()); if (cpu_addr) { binding.cpu_addr = *cpu_addr; binding.size = cbuf.size; @@ -1493,6 +1517,14 @@ typename BufferCache

    ::OverlapResult BufferCache

    ::ResolveOverlaps(VAddr cpu VAddr end = cpu_addr + wanted_size; int stream_score = 0; bool has_stream_leap = false; + if (begin == 0) { + return OverlapResult{ + .ids = std::move(overlap_ids), + .begin = begin, + .end = end, + .has_stream_leap = has_stream_leap, + }; + } for (; cpu_addr >> YUZU_PAGEBITS < Common::DivCeil(end, YUZU_PAGESIZE); cpu_addr += YUZU_PAGESIZE) { const BufferId overlap_id = page_table[cpu_addr >> YUZU_PAGEBITS]; @@ -1567,6 +1599,8 @@ BufferId BufferCache

    ::CreateBuffer(VAddr cpu_addr, u32 wanted_size) { const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size); const u32 size = static_cast(overlap.end - overlap.begin); const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); + auto& new_buffer = slot_buffers[new_buffer_id]; + runtime.ClearBuffer(new_buffer, 0, new_buffer.SizeBytes(), 0); for (const BufferId overlap_id : overlap.ids) { JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); } @@ -1695,7 +1729,7 @@ void BufferCache

    ::MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, template bool BufferCache

    ::InlineMemory(VAddr dest_address, size_t copy_size, - std::span inlined_buffer) { + std::span inlined_buffer) { const bool is_dirty = IsRegionRegistered(dest_address, copy_size); if (!is_dirty) { return false; @@ -1713,12 +1747,12 @@ bool BufferCache

    ::InlineMemory(VAddr dest_address, size_t copy_size, SynchronizeBuffer(buffer, dest_address, static_cast(copy_size)); if constexpr (USE_MEMORY_MAPS) { + auto upload_staging = runtime.UploadStagingBuffer(copy_size); std::array copies{BufferCopy{ - .src_offset = 0, + .src_offset = upload_staging.offset, .dst_offset = buffer.Offset(dest_address), .size = copy_size, }}; - auto upload_staging = runtime.UploadStagingBuffer(copy_size); u8* const src_pointer = upload_staging.mapped_span.data(); std::memcpy(src_pointer, inlined_buffer.data(), copy_size); runtime.CopyBuffer(buffer, upload_staging.buffer, copies); @@ -1831,7 +1865,7 @@ void BufferCache

    ::NotifyBufferDeletion() { dirty_uniform_buffers.fill(~u32{0}); uniform_buffer_binding_sizes.fill({}); } - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; flags[Dirty::IndexBuffer] = true; flags[Dirty::VertexBuffers] = true; for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { @@ -1841,16 +1875,18 @@ void BufferCache

    ::NotifyBufferDeletion() { } template -typename BufferCache

    ::Binding BufferCache

    ::StorageBufferBinding(GPUVAddr ssbo_addr) const { - const GPUVAddr gpu_addr = gpu_memory.Read(ssbo_addr); - const u32 size = gpu_memory.Read(ssbo_addr + 8); - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); +typename BufferCache

    ::Binding BufferCache

    ::StorageBufferBinding(GPUVAddr ssbo_addr, + bool is_written) const { + const GPUVAddr gpu_addr = gpu_memory->Read(ssbo_addr); + const u32 size = gpu_memory->Read(ssbo_addr + 8); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); if (!cpu_addr || size == 0) { return NULL_BINDING; } + const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE); const Binding binding{ .cpu_addr = *cpu_addr, - .size = size, + .size = is_written ? size : static_cast(cpu_end - *cpu_addr), .buffer_id = BufferId{}, }; return binding; @@ -1859,7 +1895,7 @@ typename BufferCache

    ::Binding BufferCache

    ::StorageBufferBinding(GPUVAddr s template typename BufferCache

    ::TextureBufferBinding BufferCache

    ::GetTextureBufferBinding( GPUVAddr gpu_addr, u32 size, PixelFormat format) { - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); TextureBufferBinding binding; if (!cpu_addr || size == 0) { binding.cpu_addr = 0; diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp index 8e890a8..28a2d20 100644 --- a/src/video_core/cdma_pusher.cpp +++ b/src/video_core/cdma_pusher.cpp @@ -2,20 +2,22 @@ // SPDX-License-Identifier: MIT #include -#include "command_classes/host1x.h" -#include "command_classes/nvdec.h" -#include "command_classes/vic.h" #include "video_core/cdma_pusher.h" -#include "video_core/command_classes/sync_manager.h" #include "video_core/engines/maxwell_3d.h" -#include "video_core/gpu.h" +#include "video_core/host1x/control.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/nvdec.h" +#include "video_core/host1x/nvdec_common.h" +#include "video_core/host1x/sync_manager.h" +#include "video_core/host1x/vic.h" +#include "video_core/memory_manager.h" namespace Tegra { -CDmaPusher::CDmaPusher(GPU& gpu_) - : gpu{gpu_}, nvdec_processor(std::make_shared(gpu)), - vic_processor(std::make_unique(gpu, nvdec_processor)), - host1x_processor(std::make_unique(gpu)), - sync_manager(std::make_unique(gpu)) {} +CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_) + : host1x{host1x_}, nvdec_processor(std::make_shared(host1x)), + vic_processor(std::make_unique(host1x, nvdec_processor)), + host1x_processor(std::make_unique(host1x)), + sync_manager(std::make_unique(host1x)) {} CDmaPusher::~CDmaPusher() = default; @@ -109,16 +111,17 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) { case ThiMethod::SetMethod1: LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})", static_cast(vic_thi_state.method_0), data); - vic_processor->ProcessMethod(static_cast(vic_thi_state.method_0), data); + vic_processor->ProcessMethod(static_cast(vic_thi_state.method_0), + data); break; default: break; } break; - case ChClassId::Host1x: + case ChClassId::Control: // This device is mainly for syncpoint synchronization LOG_DEBUG(Service_NVDRV, "Host1X Class Method"); - host1x_processor->ProcessMethod(static_cast(offset), data); + host1x_processor->ProcessMethod(static_cast(offset), data); break; default: UNIMPLEMENTED_MSG("Current class not implemented {:X}", static_cast(current_class)); diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h index d6ffef9..83112df 100644 --- a/src/video_core/cdma_pusher.h +++ b/src/video_core/cdma_pusher.h @@ -12,11 +12,13 @@ namespace Tegra { -class GPU; +namespace Host1x { +class Control; class Host1x; class Nvdec; class SyncptIncrManager; class Vic; +} // namespace Host1x enum class ChSubmissionMode : u32 { SetClass = 0, @@ -30,7 +32,7 @@ enum class ChSubmissionMode : u32 { enum class ChClassId : u32 { NoClass = 0x0, - Host1x = 0x1, + Control = 0x1, VideoEncodeMpeg = 0x20, VideoEncodeNvEnc = 0x21, VideoStreamingVi = 0x30, @@ -88,7 +90,7 @@ enum class ThiMethod : u32 { class CDmaPusher { public: - explicit CDmaPusher(GPU& gpu_); + explicit CDmaPusher(Host1x::Host1x& host1x); ~CDmaPusher(); /// Process the command entry @@ -101,11 +103,11 @@ private: /// Write arguments value to the ThiRegisters member at the specified offset void ThiStateWrite(ThiRegisters& state, u32 offset, u32 argument); - GPU& gpu; - std::shared_ptr nvdec_processor; - std::unique_ptr vic_processor; - std::unique_ptr host1x_processor; - std::unique_ptr sync_manager; + Host1x::Host1x& host1x; + std::shared_ptr nvdec_processor; + std::unique_ptr vic_processor; + std::unique_ptr host1x_processor; + std::unique_ptr sync_manager; ChClassId current_class{}; ThiRegisters vic_thi_state{}; ThiRegisters nvdec_thi_state{}; diff --git a/src/video_core/command_classes/host1x.cpp b/src/video_core/command_classes/host1x.cpp deleted file mode 100644 index 11855fe..0000000 --- a/src/video_core/command_classes/host1x.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "video_core/command_classes/host1x.h" -#include "video_core/gpu.h" - -Tegra::Host1x::Host1x(GPU& gpu_) : gpu(gpu_) {} - -Tegra::Host1x::~Host1x() = default; - -void Tegra::Host1x::ProcessMethod(Method method, u32 argument) { - switch (method) { - case Method::LoadSyncptPayload32: - syncpoint_value = argument; - break; - case Method::WaitSyncpt: - case Method::WaitSyncpt32: - Execute(argument); - break; - default: - UNIMPLEMENTED_MSG("Host1x method 0x{:X}", static_cast(method)); - break; - } -} - -void Tegra::Host1x::Execute(u32 data) { - gpu.WaitFence(data, syncpoint_value); -} diff --git a/src/video_core/control/channel_state.cpp b/src/video_core/control/channel_state.cpp new file mode 100644 index 0000000..832025d --- /dev/null +++ b/src/video_core/control/channel_state.cpp @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/assert.h" +#include "video_core/control/channel_state.h" +#include "video_core/dma_pusher.h" +#include "video_core/engines/fermi_2d.h" +#include "video_core/engines/kepler_compute.h" +#include "video_core/engines/kepler_memory.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/engines/maxwell_dma.h" +#include "video_core/engines/puller.h" +#include "video_core/memory_manager.h" + +namespace Tegra::Control { + +ChannelState::ChannelState(s32 bind_id_) : bind_id{bind_id_}, initialized{} {} + +void ChannelState::Init(Core::System& system, GPU& gpu) { + ASSERT(memory_manager); + dma_pusher = std::make_unique(system, gpu, *memory_manager, *this); + maxwell_3d = std::make_unique(system, *memory_manager); + fermi_2d = std::make_unique(*memory_manager); + kepler_compute = std::make_unique(system, *memory_manager); + maxwell_dma = std::make_unique(system, *memory_manager); + kepler_memory = std::make_unique(system, *memory_manager); + initialized = true; +} + +void ChannelState::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) { + dma_pusher->BindRasterizer(rasterizer); + memory_manager->BindRasterizer(rasterizer); + maxwell_3d->BindRasterizer(rasterizer); + fermi_2d->BindRasterizer(rasterizer); + kepler_memory->BindRasterizer(rasterizer); + kepler_compute->BindRasterizer(rasterizer); + maxwell_dma->BindRasterizer(rasterizer); +} + +} // namespace Tegra::Control diff --git a/src/video_core/control/channel_state.h b/src/video_core/control/channel_state.h new file mode 100644 index 0000000..3a7b987 --- /dev/null +++ b/src/video_core/control/channel_state.h @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" + +namespace Core { +class System; +} + +namespace VideoCore { +class RasterizerInterface; +} + +namespace Tegra { + +class GPU; + +namespace Engines { +class Puller; +class Fermi2D; +class Maxwell3D; +class MaxwellDMA; +class KeplerCompute; +class KeplerMemory; +} // namespace Engines + +class MemoryManager; +class DmaPusher; + +namespace Control { + +struct ChannelState { + explicit ChannelState(s32 bind_id); + ChannelState(const ChannelState& state) = delete; + ChannelState& operator=(const ChannelState&) = delete; + ChannelState(ChannelState&& other) noexcept = default; + ChannelState& operator=(ChannelState&& other) noexcept = default; + + void Init(Core::System& system, GPU& gpu); + + void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); + + s32 bind_id = -1; + /// 3D engine + std::unique_ptr maxwell_3d; + /// 2D engine + std::unique_ptr fermi_2d; + /// Compute engine + std::unique_ptr kepler_compute; + /// DMA engine + std::unique_ptr maxwell_dma; + /// Inline memory engine + std::unique_ptr kepler_memory; + + std::shared_ptr memory_manager; + + std::unique_ptr dma_pusher; + + bool initialized{}; +}; + +} // namespace Control + +} // namespace Tegra diff --git a/src/video_core/control/channel_state_cache.cpp b/src/video_core/control/channel_state_cache.cpp new file mode 100644 index 0000000..4ebeb63 --- /dev/null +++ b/src/video_core/control/channel_state_cache.cpp @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "video_core/control/channel_state_cache.inc" + +namespace VideoCommon { + +ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state) + : maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute}, + gpu_memory{*channel_state.memory_manager} {} + +template class VideoCommon::ChannelSetupCaches; + +} // namespace VideoCommon diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h new file mode 100644 index 0000000..cdaf4f8 --- /dev/null +++ b/src/video_core/control/channel_state_cache.h @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "common/common_types.h" + +namespace Tegra { + +namespace Engines { +class Maxwell3D; +class KeplerCompute; +} // namespace Engines + +class MemoryManager; + +namespace Control { +struct ChannelState; +} + +} // namespace Tegra + +namespace VideoCommon { + +class ChannelInfo { +public: + ChannelInfo() = delete; + explicit ChannelInfo(Tegra::Control::ChannelState& state); + ChannelInfo(const ChannelInfo& state) = delete; + ChannelInfo& operator=(const ChannelInfo&) = delete; + + Tegra::Engines::Maxwell3D& maxwell3d; + Tegra::Engines::KeplerCompute& kepler_compute; + Tegra::MemoryManager& gpu_memory; +}; + +template +class ChannelSetupCaches { +public: + /// Operations for seting the channel of execution. + virtual ~ChannelSetupCaches(); + + /// Create channel state. + virtual void CreateChannel(Tegra::Control::ChannelState& channel); + + /// Bind a channel for execution. + void BindToChannel(s32 id); + + /// Erase channel's state. + void EraseChannel(s32 id); + + Tegra::MemoryManager* GetFromID(size_t id) const { + std::unique_lock lk(config_mutex); + const auto ref = address_spaces.find(id); + return ref->second.gpu_memory; + } + + std::optional getStorageID(size_t id) const { + std::unique_lock lk(config_mutex); + const auto ref = address_spaces.find(id); + if (ref == address_spaces.end()) { + return std::nullopt; + } + return ref->second.storage_id; + } + +protected: + static constexpr size_t UNSET_CHANNEL{std::numeric_limits::max()}; + + P* channel_state; + size_t current_channel_id{UNSET_CHANNEL}; + size_t current_address_space{}; + Tegra::Engines::Maxwell3D* maxwell3d; + Tegra::Engines::KeplerCompute* kepler_compute; + Tegra::MemoryManager* gpu_memory; + + std::deque

    channel_storage; + std::deque free_channel_ids; + std::unordered_map channel_map; + std::vector active_channel_ids; + struct AddresSpaceRef { + size_t ref_count; + size_t storage_id; + Tegra::MemoryManager* gpu_memory; + }; + std::unordered_map address_spaces; + mutable std::mutex config_mutex; + + virtual void OnGPUASRegister([[maybe_unused]] size_t map_id) {} +}; + +} // namespace VideoCommon diff --git a/src/video_core/control/channel_state_cache.inc b/src/video_core/control/channel_state_cache.inc new file mode 100644 index 0000000..4603138 --- /dev/null +++ b/src/video_core/control/channel_state_cache.inc @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "video_core/control/channel_state.h" +#include "video_core/control/channel_state_cache.h" +#include "video_core/engines/kepler_compute.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/memory_manager.h" + +namespace VideoCommon { + +template +ChannelSetupCaches

    ::~ChannelSetupCaches() = default; + +template +void ChannelSetupCaches

    ::CreateChannel(struct Tegra::Control::ChannelState& channel) { + std::unique_lock lk(config_mutex); + ASSERT(channel_map.find(channel.bind_id) == channel_map.end() && channel.bind_id >= 0); + auto new_id = [this, &channel]() { + if (!free_channel_ids.empty()) { + auto id = free_channel_ids.front(); + free_channel_ids.pop_front(); + new (&channel_storage[id]) P(channel); + return id; + } + channel_storage.emplace_back(channel); + return channel_storage.size() - 1; + }(); + channel_map.emplace(channel.bind_id, new_id); + if (current_channel_id != UNSET_CHANNEL) { + channel_state = &channel_storage[current_channel_id]; + } + active_channel_ids.push_back(new_id); + auto as_it = address_spaces.find(channel.memory_manager->GetID()); + if (as_it != address_spaces.end()) { + as_it->second.ref_count++; + return; + } + AddresSpaceRef new_gpu_mem_ref{ + .ref_count = 1, + .storage_id = address_spaces.size(), + .gpu_memory = channel.memory_manager.get(), + }; + address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref); + OnGPUASRegister(channel.memory_manager->GetID()); +} + +/// Bind a channel for execution. +template +void ChannelSetupCaches

    ::BindToChannel(s32 id) { + std::unique_lock lk(config_mutex); + auto it = channel_map.find(id); + ASSERT(it != channel_map.end() && id >= 0); + current_channel_id = it->second; + channel_state = &channel_storage[current_channel_id]; + maxwell3d = &channel_state->maxwell3d; + kepler_compute = &channel_state->kepler_compute; + gpu_memory = &channel_state->gpu_memory; + current_address_space = gpu_memory->GetID(); +} + +/// Erase channel's channel_state. +template +void ChannelSetupCaches

    ::EraseChannel(s32 id) { + std::unique_lock lk(config_mutex); + const auto it = channel_map.find(id); + ASSERT(it != channel_map.end() && id >= 0); + const auto this_id = it->second; + free_channel_ids.push_back(this_id); + channel_map.erase(it); + if (this_id == current_channel_id) { + current_channel_id = UNSET_CHANNEL; + channel_state = nullptr; + maxwell3d = nullptr; + kepler_compute = nullptr; + gpu_memory = nullptr; + } else if (current_channel_id != UNSET_CHANNEL) { + channel_state = &channel_storage[current_channel_id]; + } + active_channel_ids.erase( + std::find(active_channel_ids.begin(), active_channel_ids.end(), this_id)); +} + +} // namespace VideoCommon diff --git a/src/video_core/control/scheduler.cpp b/src/video_core/control/scheduler.cpp new file mode 100644 index 0000000..f7cbe20 --- /dev/null +++ b/src/video_core/control/scheduler.cpp @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "common/assert.h" +#include "video_core/control/channel_state.h" +#include "video_core/control/scheduler.h" +#include "video_core/gpu.h" + +namespace Tegra::Control { +Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {} + +Scheduler::~Scheduler() = default; + +void Scheduler::Push(s32 channel, CommandList&& entries) { + std::unique_lock lk(scheduling_guard); + auto it = channels.find(channel); + ASSERT(it != channels.end()); + auto channel_state = it->second; + gpu.BindChannel(channel_state->bind_id); + channel_state->dma_pusher->Push(std::move(entries)); + channel_state->dma_pusher->DispatchCalls(); +} + +void Scheduler::DeclareChannel(std::shared_ptr new_channel) { + s32 channel = new_channel->bind_id; + std::unique_lock lk(scheduling_guard); + channels.emplace(channel, new_channel); +} + +} // namespace Tegra::Control diff --git a/src/video_core/control/scheduler.h b/src/video_core/control/scheduler.h new file mode 100644 index 0000000..44addf6 --- /dev/null +++ b/src/video_core/control/scheduler.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "video_core/dma_pusher.h" + +namespace Tegra { + +class GPU; + +namespace Control { + +struct ChannelState; + +class Scheduler { +public: + explicit Scheduler(GPU& gpu_); + ~Scheduler(); + + void Push(s32 channel, CommandList&& entries); + + void DeclareChannel(std::shared_ptr new_channel); + +private: + std::unordered_map> channels; + std::mutex scheduling_guard; + GPU& gpu; +}; + +} // namespace Control + +} // namespace Tegra diff --git a/src/video_core/dirty_flags.cpp b/src/video_core/dirty_flags.cpp index 9dc4341..c2ecc12 100644 --- a/src/video_core/dirty_flags.cpp +++ b/src/video_core/dirty_flags.cpp @@ -17,21 +17,23 @@ using Tegra::Engines::Maxwell3D; void SetupDirtyVertexBuffers(Maxwell3D::DirtyState::Tables& tables) { static constexpr std::size_t num_array = 3; for (std::size_t i = 0; i < Maxwell3D::Regs::NumVertexArrays; ++i) { - const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]); - const std::size_t limit_offset = OFF(vertex_array_limit) + i * NUM(vertex_array_limit[0]); + const std::size_t array_offset = OFF(vertex_streams) + i * NUM(vertex_streams[0]); + const std::size_t limit_offset = + OFF(vertex_stream_limits) + i * NUM(vertex_stream_limits[0]); FillBlock(tables, array_offset, num_array, VertexBuffer0 + i, VertexBuffers); - FillBlock(tables, limit_offset, NUM(vertex_array_limit), VertexBuffer0 + i, VertexBuffers); + FillBlock(tables, limit_offset, NUM(vertex_stream_limits), VertexBuffer0 + i, + VertexBuffers); } } void SetupIndexBuffer(Maxwell3D::DirtyState::Tables& tables) { - FillBlock(tables[0], OFF(index_array), NUM(index_array), IndexBuffer); + FillBlock(tables[0], OFF(index_buffer), NUM(index_buffer), IndexBuffer); } void SetupDirtyDescriptors(Maxwell3D::DirtyState::Tables& tables) { - FillBlock(tables[0], OFF(tic), NUM(tic), Descriptors); - FillBlock(tables[0], OFF(tsc), NUM(tsc), Descriptors); + FillBlock(tables[0], OFF(tex_header), NUM(tex_header), Descriptors); + FillBlock(tables[0], OFF(tex_sampler), NUM(tex_sampler), Descriptors); } void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) { @@ -42,7 +44,7 @@ void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) { FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt); } FillBlock(tables[1], begin, num, RenderTargets); - FillBlock(tables[0], OFF(render_area), NUM(render_area), RenderTargets); + FillBlock(tables[0], OFF(surface_clip), NUM(surface_clip), RenderTargets); tables[0][OFF(rt_control)] = RenderTargets; tables[1][OFF(rt_control)] = RenderTargetControl; @@ -52,15 +54,15 @@ void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) { const u8 flag = zeta_flags[i]; auto& table = tables[i]; table[OFF(zeta_enable)] = flag; - table[OFF(zeta_width)] = flag; - table[OFF(zeta_height)] = flag; + table[OFF(zeta_size.width)] = flag; + table[OFF(zeta_size.height)] = flag; FillBlock(table, OFF(zeta), NUM(zeta), flag); } } void SetupDirtyShaders(Maxwell3D::DirtyState::Tables& tables) { - FillBlock(tables[0], OFF(shader_config[0]), - NUM(shader_config[0]) * Maxwell3D::Regs::MaxShaderProgram, Shaders); + FillBlock(tables[0], OFF(pipelines), NUM(pipelines[0]) * Maxwell3D::Regs::MaxShaderProgram, + Shaders); } } // Anonymous namespace diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 29b8582..9835e3a 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -12,7 +12,10 @@ namespace Tegra { -DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_) : gpu{gpu_}, system{system_} {} +DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, + Control::ChannelState& channel_state_) + : gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_, + *this, channel_state_} {} DmaPusher::~DmaPusher() = default; @@ -21,8 +24,6 @@ MICROPROFILE_DEFINE(DispatchCalls, "GPU", "Execute command buffer", MP_RGB(128, void DmaPusher::DispatchCalls() { MICROPROFILE_SCOPE(DispatchCalls); - gpu.SyncGuestHost(); - dma_pushbuffer_subindex = 0; dma_state.is_last_call = true; @@ -33,7 +34,6 @@ void DmaPusher::DispatchCalls() { } } gpu.FlushCommands(); - gpu.SyncGuestHost(); gpu.OnCommandListEnd(); } @@ -76,11 +76,11 @@ bool DmaPusher::Step() { // Push buffer non-empty, read a word command_headers.resize(command_list_header.size); if (Settings::IsGPULevelHigh()) { - gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(), - command_list_header.size * sizeof(u32)); + memory_manager.ReadBlock(dma_get, command_headers.data(), + command_list_header.size * sizeof(u32)); } else { - gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), - command_list_header.size * sizeof(u32)); + memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(), + command_list_header.size * sizeof(u32)); } } for (std::size_t index = 0; index < command_headers.size();) { @@ -154,7 +154,7 @@ void DmaPusher::SetState(const CommandHeader& command_header) { void DmaPusher::CallMethod(u32 argument) const { if (dma_state.method < non_puller_methods) { - gpu.CallMethod(GPU::MethodCall{ + puller.CallPullerMethod(Engines::Puller::MethodCall{ dma_state.method, argument, dma_state.subchannel, @@ -168,12 +168,16 @@ void DmaPusher::CallMethod(u32 argument) const { void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const { if (dma_state.method < non_puller_methods) { - gpu.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, - dma_state.method_count); + puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, + dma_state.method_count); } else { subchannels[dma_state.subchannel]->CallMultiMethod(dma_state.method, base_start, num_methods, dma_state.method_count); } } +void DmaPusher::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) { + puller.BindRasterizer(rasterizer); +} + } // namespace Tegra diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index 872fd14..938f0f1 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h @@ -10,6 +10,7 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "video_core/engines/engine_interface.h" +#include "video_core/engines/puller.h" namespace Core { class System; @@ -17,7 +18,12 @@ class System; namespace Tegra { +namespace Control { +struct ChannelState; +} + class GPU; +class MemoryManager; enum class SubmissionMode : u32 { IncreasingOld = 0, @@ -31,24 +37,32 @@ enum class SubmissionMode : u32 { // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence // their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4. // So the values you see in docs might be multiplied by 4. +// Register documentation: +// https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/classes/host/cla26f.h +// +// Register Description (approx): +// https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/manuals/volta/gv100/dev_pbdma.ref.txt enum class BufferMethods : u32 { BindObject = 0x0, + Illegal = 0x1, Nop = 0x2, SemaphoreAddressHigh = 0x4, SemaphoreAddressLow = 0x5, - SemaphoreSequence = 0x6, - SemaphoreTrigger = 0x7, - NotifyIntr = 0x8, + SemaphoreSequencePayload = 0x6, + SemaphoreOperation = 0x7, + NonStallInterrupt = 0x8, WrcacheFlush = 0x9, - Unk28 = 0xA, - UnkCacheFlush = 0xB, + MemOpA = 0xA, + MemOpB = 0xB, + MemOpC = 0xC, + MemOpD = 0xD, RefCnt = 0x14, SemaphoreAcquire = 0x1A, SemaphoreRelease = 0x1B, - FenceValue = 0x1C, - FenceAction = 0x1D, - WaitForInterrupt = 0x1E, - Unk7c = 0x1F, + SyncpointPayload = 0x1C, + SyncpointOperation = 0x1D, + WaitForIdle = 0x1E, + CRCCheck = 0x1F, Yield = 0x20, NonPullerMethods = 0x40, }; @@ -102,7 +116,8 @@ struct CommandList final { */ class DmaPusher final { public: - explicit DmaPusher(Core::System& system_, GPU& gpu_); + explicit DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, + Control::ChannelState& channel_state_); ~DmaPusher(); void Push(CommandList&& entries) { @@ -115,6 +130,8 @@ public: subchannels[subchannel_id] = engine; } + void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); + private: static constexpr u32 non_puller_methods = 0x40; static constexpr u32 max_subchannels = 8; @@ -148,6 +165,8 @@ private: GPU& gpu; Core::System& system; + MemoryManager& memory_manager; + mutable Engines::Puller puller; }; } // namespace Tegra diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp new file mode 100644 index 0000000..3a78421 --- /dev/null +++ b/src/video_core/engines/draw_manager.cpp @@ -0,0 +1,201 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/dirty_flags.h" +#include "video_core/engines/draw_manager.h" +#include "video_core/rasterizer_interface.h" + +namespace Tegra::Engines { +DrawManager::DrawManager(Maxwell3D* maxwell3d_) : maxwell3d(maxwell3d_) {} + +void DrawManager::ProcessMethodCall(u32 method, u32 argument) { + const auto& regs{maxwell3d->regs}; + switch (method) { + case MAXWELL3D_REG_INDEX(clear_surface): + return Clear(1); + case MAXWELL3D_REG_INDEX(draw.begin): + return DrawBegin(); + case MAXWELL3D_REG_INDEX(draw.end): + return DrawEnd(); + case MAXWELL3D_REG_INDEX(vertex_buffer.first): + case MAXWELL3D_REG_INDEX(vertex_buffer.count): + case MAXWELL3D_REG_INDEX(index_buffer.first): + break; + case MAXWELL3D_REG_INDEX(index_buffer.count): + draw_state.draw_indexed = true; + break; + case MAXWELL3D_REG_INDEX(index_buffer32_subsequent): + case MAXWELL3D_REG_INDEX(index_buffer16_subsequent): + case MAXWELL3D_REG_INDEX(index_buffer8_subsequent): + draw_state.instance_count++; + [[fallthrough]]; + case MAXWELL3D_REG_INDEX(index_buffer32_first): + case MAXWELL3D_REG_INDEX(index_buffer16_first): + case MAXWELL3D_REG_INDEX(index_buffer8_first): + return DrawIndexSmall(argument); + case MAXWELL3D_REG_INDEX(draw_inline_index): + SetInlineIndexBuffer(argument); + break; + case MAXWELL3D_REG_INDEX(inline_index_2x16.even): + SetInlineIndexBuffer(regs.inline_index_2x16.even); + SetInlineIndexBuffer(regs.inline_index_2x16.odd); + break; + case MAXWELL3D_REG_INDEX(inline_index_4x8.index0): + SetInlineIndexBuffer(regs.inline_index_4x8.index0); + SetInlineIndexBuffer(regs.inline_index_4x8.index1); + SetInlineIndexBuffer(regs.inline_index_4x8.index2); + SetInlineIndexBuffer(regs.inline_index_4x8.index3); + break; + case MAXWELL3D_REG_INDEX(vertex_array_instance_first): + case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): { + LOG_WARNING(HW_GPU, "(STUBBED) called"); + break; + } + default: + break; + } +} + +void DrawManager::Clear(u32 layer_count) { + if (maxwell3d->ShouldExecute()) { + maxwell3d->rasterizer->Clear(layer_count); + } +} + +void DrawManager::DrawDeferred() { + if (draw_state.draw_mode != DrawMode::Instance || draw_state.instance_count == 0) { + return; + } + DrawEnd(draw_state.instance_count + 1, true); + draw_state.instance_count = 0; +} + +void DrawManager::DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, + u32 base_instance, u32 num_instances) { + draw_state.topology = topology; + draw_state.vertex_buffer.first = vertex_first; + draw_state.vertex_buffer.count = vertex_count; + draw_state.base_instance = base_instance; + ProcessDraw(false, num_instances); +} + +void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, + u32 base_index, u32 base_instance, u32 num_instances) { + const auto& regs{maxwell3d->regs}; + draw_state.topology = topology; + draw_state.index_buffer = regs.index_buffer; + draw_state.index_buffer.first = index_first; + draw_state.index_buffer.count = index_count; + draw_state.base_index = base_index; + draw_state.base_instance = base_instance; + ProcessDraw(true, num_instances); +} + +void DrawManager::SetInlineIndexBuffer(u32 index) { + draw_state.inline_index_draw_indexes.push_back(static_cast(index & 0x000000ff)); + draw_state.inline_index_draw_indexes.push_back(static_cast((index & 0x0000ff00) >> 8)); + draw_state.inline_index_draw_indexes.push_back(static_cast((index & 0x00ff0000) >> 16)); + draw_state.inline_index_draw_indexes.push_back(static_cast((index & 0xff000000) >> 24)); + draw_state.draw_mode = DrawMode::InlineIndex; +} + +void DrawManager::DrawBegin() { + const auto& regs{maxwell3d->regs}; + auto reset_instance_count = regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First; + auto increment_instance_count = + regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent; + if (reset_instance_count) { + DrawDeferred(); + draw_state.instance_count = 0; + draw_state.draw_mode = DrawMode::General; + } else if (increment_instance_count) { + draw_state.instance_count++; + draw_state.draw_mode = DrawMode::Instance; + } + + draw_state.topology = regs.draw.topology; +} + +void DrawManager::DrawEnd(u32 instance_count, bool force_draw) { + const auto& regs{maxwell3d->regs}; + switch (draw_state.draw_mode) { + case DrawMode::Instance: + if (!force_draw) { + break; + } + [[fallthrough]]; + case DrawMode::General: + draw_state.base_instance = regs.global_base_instance_index; + draw_state.base_index = regs.global_base_vertex_index; + if (draw_state.draw_indexed) { + draw_state.index_buffer = regs.index_buffer; + ProcessDraw(true, instance_count); + } else { + draw_state.vertex_buffer = regs.vertex_buffer; + ProcessDraw(false, instance_count); + } + draw_state.draw_indexed = false; + break; + case DrawMode::InlineIndex: + draw_state.base_instance = regs.global_base_instance_index; + draw_state.base_index = regs.global_base_vertex_index; + draw_state.index_buffer = regs.index_buffer; + draw_state.index_buffer.count = + static_cast(draw_state.inline_index_draw_indexes.size() / 4); + draw_state.index_buffer.format = Maxwell3D::Regs::IndexFormat::UnsignedInt; + ProcessDraw(true, instance_count); + draw_state.inline_index_draw_indexes.clear(); + break; + } +} + +void DrawManager::DrawIndexSmall(u32 argument) { + const auto& regs{maxwell3d->regs}; + IndexBufferSmall index_small_params{argument}; + draw_state.base_instance = regs.global_base_instance_index; + draw_state.base_index = regs.global_base_vertex_index; + draw_state.index_buffer = regs.index_buffer; + draw_state.index_buffer.first = index_small_params.first; + draw_state.index_buffer.count = index_small_params.count; + draw_state.topology = index_small_params.topology; + maxwell3d->dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + ProcessDraw(true, 1); +} + +void DrawManager::UpdateTopology() { + const auto& regs{maxwell3d->regs}; + switch (regs.primitive_topology_control) { + case PrimitiveTopologyControl::UseInBeginMethods: + break; + case PrimitiveTopologyControl::UseSeparateState: + switch (regs.topology_override) { + case PrimitiveTopologyOverride::None: + break; + case PrimitiveTopologyOverride::Points: + draw_state.topology = PrimitiveTopology::Points; + break; + case PrimitiveTopologyOverride::Lines: + draw_state.topology = PrimitiveTopology::Lines; + break; + case PrimitiveTopologyOverride::LineStrip: + draw_state.topology = PrimitiveTopology::LineStrip; + break; + default: + draw_state.topology = static_cast(regs.topology_override); + break; + } + break; + } +} + +void DrawManager::ProcessDraw(bool draw_indexed, u32 instance_count) { + LOG_TRACE(HW_GPU, "called, topology={}, count={}", draw_state.topology, + draw_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count); + + UpdateTopology(); + + if (maxwell3d->ShouldExecute()) { + maxwell3d->rasterizer->Draw(draw_indexed, instance_count); + } +} +} // namespace Tegra::Engines diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h new file mode 100644 index 0000000..0e6930a --- /dev/null +++ b/src/video_core/engines/draw_manager.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include "common/common_types.h" +#include "video_core/engines/maxwell_3d.h" + +namespace VideoCore { +class RasterizerInterface; +} + +namespace Tegra::Engines { +using PrimitiveTopologyControl = Maxwell3D::Regs::PrimitiveTopologyControl; +using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology; +using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride; +using IndexBuffer = Maxwell3D::Regs::IndexBuffer; +using VertexBuffer = Maxwell3D::Regs::VertexBuffer; +using IndexBufferSmall = Maxwell3D::Regs::IndexBufferSmall; + +class DrawManager { +public: + enum class DrawMode : u32 { General = 0, Instance, InlineIndex }; + struct State { + PrimitiveTopology topology{}; + DrawMode draw_mode{}; + bool draw_indexed{}; + u32 base_index{}; + VertexBuffer vertex_buffer; + IndexBuffer index_buffer; + u32 base_instance{}; + u32 instance_count{}; + std::vector inline_index_draw_indexes; + }; + + explicit DrawManager(Maxwell3D* maxwell_3d); + + void ProcessMethodCall(u32 method, u32 argument); + + void Clear(u32 layer_count); + + void DrawDeferred(); + + void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, + u32 base_instance, u32 num_instances); + + void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, + u32 base_instance, u32 num_instances); + + const State& GetDrawState() const { + return draw_state; + } + +private: + void SetInlineIndexBuffer(u32 index); + + void DrawBegin(); + + void DrawEnd(u32 instance_count = 1, bool force_draw = false); + + void DrawIndexSmall(u32 argument); + + void UpdateTopology(); + + void ProcessDraw(bool draw_indexed, u32 instance_count); + + Maxwell3D* maxwell3d{}; + State draw_state{}; +}; +} // namespace Tegra::Engines diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp index 6ff5b1e..e4f8331 100644 --- a/src/video_core/engines/engine_upload.cpp +++ b/src/video_core/engines/engine_upload.cpp @@ -3,6 +3,7 @@ #include +#include "common/algorithm.h" #include "common/assert.h" #include "video_core/engines/engine_upload.h" #include "video_core/memory_manager.h" @@ -34,21 +35,47 @@ void State::ProcessData(const u32 data, const bool is_last_call) { if (!is_last_call) { return; } + ProcessData(inner_buffer); +} + +void State::ProcessData(const u32* data, size_t num_data) { + std::span read_buffer(reinterpret_cast(data), num_data * sizeof(u32)); + ProcessData(read_buffer); +} + +void State::ProcessData(std::span read_buffer) { const GPUVAddr address{regs.dest.Address()}; if (is_linear) { - rasterizer->AccelerateInlineToMemory(address, copy_size, inner_buffer); + if (regs.line_count == 1) { + rasterizer->AccelerateInlineToMemory(address, copy_size, read_buffer); + } else { + for (size_t line = 0; line < regs.line_count; ++line) { + const GPUVAddr dest_line = address + line * regs.dest.pitch; + std::span buffer(read_buffer.data() + line * regs.line_length_in, + regs.line_length_in); + rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer); + } + } } else { - UNIMPLEMENTED_IF(regs.dest.z != 0); - UNIMPLEMENTED_IF(regs.dest.depth != 1); - UNIMPLEMENTED_IF(regs.dest.BlockWidth() != 0); - UNIMPLEMENTED_IF(regs.dest.BlockDepth() != 0); + u32 width = regs.dest.width; + u32 x_elements = regs.line_length_in; + u32 x_offset = regs.dest.x; + const u32 bpp_shift = Common::FoldRight( + 4U, [](u32 x, u32 y) { return std::min(x, static_cast(std::countr_zero(y))); }, + width, x_elements, x_offset, static_cast(address)); + width >>= bpp_shift; + x_elements >>= bpp_shift; + x_offset >>= bpp_shift; + const u32 bytes_per_pixel = 1U << bpp_shift; const std::size_t dst_size = Tegra::Texture::CalculateSize( - true, 1, regs.dest.width, regs.dest.height, 1, regs.dest.BlockHeight(), 0); + true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth, + regs.dest.BlockHeight(), regs.dest.BlockDepth()); tmp_buffer.resize(dst_size); memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); - Tegra::Texture::SwizzleKepler(regs.dest.width, regs.dest.height, regs.dest.x, regs.dest.y, - regs.dest.BlockHeight(), copy_size, inner_buffer.data(), - tmp_buffer.data()); + Tegra::Texture::SwizzleSubrect(tmp_buffer, read_buffer, bytes_per_pixel, width, + regs.dest.height, regs.dest.depth, x_offset, regs.dest.y, + x_elements, regs.line_count, regs.dest.BlockHeight(), + regs.dest.BlockDepth(), regs.line_length_in); memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size); } } diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h index 94ff331..94fafd9 100644 --- a/src/video_core/engines/engine_upload.h +++ b/src/video_core/engines/engine_upload.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include "common/bit_field.h" #include "common/common_types.h" @@ -33,12 +34,12 @@ struct Registers { u32 width; u32 height; u32 depth; - u32 z; + u32 layer; u32 x; u32 y; GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | address_low); + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; } u32 BlockWidth() const { @@ -62,11 +63,14 @@ public: void ProcessExec(bool is_linear_); void ProcessData(u32 data, bool is_last_call); + void ProcessData(const u32* data, size_t num_data); /// Binds a rasterizer to this engine. void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); private: + void ProcessData(std::span read_buffer); + u32 write_offset = 0; u32 copy_size = 0; std::vector inner_buffer; diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 453e0fb..c6478ae 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -3,17 +3,25 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/microprofile.h" #include "video_core/engines/fermi_2d.h" -#include "video_core/memory_manager.h" +#include "video_core/engines/sw_blitter/blitter.h" #include "video_core/rasterizer_interface.h" #include "video_core/surface.h" +#include "video_core/textures/decoders.h" + +MICROPROFILE_DECLARE(GPU_BlitEngine); +MICROPROFILE_DEFINE(GPU_BlitEngine, "GPU", "Blit Engine", MP_RGB(224, 224, 128)); using VideoCore::Surface::BytesPerBlock; using VideoCore::Surface::PixelFormatFromRenderTargetFormat; namespace Tegra::Engines { -Fermi2D::Fermi2D() { +using namespace Texture; + +Fermi2D::Fermi2D(MemoryManager& memory_manager_) { + sw_blitter = std::make_unique(memory_manager_); // Nvidia's OpenGL driver seems to assume these values regs.src.depth = 1; regs.dst.depth = 1; @@ -42,6 +50,7 @@ void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 } void Fermi2D::Blit() { + MICROPROFILE_SCOPE(GPU_BlitEngine); LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", regs.src.Address(), regs.dst.Address()); @@ -52,9 +61,16 @@ void Fermi2D::Blit() { UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled"); const auto& args = regs.pixels_from_memory; + constexpr s64 null_derivate = 1ULL << 32; + Surface src = regs.src; + const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); + const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 && + src.format != regs.dst.format; Config config{ .operation = regs.operation, .filter = args.sample_mode.filter, + .must_accelerate = + args.du_dx != null_derivate || args.dv_dy != null_derivate || delegate_to_gpu, .dst_x0 = args.dst_x0, .dst_y0 = args.dst_y0, .dst_x1 = args.dst_x0 + args.dst_width, @@ -64,8 +80,7 @@ void Fermi2D::Blit() { .src_x1 = static_cast((args.du_dx * args.dst_width + args.src_x0) >> 32), .src_y1 = static_cast((args.dv_dy * args.dst_height + args.src_y0) >> 32), }; - Surface src = regs.src; - const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); + const auto need_align_to_pitch = src.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch && static_cast(src.width) == config.src_x1 && @@ -78,8 +93,9 @@ void Fermi2D::Blit() { config.src_x1 -= config.src_x0; config.src_x0 = 0; } + if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) { - UNIMPLEMENTED(); + sw_blitter->Blit(src, regs.dst, config); } } diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index 1229aa3..100b21b 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h @@ -5,6 +5,7 @@ #include #include +#include #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" @@ -21,6 +22,10 @@ class RasterizerInterface; namespace Tegra::Engines { +namespace Blitter { +class SoftwareBlitEngine; +} + /** * This Engine is known as G80_2D. Documentation can be found in: * https://github.com/envytools/envytools/blob/master/rnndb/graph/g80_2d.xml @@ -32,7 +37,7 @@ namespace Tegra::Engines { class Fermi2D final : public EngineInterface { public: - explicit Fermi2D(); + explicit Fermi2D(MemoryManager& memory_manager_); ~Fermi2D() override; /// Binds a rasterizer to this engine. @@ -92,7 +97,7 @@ public: u32 addr_lower; [[nodiscard]] constexpr GPUVAddr Address() const noexcept { - return (static_cast(addr_upper) << 32) | static_cast(addr_lower); + return (GPUVAddr{addr_upper} << 32) | GPUVAddr{addr_lower}; } }; static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size"); @@ -286,6 +291,7 @@ public: struct Config { Operation operation; Filter filter; + bool must_accelerate; s32 dst_x0; s32 dst_y0; s32 dst_x1; @@ -298,6 +304,7 @@ public: private: VideoCore::RasterizerInterface* rasterizer = nullptr; + std::unique_ptr sw_blitter; /// Performs the copy from the source surface to the destination surface as configured in the /// registers. diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 5db254d..e5c6221 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -36,8 +36,6 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal } case KEPLER_COMPUTE_REG_INDEX(data_upload): { upload_state.ProcessData(method_argument, is_last_call); - if (is_last_call) { - } break; } case KEPLER_COMPUTE_REG_INDEX(launch): @@ -50,8 +48,15 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) { - for (std::size_t i = 0; i < amount; i++) { - CallMethod(method, base_start[i], methods_pending - static_cast(i) <= 1); + switch (method) { + case KEPLER_COMPUTE_REG_INDEX(data_upload): + upload_state.ProcessData(base_start, amount); + return; + default: + for (u32 i = 0; i < amount; i++) { + CallMethod(method, base_start[i], methods_pending - i <= 1); + } + break; } } diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index aab309e..e154e3f 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h @@ -68,7 +68,7 @@ public: struct { u32 address; GPUVAddr Address() const { - return static_cast((static_cast(address) << 8)); + return GPUVAddr{address} << 8; } } launch_desc_loc; @@ -83,8 +83,7 @@ public: u32 address_low; u32 limit; GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; } } tsc; @@ -95,8 +94,7 @@ public: u32 address_low; u32 limit; GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; } } tic; @@ -106,8 +104,7 @@ public: u32 address_high; u32 address_low; GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; } } code_loc; @@ -162,8 +159,7 @@ public: BitField<15, 17, u32> size; }; GPUVAddr Address() const { - return static_cast((static_cast(address_high.Value()) << 32) | - address_low); + return (GPUVAddr{address_high.Value()} << 32) | GPUVAddr{address_low}; } }; std::array const_buffer_config; diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index e2b0295..08045d1 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp @@ -33,8 +33,6 @@ void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call } case KEPLERMEMORY_REG_INDEX(data): { upload_state.ProcessData(method_argument, is_last_call); - if (is_last_call) { - } break; } } @@ -42,8 +40,15 @@ void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call void KeplerMemory::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) { - for (std::size_t i = 0; i < amount; i++) { - CallMethod(method, base_start[i], methods_pending - static_cast(i) <= 1); + switch (method) { + case KEPLERMEMORY_REG_INDEX(data): + upload_state.ProcessData(base_start, amount); + return; + default: + for (u32 i = 0; i < amount; i++) { + CallMethod(method, base_start[i], methods_pending - i <= 1); + } + break; } } diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 3a46462..9b182b6 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -7,6 +7,7 @@ #include "core/core.h" #include "core/core_timing.h" #include "video_core/dirty_flags.h" +#include "video_core/engines/draw_manager.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/gpu.h" #include "video_core/memory_manager.h" @@ -21,8 +22,10 @@ using VideoCore::QueryType; constexpr u32 MacroRegistersStart = 0xE00; Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_) - : system{system_}, memory_manager{memory_manager_}, macro_engine{GetMacroEngine(*this)}, - upload_state{memory_manager, regs.upload} { + : draw_manager{std::make_unique(this)}, system{system_}, + memory_manager{memory_manager_}, macro_engine{GetMacroEngine(*this)}, upload_state{ + memory_manager, + regs.upload} { dirty.flags.flip(); InitializeRegisterDefaults(); } @@ -56,37 +59,37 @@ void Maxwell3D::InitializeRegisterDefaults() { // Doom and Bomberman seems to use the uninitialized registers and just enable blend // so initialize blend registers with sane values - regs.blend.equation_rgb = Regs::Blend::Equation::Add; - regs.blend.factor_source_rgb = Regs::Blend::Factor::One; - regs.blend.factor_dest_rgb = Regs::Blend::Factor::Zero; - regs.blend.equation_a = Regs::Blend::Equation::Add; - regs.blend.factor_source_a = Regs::Blend::Factor::One; - regs.blend.factor_dest_a = Regs::Blend::Factor::Zero; - for (auto& blend : regs.independent_blend) { - blend.equation_rgb = Regs::Blend::Equation::Add; - blend.factor_source_rgb = Regs::Blend::Factor::One; - blend.factor_dest_rgb = Regs::Blend::Factor::Zero; - blend.equation_a = Regs::Blend::Equation::Add; - blend.factor_source_a = Regs::Blend::Factor::One; - blend.factor_dest_a = Regs::Blend::Factor::Zero; + regs.blend.color_op = Regs::Blend::Equation::Add_D3D; + regs.blend.color_source = Regs::Blend::Factor::One_D3D; + regs.blend.color_dest = Regs::Blend::Factor::Zero_D3D; + regs.blend.alpha_op = Regs::Blend::Equation::Add_D3D; + regs.blend.alpha_source = Regs::Blend::Factor::One_D3D; + regs.blend.alpha_dest = Regs::Blend::Factor::Zero_D3D; + for (auto& blend : regs.blend_per_target) { + blend.color_op = Regs::Blend::Equation::Add_D3D; + blend.color_source = Regs::Blend::Factor::One_D3D; + blend.color_dest = Regs::Blend::Factor::Zero_D3D; + blend.alpha_op = Regs::Blend::Equation::Add_D3D; + blend.alpha_source = Regs::Blend::Factor::One_D3D; + blend.alpha_dest = Regs::Blend::Factor::Zero_D3D; } - regs.stencil_front_op_fail = Regs::StencilOp::Keep; - regs.stencil_front_op_zfail = Regs::StencilOp::Keep; - regs.stencil_front_op_zpass = Regs::StencilOp::Keep; - regs.stencil_front_func_func = Regs::ComparisonOp::Always; + regs.stencil_front_op.fail = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_front_op.zfail = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_front_op.zpass = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_front_op.func = Regs::ComparisonOp::Always_GL; regs.stencil_front_func_mask = 0xFFFFFFFF; regs.stencil_front_mask = 0xFFFFFFFF; regs.stencil_two_side_enable = 1; - regs.stencil_back_op_fail = Regs::StencilOp::Keep; - regs.stencil_back_op_zfail = Regs::StencilOp::Keep; - regs.stencil_back_op_zpass = Regs::StencilOp::Keep; - regs.stencil_back_func_func = Regs::ComparisonOp::Always; + regs.stencil_back_op.fail = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_back_op.zfail = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_back_op.zpass = Regs::StencilOp::Op::Keep_D3D; + regs.stencil_back_op.func = Regs::ComparisonOp::Always_GL; regs.stencil_back_func_mask = 0xFFFFFFFF; regs.stencil_back_mask = 0xFFFFFFFF; - regs.depth_test_func = Regs::ComparisonOp::Always; - regs.front_face = Regs::FrontFace::CounterClockWise; - regs.cull_face = Regs::CullFace::Back; + regs.depth_test_func = Regs::ComparisonOp::Always_GL; + regs.gl_front_face = Regs::FrontFace::CounterClockWise; + regs.gl_cull_face = Regs::CullFace::Back; // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a // register carrying a default value. Assume it's OpenGL's default (1). @@ -107,20 +110,15 @@ void Maxwell3D::InitializeRegisterDefaults() { // NVN games expect these values to be enabled at boot regs.rasterize_enable = 1; - regs.rt_separate_frag_data = 1; + regs.color_target_mrt_enable = 1; regs.framebuffer_srgb = 1; regs.line_width_aliased = 1.0f; regs.line_width_smooth = 1.0f; - regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise; + regs.gl_front_face = Maxwell3D::Regs::FrontFace::ClockWise; regs.polygon_mode_back = Maxwell3D::Regs::PolygonMode::Fill; regs.polygon_mode_front = Maxwell3D::Regs::PolygonMode::Fill; shadow_state = regs; - - mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true; - mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true; - mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true; - mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; } void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { @@ -173,77 +171,62 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume case MAXWELL3D_REG_INDEX(shadow_ram_control): shadow_state.shadow_ram_control = static_cast(nonshadow_argument); return; - case MAXWELL3D_REG_INDEX(macros.upload_address): - return macro_engine->ClearCode(regs.macros.upload_address); - case MAXWELL3D_REG_INDEX(macros.data): - return macro_engine->AddCode(regs.macros.upload_address, argument); - case MAXWELL3D_REG_INDEX(macros.bind): + case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr): + return macro_engine->ClearCode(regs.load_mme.instruction_ptr); + case MAXWELL3D_REG_INDEX(load_mme.instruction): + return macro_engine->AddCode(regs.load_mme.instruction_ptr, argument); + case MAXWELL3D_REG_INDEX(load_mme.start_address): return ProcessMacroBind(argument); - case MAXWELL3D_REG_INDEX(firmware[4]): + case MAXWELL3D_REG_INDEX(falcon[4]): return ProcessFirmwareCall4(); - case MAXWELL3D_REG_INDEX(const_buffer.cb_data): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: + case MAXWELL3D_REG_INDEX(const_buffer.buffer): + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15: return ProcessCBData(argument); - case MAXWELL3D_REG_INDEX(cb_bind[0]): + case MAXWELL3D_REG_INDEX(bind_groups[0].raw_config): return ProcessCBBind(0); - case MAXWELL3D_REG_INDEX(cb_bind[1]): + case MAXWELL3D_REG_INDEX(bind_groups[1].raw_config): return ProcessCBBind(1); - case MAXWELL3D_REG_INDEX(cb_bind[2]): + case MAXWELL3D_REG_INDEX(bind_groups[2].raw_config): return ProcessCBBind(2); - case MAXWELL3D_REG_INDEX(cb_bind[3]): + case MAXWELL3D_REG_INDEX(bind_groups[3].raw_config): return ProcessCBBind(3); - case MAXWELL3D_REG_INDEX(cb_bind[4]): + case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config): return ProcessCBBind(4); - case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): - return DrawArrays(); - case MAXWELL3D_REG_INDEX(small_index): - regs.index_array.count = regs.small_index.count; - regs.index_array.first = regs.small_index.first; - dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; - return DrawArrays(); - case MAXWELL3D_REG_INDEX(small_index_2): - regs.index_array.count = regs.small_index_2.count; - regs.index_array.first = regs.small_index_2.first; - dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; - return DrawArrays(); - case MAXWELL3D_REG_INDEX(topology_override): - use_topology_override = true; - return; - case MAXWELL3D_REG_INDEX(clear_buffers): - return ProcessClearBuffers(); - case MAXWELL3D_REG_INDEX(query.query_get): + case MAXWELL3D_REG_INDEX(report_semaphore.query): return ProcessQueryGet(); - case MAXWELL3D_REG_INDEX(condition.mode): + case MAXWELL3D_REG_INDEX(render_enable.mode): return ProcessQueryCondition(); - case MAXWELL3D_REG_INDEX(counter_reset): + case MAXWELL3D_REG_INDEX(clear_report_value): return ProcessCounterReset(); case MAXWELL3D_REG_INDEX(sync_info): return ProcessSyncPoint(); - case MAXWELL3D_REG_INDEX(exec_upload): - return upload_state.ProcessExec(regs.exec_upload.linear != 0); - case MAXWELL3D_REG_INDEX(data_upload): + case MAXWELL3D_REG_INDEX(launch_dma): + return upload_state.ProcessExec(regs.launch_dma.memory_layout.Value() == + Regs::LaunchDMA::Layout::Pitch); + case MAXWELL3D_REG_INDEX(inline_data): upload_state.ProcessData(argument, is_last_call); - if (is_last_call) { - } return; case MAXWELL3D_REG_INDEX(fragment_barrier): return rasterizer->FragmentBarrier(); case MAXWELL3D_REG_INDEX(tiled_cache_barrier): return rasterizer->TiledCacheBarrier(); + default: + draw_manager->ProcessMethodCall(method, argument); + break; } } @@ -257,14 +240,13 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector& parameters) // Execute the current macro. macro_engine->Execute(macro_positions[entry], parameters); - if (mme_draw.current_mode != MMEDrawMode::Undefined) { - FlushMMEInlineDraw(); - } + + draw_manager->DrawDeferred(); } void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { - // It is an error to write to a register other than the current macro's ARG register before it - // has finished execution. + // It is an error to write to a register other than the current macro's ARG register before + // it has finished execution. if (executing_macro != 0) { ASSERT(method == executing_macro + 1); } @@ -281,6 +263,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { const u32 argument = ProcessShadowRam(method, method_argument); ProcessDirtyRegisters(method, argument); + ProcessMethodCall(method, argument, method_argument, is_last_call); } @@ -293,149 +276,41 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, return; } switch (method) { - case MAXWELL3D_REG_INDEX(const_buffer.cb_data): - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: - case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: + case MAXWELL3D_REG_INDEX(const_buffer.buffer): + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14: + case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15: ProcessCBMultiData(base_start, amount); break; - default: - for (std::size_t i = 0; i < amount; i++) { - CallMethod(method, base_start[i], methods_pending - static_cast(i) <= 1); - } - break; - } -} - -void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) { - if (mme_draw.current_mode == MMEDrawMode::Undefined) { - if (mme_draw.gl_begin_consume) { - mme_draw.current_mode = expected_mode; - mme_draw.current_count = count; - mme_draw.instance_count = 1; - mme_draw.gl_begin_consume = false; - mme_draw.gl_end_count = 0; - } + case MAXWELL3D_REG_INDEX(inline_data): + upload_state.ProcessData(base_start, amount); return; - } else { - if (mme_draw.current_mode == expected_mode && count == mme_draw.current_count && - mme_draw.instance_mode && mme_draw.gl_begin_consume) { - mme_draw.instance_count++; - mme_draw.gl_begin_consume = false; - return; - } else { - FlushMMEInlineDraw(); - } - } - // Tail call in case it needs to retry. - StepInstance(expected_mode, count); -} - -void Maxwell3D::CallMethodFromMME(u32 method, u32 method_argument) { - if (mme_inline[method]) { - regs.reg_array[method] = method_argument; - if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) || - method == MAXWELL3D_REG_INDEX(index_array.count)) { - const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count) - ? MMEDrawMode::Array - : MMEDrawMode::Indexed; - StepInstance(expected_mode, method_argument); - } else if (method == MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)) { - mme_draw.instance_mode = - (regs.draw.instance_next != 0) || (regs.draw.instance_cont != 0); - mme_draw.gl_begin_consume = true; - } else { - mme_draw.gl_end_count++; - } - } else { - if (mme_draw.current_mode != MMEDrawMode::Undefined) { - FlushMMEInlineDraw(); - } - CallMethod(method, method_argument, true); - } -} - -void Maxwell3D::ProcessTopologyOverride() { - using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology; - using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride; - - PrimitiveTopology topology{}; - - switch (regs.topology_override) { - case PrimitiveTopologyOverride::None: - topology = regs.draw.topology; - break; - case PrimitiveTopologyOverride::Points: - topology = PrimitiveTopology::Points; - break; - case PrimitiveTopologyOverride::Lines: - topology = PrimitiveTopology::Lines; - break; - case PrimitiveTopologyOverride::LineStrip: - topology = PrimitiveTopology::LineStrip; - break; default: - topology = static_cast(regs.topology_override); + for (u32 i = 0; i < amount; i++) { + CallMethod(method, base_start[i], methods_pending - i <= 1); + } break; } - - if (use_topology_override) { - regs.draw.topology.Assign(topology); - } -} - -void Maxwell3D::FlushMMEInlineDraw() { - LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), - regs.vertex_buffer.count); - ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); - ASSERT(mme_draw.instance_count == mme_draw.gl_end_count); - - // Both instance configuration registers can not be set at the same time. - ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, - "Illegal combination of instancing parameters"); - - ProcessTopologyOverride(); - - const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; - if (ShouldExecute()) { - rasterizer->Draw(is_indexed, true); - } - - // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if - // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - - // it's possible that it is incorrect and that there is some other register used to specify the - // drawing mode. - if (is_indexed) { - regs.index_array.count = 0; - } else { - regs.vertex_buffer.count = 0; - } - mme_draw.current_mode = MMEDrawMode::Undefined; - mme_draw.current_count = 0; - mme_draw.instance_count = 0; - mme_draw.instance_mode = false; - mme_draw.gl_begin_consume = false; - mme_draw.gl_end_count = 0; } void Maxwell3D::ProcessMacroUpload(u32 data) { - macro_engine->AddCode(regs.macros.upload_address++, data); + macro_engine->AddCode(regs.load_mme.instruction_ptr++, data); } void Maxwell3D::ProcessMacroBind(u32 data) { - macro_positions[regs.macros.entry++] = data; + macro_positions[regs.load_mme.start_address_ptr++] = data; } void Maxwell3D::ProcessFirmwareCall4() { @@ -443,22 +318,14 @@ void Maxwell3D::ProcessFirmwareCall4() { // Firmware call 4 is a blob that changes some registers depending on its parameters. // These registers don't affect emulation and so are stubbed by setting 0xd00 to 1. - regs.reg_array[0xd00] = 1; + regs.shadow_scratch[0] = 1; } void Maxwell3D::StampQueryResult(u64 payload, bool long_query) { - struct LongQueryResult { - u64_le value; - u64_le timestamp; - }; - static_assert(sizeof(LongQueryResult) == 16, "LongQueryResult has wrong size"); - const GPUVAddr sequence_address{regs.query.QueryAddress()}; + const GPUVAddr sequence_address{regs.report_semaphore.Address()}; if (long_query) { - // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast - // GPU, this command may actually take a while to complete in real hardware due to GPU - // wait queues. - LongQueryResult query_result{payload, system.GPU().GetTicks()}; - memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result)); + memory_manager.Write(sequence_address + sizeof(u64), system.GPU().GetTicks()); + memory_manager.Write(sequence_address, payload); } else { memory_manager.Write(sequence_address, static_cast(payload)); } @@ -466,31 +333,45 @@ void Maxwell3D::StampQueryResult(u64 payload, bool long_query) { void Maxwell3D::ProcessQueryGet() { // TODO(Subv): Support the other query units. - if (regs.query.query_get.unit != Regs::QueryUnit::Crop) { - LOG_DEBUG(HW_GPU, "Units other than CROP are unimplemented"); + if (regs.report_semaphore.query.location != Regs::ReportSemaphore::Location::All) { + LOG_DEBUG(HW_GPU, "Locations other than ALL are unimplemented"); } - switch (regs.query.query_get.operation) { - case Regs::QueryOperation::Release: - if (regs.query.query_get.fence == 1) { - rasterizer->SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence); + switch (regs.report_semaphore.query.operation) { + case Regs::ReportSemaphore::Operation::Release: + if (regs.report_semaphore.query.short_query != 0) { + const GPUVAddr sequence_address{regs.report_semaphore.Address()}; + const u32 payload = regs.report_semaphore.payload; + std::function operation([this, sequence_address, payload] { + memory_manager.Write(sequence_address, payload); + }); + rasterizer->SignalFence(std::move(operation)); } else { - StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0); + struct LongQueryResult { + u64_le value; + u64_le timestamp; + }; + const GPUVAddr sequence_address{regs.report_semaphore.Address()}; + const u32 payload = regs.report_semaphore.payload; + [this, sequence_address, payload] { + memory_manager.Write(sequence_address + sizeof(u64), system.GPU().GetTicks()); + memory_manager.Write(sequence_address, payload); + }(); } break; - case Regs::QueryOperation::Acquire: + case Regs::ReportSemaphore::Operation::Acquire: // TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that // matches the current payload. UNIMPLEMENTED_MSG("Unimplemented query operation ACQUIRE"); break; - case Regs::QueryOperation::Counter: + case Regs::ReportSemaphore::Operation::ReportOnly: if (const std::optional result = GetQueryResult()) { // If the query returns an empty optional it means it's cached and deferred. // In this case we have a non-empty result, so we stamp it immediately. - StampQueryResult(*result, regs.query.query_get.short_query == 0); + StampQueryResult(*result, regs.report_semaphore.query.short_query == 0); } break; - case Regs::QueryOperation::Trap: + case Regs::ReportSemaphore::Operation::Trap: UNIMPLEMENTED_MSG("Unimplemented query operation TRAP"); break; default: @@ -500,147 +381,119 @@ void Maxwell3D::ProcessQueryGet() { } void Maxwell3D::ProcessQueryCondition() { - const GPUVAddr condition_address{regs.condition.Address()}; - switch (regs.condition.mode) { - case Regs::ConditionMode::Always: { + const GPUVAddr condition_address{regs.render_enable.Address()}; + switch (regs.render_enable_override) { + case Regs::RenderEnable::Override::AlwaysRender: execute_on = true; break; - } - case Regs::ConditionMode::Never: { + case Regs::RenderEnable::Override::NeverRender: execute_on = false; break; - } - case Regs::ConditionMode::ResNonZero: { - Regs::QueryCompare cmp; - memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); - execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U; + case Regs::RenderEnable::Override::UseRenderEnable: + switch (regs.render_enable.mode) { + case Regs::RenderEnable::Mode::True: { + execute_on = true; + break; + } + case Regs::RenderEnable::Mode::False: { + execute_on = false; + break; + } + case Regs::RenderEnable::Mode::Conditional: { + Regs::ReportSemaphore::Compare cmp; + memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); + execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U; + break; + } + case Regs::RenderEnable::Mode::IfEqual: { + Regs::ReportSemaphore::Compare cmp; + memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); + execute_on = cmp.initial_sequence == cmp.current_sequence && + cmp.initial_mode == cmp.current_mode; + break; + } + case Regs::RenderEnable::Mode::IfNotEqual: { + Regs::ReportSemaphore::Compare cmp; + memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); + execute_on = cmp.initial_sequence != cmp.current_sequence || + cmp.initial_mode != cmp.current_mode; + break; + } + default: { + UNIMPLEMENTED_MSG("Uninplemented Condition Mode!"); + execute_on = true; + break; + } + } break; } - case Regs::ConditionMode::Equal: { - Regs::QueryCompare cmp; - memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); - execute_on = - cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode; - break; - } - case Regs::ConditionMode::NotEqual: { - Regs::QueryCompare cmp; - memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); - execute_on = - cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode; - break; - } - default: { - UNIMPLEMENTED_MSG("Uninplemented Condition Mode!"); - execute_on = true; - break; - } - } } void Maxwell3D::ProcessCounterReset() { - switch (regs.counter_reset) { - case Regs::CounterReset::SampleCnt: + switch (regs.clear_report_value) { + case Regs::ClearReport::ZPassPixelCount: rasterizer->ResetCounter(QueryType::SamplesPassed); break; default: - LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.counter_reset); + LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.clear_report_value); break; } } void Maxwell3D::ProcessSyncPoint() { const u32 sync_point = regs.sync_info.sync_point.Value(); - const u32 increment = regs.sync_info.increment.Value(); - [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value(); - if (increment) { - rasterizer->SignalSyncPoint(sync_point); - } -} - -void Maxwell3D::DrawArrays() { - LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), - regs.vertex_buffer.count); - ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); - - // Both instance configuration registers can not be set at the same time. - ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, - "Illegal combination of instancing parameters"); - - ProcessTopologyOverride(); - - if (regs.draw.instance_next) { - // Increment the current instance *before* drawing. - state.current_instance += 1; - } else if (!regs.draw.instance_cont) { - // Reset the current instance to 0. - state.current_instance = 0; - } - - const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; - if (ShouldExecute()) { - rasterizer->Draw(is_indexed, false); - } - - // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if - // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - - // it's possible that it is incorrect and that there is some other register used to specify the - // drawing mode. - if (is_indexed) { - regs.index_array.count = 0; - } else { - regs.vertex_buffer.count = 0; - } + [[maybe_unused]] const u32 cache_flush = regs.sync_info.clean_l2.Value(); + rasterizer->SignalSyncPoint(sync_point); } std::optional Maxwell3D::GetQueryResult() { - switch (regs.query.query_get.select) { - case Regs::QuerySelect::Payload: - return regs.query.query_sequence; - case Regs::QuerySelect::SamplesPassed: + switch (regs.report_semaphore.query.report) { + case Regs::ReportSemaphore::Report::Payload: + return regs.report_semaphore.payload; + case Regs::ReportSemaphore::Report::ZPassPixelCount64: // Deferred. - rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed, + rasterizer->Query(regs.report_semaphore.Address(), QueryType::SamplesPassed, system.GPU().GetTicks()); return std::nullopt; default: - LOG_DEBUG(HW_GPU, "Unimplemented query select type {}", - regs.query.query_get.select.Value()); + LOG_DEBUG(HW_GPU, "Unimplemented query report type {}", + regs.report_semaphore.query.report.Value()); return 1; } } void Maxwell3D::ProcessCBBind(size_t stage_index) { // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. - const auto& bind_data = regs.cb_bind[stage_index]; - auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.index]; + const auto& bind_data = regs.bind_groups[stage_index]; + auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot]; buffer.enabled = bind_data.valid.Value() != 0; - buffer.address = regs.const_buffer.BufferAddress(); - buffer.size = regs.const_buffer.cb_size; + buffer.address = regs.const_buffer.Address(); + buffer.size = regs.const_buffer.size; const bool is_enabled = bind_data.valid.Value() != 0; if (!is_enabled) { - rasterizer->DisableGraphicsUniformBuffer(stage_index, bind_data.index); + rasterizer->DisableGraphicsUniformBuffer(stage_index, bind_data.shader_slot); return; } - const GPUVAddr gpu_addr = regs.const_buffer.BufferAddress(); - const u32 size = regs.const_buffer.cb_size; - rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size); + const GPUVAddr gpu_addr = regs.const_buffer.Address(); + const u32 size = regs.const_buffer.size; + rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.shader_slot, gpu_addr, size); } void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) { // Write the input value to the current const buffer at the current position. - const GPUVAddr buffer_address = regs.const_buffer.BufferAddress(); + const GPUVAddr buffer_address = regs.const_buffer.Address(); ASSERT(buffer_address != 0); // Don't allow writing past the end of the buffer. - ASSERT(regs.const_buffer.cb_pos <= regs.const_buffer.cb_size); + ASSERT(regs.const_buffer.offset <= regs.const_buffer.size); - const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos}; + const GPUVAddr address{buffer_address + regs.const_buffer.offset}; const size_t copy_size = amount * sizeof(u32); memory_manager.WriteBlock(address, start_base, copy_size); // Increment the current buffer position. - regs.const_buffer.cb_pos += static_cast(copy_size); + regs.const_buffer.offset += static_cast(copy_size); } void Maxwell3D::ProcessCBData(u32 value) { @@ -648,7 +501,8 @@ void Maxwell3D::ProcessCBData(u32 value) { } Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { - const GPUVAddr tic_address_gpu{regs.tic.Address() + tic_index * sizeof(Texture::TICEntry)}; + const GPUVAddr tic_address_gpu{regs.tex_header.Address() + + tic_index * sizeof(Texture::TICEntry)}; Texture::TICEntry tic_entry; memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); @@ -657,7 +511,8 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { } Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { - const GPUVAddr tsc_address_gpu{regs.tsc.Address() + tsc_index * sizeof(Texture::TSCEntry)}; + const GPUVAddr tsc_address_gpu{regs.tex_sampler.Address() + + tsc_index * sizeof(Texture::TSCEntry)}; Texture::TSCEntry tsc_entry; memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry)); @@ -669,8 +524,4 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const { return regs.reg_array[method]; } -void Maxwell3D::ProcessClearBuffers() { - rasterizer->Clear(); -} - } // namespace Tegra::Engines diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 5f9eb20..22b9043 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -37,14 +37,19 @@ class RasterizerInterface; namespace Tegra::Engines { +class DrawManager; + /** * This Engine is known as GF100_3D. Documentation can be found in: + * https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/3d/clb197.h * https://github.com/envytools/envytools/blob/master/rnndb/graph/gf100_3d.xml * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h + * + * Note: nVidia have confirmed that their open docs have had parts redacted, so this list is + * currently incomplete, and the gaps are still worth exploring. */ -#define MAXWELL3D_REG_INDEX(field_name) \ - (offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32)) +#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Maxwell3D::Regs, field_name) / sizeof(u32)) class Maxwell3D final : public EngineInterface { public: @@ -55,7 +60,6 @@ public: void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); /// Register structure of the Maxwell3D engine. - /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. struct Regs { static constexpr std::size_t NUM_REGS = 0xE00; @@ -74,90 +78,508 @@ public: static constexpr std::size_t MaxConstBuffers = 18; static constexpr std::size_t MaxConstBufferSize = 0x10000; - enum class QueryOperation : u32 { - Release = 0, - Acquire = 1, - Counter = 2, - Trap = 3, + struct ID { + union { + BitField<0, 16, u32> cls; + BitField<16, 5, u32> engine; + }; }; - enum class QueryUnit : u32 { - VFetch = 1, - VP = 2, - Rast = 4, - StrmOut = 5, - GP = 6, - ZCull = 7, - Prop = 10, - Crop = 15, + struct LoadMME { + u32 instruction_ptr; + u32 instruction; + u32 start_address_ptr; + u32 start_address; }; - enum class QuerySelect : u32 { - Payload = 0, - TimeElapsed = 2, - TransformFeedbackPrimitivesGenerated = 11, - PrimitivesGenerated = 18, - SamplesPassed = 21, - TransformFeedbackUnknown = 26, + struct Notify { + u32 address_high; + u32 address_low; + u32 type; + + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } }; - struct QueryCompare { - u32 initial_sequence; - u32 initial_mode; - u32 unknown1; - u32 unknown2; - u32 current_sequence; - u32 current_mode; + struct PeerSemaphore { + u32 address_high; + u32 address_low; + + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } }; - enum class QuerySyncCondition : u32 { - NotEqual = 0, - GreaterThan = 1, + struct GlobalRender { + enum class Mode : u32 { + False = 0, + True = 1, + Conditional = 2, + IfEqual = 3, + IfNotEqual = 4, + }; + u32 offset_high; + u32 offset_low; + Mode mode; + + GPUVAddr Address() const { + return (GPUVAddr{offset_high} << 32) | GPUVAddr{offset_low}; + } }; - enum class ConditionMode : u32 { - Never = 0, - Always = 1, - ResNonZero = 2, - Equal = 3, - NotEqual = 4, + enum class ReductionOp : u32 { + RedAdd = 0, + RedMin = 1, + RedMax = 2, + RedInc = 3, + RedDec = 4, + RedAnd = 5, + RedOr = 6, + RedXor = 7, }; - enum class ShaderProgram : u32 { - VertexA = 0, - VertexB = 1, - TesselationControl = 2, - TesselationEval = 3, - Geometry = 4, - Fragment = 5, + struct LaunchDMA { + enum class Layout : u32 { + Blocklinear = 0, + Pitch = 1, + }; + + enum class CompletionType : u32 { + FlushDisable = 0, + FlushOnly = 1, + Release = 2, + }; + + union { + BitField<0, 1, Layout> memory_layout; + BitField<4, 2, CompletionType> completion_type; + BitField<8, 2, u32> interrupt_type; + BitField<12, 1, u32> sem_size; + BitField<1, 1, u32> reduction_enable; + BitField<13, 3, ReductionOp> reduction_op; + BitField<2, 2, u32> reduction_format; + BitField<6, 1, u32> sysmembar_disable; + }; + }; + + struct I2M { + u32 address_high; + u32 address_low; + u32 payload; + INSERT_PADDING_BYTES_NOINIT(0x8); + u32 nop0; + u32 nop1; + u32 nop2; + u32 nop3; + }; + + struct OpportunisticEarlyZ { + BitField<0, 5, u32> threshold; + + u32 Threshold() const { + switch (threshold) { + case 0x0: + return 0; + case 0x1F: + return 0x1F; + default: + // Thresholds begin at 0x10 (1 << 4) + // Threshold is in the range 0x1 to 0x13 + return 1U << (4 + threshold.Value() - 1); + } + } + }; + + struct GeometryShaderDmFifo { + union { + BitField<0, 13, u32> raster_on; + BitField<16, 13, u32> raster_off; + BitField<31, 1, u32> spill_enabled; + }; + }; + + struct L2CacheControl { + enum class EvictPolicy : u32 { + First = 0, + Normal = 1, + Last = 2, + }; + + union { + BitField<4, 2, EvictPolicy> policy; + }; + }; + + struct InvalidateShaderCache { + union { + BitField<0, 1, u32> instruction; + BitField<4, 1, u32> data; + BitField<12, 1, u32> constant; + BitField<1, 1, u32> locks; + BitField<2, 1, u32> flush_data; + }; + }; + + struct SyncInfo { + enum class Condition : u32 { + StreamOutWritesDone = 0, + RopWritesDone = 1, + }; + + union { + BitField<0, 16, u32> sync_point; + BitField<16, 1, u32> clean_l2; + BitField<20, 1, Condition> condition; + }; + }; + + struct SurfaceClipBlockId { + union { + BitField<0, 4, u32> block_width; + BitField<4, 4, u32> block_height; + BitField<8, 4, u32> block_depth; + }; + }; + + struct DecompressSurface { + union { + BitField<0, 3, u32> mrt_select; + BitField<4, 16, u32> rt_array_index; + }; + }; + + struct ZCullRopBypass { + union { + BitField<0, 1, u32> enable; + BitField<4, 1, u32> no_stall; + BitField<8, 1, u32> cull_everything; + BitField<12, 4, u32> threshold; + }; + }; + + struct ZCullSubregion { + union { + BitField<0, 1, u32> enable; + BitField<4, 24, u32> normalized_aliquots; + }; + }; + + struct RasterBoundingBox { + enum class Mode : u32 { + BoundingBox = 0, + FullViewport = 1, + }; + + union { + BitField<0, 1, Mode> mode; + BitField<4, 8, u32> pad; + }; + }; + + struct IteratedBlendOptimization { + enum class Noop : u32 { + Never = 0, + SourceRGBA0000 = 1, + SourceAlpha = 2, + SourceRGBA0001 = 3, + }; + + union { + BitField<0, 1, Noop> noop; + }; + }; + + struct ZCullSubregionAllocation { + enum class Format : u32 { + Z_16x16x2_4x4 = 0, + ZS_16x16_4x4 = 1, + Z_16x16_4x2 = 2, + Z_16x16_2x4 = 3, + Z_16x8_4x4 = 4, + Z_8x8_4x2 = 5, + Z_8x8_2x4 = 6, + Z_16x16_4x8 = 7, + Z_4x8_2x2 = 8, + ZS_16x8_4x2 = 9, + ZS_16x8_2x4 = 10, + ZS_8x8_2x2 = 11, + Z_4x8_1x1 = 12, + None = 15, + }; + + union { + BitField<0, 8, u32> id; + BitField<8, 16, u32> aliquots; + BitField<24, 4, Format> format; + }; + }; + + enum class ZCullSubregionAlgorithm : u32 { + Static = 0, + Adaptive = 1, + }; + + struct PixelShaderOutputSampleMaskUsage { + union { + BitField<0, 1, u32> enable; + BitField<1, 1, u32> qualify_by_aa; + }; + }; + + struct L1Configuration { + enum class AddressableMemory : u32 { + Size16Kb = 0, + Size48Kb = 3, + }; + union { + BitField<0, 3, AddressableMemory> direct_addressable_memory; + }; + }; + + struct SPAVersion { + union { + BitField<0, 8, u32> minor; + BitField<8, 8, u32> major; + }; + }; + + struct SnapGrid { + enum class Location : u32 { + Pixel2x2 = 1, + Pixel4x4 = 2, + Pixel8x8 = 3, + Pixel16x16 = 4, + Pixel32x32 = 5, + Pixel64x64 = 6, + Pixel128x128 = 7, + Pixel256x256 = 8, + }; + + enum class Mode : u32 { + RTNE = 0, + Tesla = 1, + }; + + struct { + union { + BitField<0, 4, Location> location; + BitField<8, 1, Mode> rounding_mode; + }; + } line; + + struct { + union { + BitField<0, 4, Location> location; + BitField<8, 1, Mode> rounding_mode; + }; + } non_line; + }; + + struct Tessellation { + enum class DomainType : u32 { + Isolines = 0, + Triangles = 1, + Quads = 2, + }; + + enum class Spacing : u32 { + Integer = 0, + FractionalOdd = 1, + FractionalEven = 2, + }; + + enum class OutputPrimitives : u32 { + Points = 0, + Lines = 1, + Triangles_CW = 2, + Triangles_CCW = 3, + }; + + struct Parameters { + union { + BitField<0, 2, DomainType> domain_type; + BitField<4, 2, Spacing> spacing; + BitField<8, 2, OutputPrimitives> output_primitives; + }; + } params; + + struct LOD { + std::array outer; + std::array inner; + } lod; + + std::array reserved; + }; + + struct SubTilingPerf { + struct { + union { + BitField<0, 8, u32> spm_triangle_register_file_per; + BitField<8, 8, u32> spm_pixel_output_buffer_per; + BitField<16, 8, u32> spm_triangle_ram_per; + BitField<24, 8, u32> max_quads_per; + }; + } knob_a; + + struct { + union { + BitField<0, 8, u32> max_primitives_per; + }; + } knob_b; + + u32 knob_c; + }; + + struct ZCullSubregionReport { + enum class ReportType : u32 { + DepthTest = 0, + DepthTestNoAccept = 1, + DepthTestLateZ = 2, + StencilTest = 3, + }; + + union { + BitField<0, 1, u32> enabled; + BitField<4, 8, u32> subregion_id; + } to_report; + + union { + BitField<0, 1, u32> enabled; + BitField<4, 3, ReportType> type; + } report_type; + }; + + struct BalancedPrimitiveWorkload { + union { + BitField<0, 1, u32> unpartitioned_mode; + BitField<4, 1, u32> timesliced_mode; + }; + }; + + struct TransformFeedback { + struct Buffer { + u32 enable; + u32 address_high; + u32 address_low; + s32 size; + s32 start_offset; + INSERT_PADDING_BYTES_NOINIT(0xC); + + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + }; + static_assert(sizeof(Buffer) == 0x20); + + struct Control { + u32 stream; + u32 varying_count; + u32 stride; + INSERT_PADDING_BYTES_NOINIT(0x4); + }; + static_assert(sizeof(Control) == 0x10); + + std::array buffers; + + INSERT_PADDING_BYTES_NOINIT(0x300); + + std::array controls; + }; + + struct HybridAntiAliasControl { + enum class Centroid : u32 { + PerFragment = 0, + PerPass = 1, + }; + union { + BitField<0, 4, u32> passes; + BitField<4, 1, Centroid> centroid; + BitField<5, 1, u32> passes_extended; + }; + }; + + struct ShaderLocalMemory { + u32 base_address; + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 address_high; + u32 address_low; + u32 size_high; + u32 size_low; + u32 default_size_per_warp; + + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + + u64 Size() const { + return (u64{size_high} << 32) | u64{size_low}; + } + }; + + struct ZCullRegion { + u32 width; + u32 height; + u32 depth; + u32 offset; + INSERT_PADDING_BYTES_NOINIT(0xC); + u32 fetch_streams_once; + union { + BitField<0, 16, u32> start_aliquot; + BitField<16, 16, u32> aliquot_count; + } location; + u32 aliquots_per_layer; + u32 storage_address_high; + u32 storage_address_low; + u32 storage_limit_address_high; + u32 storage_limit_address_low; + + GPUVAddr StorageAddress() const { + return (GPUVAddr{storage_address_high} << 32) | GPUVAddr{storage_address_low}; + } + GPUVAddr StorageLimitAddress() const { + return (GPUVAddr{storage_limit_address_high} << 32) | + GPUVAddr{storage_limit_address_low}; + } + }; + + struct ZetaReadOnly { + union { + BitField<0, 1, u32> enable_z; + BitField<4, 1, u32> enable_stencil; + }; }; struct VertexAttribute { enum class Size : u32 { Invalid = 0x0, - Size_32_32_32_32 = 0x01, - Size_32_32_32 = 0x02, - Size_16_16_16_16 = 0x03, - Size_32_32 = 0x04, - Size_16_16_16 = 0x05, - Size_8_8_8_8 = 0x0a, - Size_16_16 = 0x0f, - Size_32 = 0x12, - Size_8_8_8 = 0x13, - Size_8_8 = 0x18, - Size_16 = 0x1b, - Size_8 = 0x1d, - Size_10_10_10_2 = 0x30, - Size_11_11_10 = 0x31, + Size_R32_G32_B32_A32 = 0x01, + Size_R32_G32_B32 = 0x02, + Size_R16_G16_B16_A16 = 0x03, + Size_R32_G32 = 0x04, + Size_R16_G16_B16 = 0x05, + Size_R8_G8_B8_A8 = 0x0A, + Size_R16_G16 = 0x0F, + Size_R32 = 0x12, + Size_R8_G8_B8 = 0x13, + Size_R8_G8 = 0x18, + Size_R16 = 0x1B, + Size_R8 = 0x1D, + Size_A2_B10_G10_R10 = 0x30, + Size_B10_G11_R11 = 0x31, + Size_G8_R8 = 0x32, + Size_X8_B8_G8_R8 = 0x33, + Size_A8 = 0x34, }; enum class Type : u32 { - SignedNorm = 1, - UnsignedNorm = 2, - SignedInt = 3, - UnsignedInt = 4, - UnsignedScaled = 5, - SignedScaled = 6, + UnusedEnumDoNotUseBecauseItWillGoAway = 0, + SNorm = 1, + UNorm = 2, + SInt = 3, + UInt = 4, + UScaled = 5, + SScaled = 6, Float = 7, }; @@ -173,33 +595,36 @@ public: u32 ComponentCount() const { switch (size) { - case Size::Size_32_32_32_32: + case Size::Size_R32_G32_B32_A32: return 4; - case Size::Size_32_32_32: + case Size::Size_R32_G32_B32: return 3; - case Size::Size_16_16_16_16: + case Size::Size_R16_G16_B16_A16: return 4; - case Size::Size_32_32: + case Size::Size_R32_G32: return 2; - case Size::Size_16_16_16: + case Size::Size_R16_G16_B16: return 3; - case Size::Size_8_8_8_8: + case Size::Size_R8_G8_B8_A8: + case Size::Size_X8_B8_G8_R8: return 4; - case Size::Size_16_16: + case Size::Size_R16_G16: return 2; - case Size::Size_32: + case Size::Size_R32: return 1; - case Size::Size_8_8_8: + case Size::Size_R8_G8_B8: return 3; - case Size::Size_8_8: + case Size::Size_R8_G8: + case Size::Size_G8_R8: return 2; - case Size::Size_16: + case Size::Size_R16: return 1; - case Size::Size_8: + case Size::Size_R8: + case Size::Size_A8: return 1; - case Size::Size_10_10_10_2: + case Size::Size_A2_B10_G10_R10: return 4; - case Size::Size_11_11_10: + case Size::Size_B10_G11_R11: return 3; default: ASSERT(false); @@ -209,33 +634,36 @@ public: u32 SizeInBytes() const { switch (size) { - case Size::Size_32_32_32_32: + case Size::Size_R32_G32_B32_A32: return 16; - case Size::Size_32_32_32: + case Size::Size_R32_G32_B32: return 12; - case Size::Size_16_16_16_16: + case Size::Size_R16_G16_B16_A16: return 8; - case Size::Size_32_32: + case Size::Size_R32_G32: return 8; - case Size::Size_16_16_16: + case Size::Size_R16_G16_B16: return 6; - case Size::Size_8_8_8_8: + case Size::Size_R8_G8_B8_A8: + case Size::Size_X8_B8_G8_R8: return 4; - case Size::Size_16_16: + case Size::Size_R16_G16: return 4; - case Size::Size_32: + case Size::Size_R32: return 4; - case Size::Size_8_8_8: + case Size::Size_R8_G8_B8: return 3; - case Size::Size_8_8: + case Size::Size_R8_G8: + case Size::Size_G8_R8: return 2; - case Size::Size_16: + case Size::Size_R16: return 2; - case Size::Size_8: + case Size::Size_R8: + case Size::Size_A8: return 1; - case Size::Size_10_10_10_2: + case Size::Size_A2_B10_G10_R10: return 4; - case Size::Size_11_11_10: + case Size::Size_B10_G11_R11: return 4; default: ASSERT(false); @@ -245,34 +673,36 @@ public: std::string SizeString() const { switch (size) { - case Size::Size_32_32_32_32: + case Size::Size_R32_G32_B32_A32: return "32_32_32_32"; - case Size::Size_32_32_32: + case Size::Size_R32_G32_B32: return "32_32_32"; - case Size::Size_16_16_16_16: + case Size::Size_R16_G16_B16_A16: return "16_16_16_16"; - case Size::Size_32_32: + case Size::Size_R32_G32: return "32_32"; - case Size::Size_16_16_16: + case Size::Size_R16_G16_B16: return "16_16_16"; - case Size::Size_8_8_8_8: + case Size::Size_R8_G8_B8_A8: return "8_8_8_8"; - case Size::Size_16_16: + case Size::Size_R16_G16: return "16_16"; - case Size::Size_32: + case Size::Size_R32: return "32"; - case Size::Size_8_8_8: + case Size::Size_R8_G8_B8: return "8_8_8"; - case Size::Size_8_8: + case Size::Size_R8_G8: + case Size::Size_G8_R8: return "8_8"; - case Size::Size_16: + case Size::Size_R16: return "16"; - case Size::Size_8: + case Size::Size_R8: + case Size::Size_A8: return "8"; - case Size::Size_10_10_10_2: - return "10_10_10_2"; - case Size::Size_11_11_10: - return "11_11_10"; + case Size::Size_A2_B10_G10_R10: + return "2_10_10_10"; + case Size::Size_B10_G11_R11: + return "10_11_11"; default: ASSERT(false); return {}; @@ -281,17 +711,19 @@ public: std::string TypeString() const { switch (type) { - case Type::SignedNorm: + case Type::UnusedEnumDoNotUseBecauseItWillGoAway: + return "Unused"; + case Type::SNorm: return "SNORM"; - case Type::UnsignedNorm: + case Type::UNorm: return "UNORM"; - case Type::SignedInt: + case Type::SInt: return "SINT"; - case Type::UnsignedInt: + case Type::UInt: return "UINT"; - case Type::UnsignedScaled: + case Type::UScaled: return "USCALED"; - case Type::SignedScaled: + case Type::SScaled: return "SSCALED"; case Type::Float: return "FLOAT"; @@ -301,7 +733,7 @@ public: } bool IsNormalized() const { - return (type == Type::SignedNorm) || (type == Type::UnsignedNorm); + return (type == Type::SNorm) || (type == Type::UNorm); } bool IsValid() const { @@ -312,6 +744,7 @@ public: return hex < other.hex; } }; + static_assert(sizeof(VertexAttribute) == 0x4); struct MsaaSampleLocation { union { @@ -342,9 +775,96 @@ public: } }; - enum class DepthMode : u32 { - MinusOneToOne = 0, - ZeroToOne = 1, + struct MultisampleCoverageToColor { + union { + BitField<0, 1, u32> enable; + BitField<4, 3, u32> target; + }; + }; + + struct DecompressZetaSurface { + union { + BitField<0, 1, u32> z_enable; + BitField<4, 1, u32> stencil_enable; + }; + }; + + struct ZetaSparse { + enum class UnmappedCompare : u32 { + Unmapped = 0, + FailAlways = 1, + }; + union { + BitField<0, 1, u32> enable; + BitField<1, 1, UnmappedCompare> unmapped_compare; + }; + }; + + struct RtControl { + union { + BitField<0, 4, u32> count; + BitField<4, 3, u32> target0; + BitField<7, 3, u32> target1; + BitField<10, 3, u32> target2; + BitField<13, 3, u32> target3; + BitField<16, 3, u32> target4; + BitField<19, 3, u32> target5; + BitField<22, 3, u32> target6; + BitField<25, 3, u32> target7; + }; + + u32 Map(std::size_t index) const { + const std::array maps{target0, target1, target2, target3, + target4, target5, target6, target7}; + ASSERT(index < maps.size()); + return maps[index]; + } + }; + + struct CompressionThresholdSamples { + u32 samples; + + u32 Samples() const { + if (samples == 0) { + return 0; + } + return 1U << (samples - 1); + } + }; + + struct PixelShaderInterlockControl { + enum class TileMode : u32 { + NoConflictDetect = 0, + DetectSampleConflict = 1, + DetectPixelConflict = 2, + }; + enum class TileSize : u32 { + Size_16x16 = 0, + Size_8x8 = 1, + }; + enum class FragmentOrder : u32 { + FragmentOrdered = 0, + FragmentUnordered = 1, + }; + union { + BitField<0, 2, TileMode> tile_mode; + BitField<2, 1, TileSize> tile_size; + BitField<3, 1, FragmentOrder> fragment_order; + }; + }; + + struct ZetaSize { + enum class DimensionControl : u32 { + DepthDefinesArray = 0, + ArraySizeOne = 1, + }; + + u32 width; + u32 height; + union { + BitField<0, 16, u32> depth; + BitField<16, 1, DimensionControl> dim_control; + }; }; enum class PrimitiveTopology : u32 { @@ -358,15 +878,21 @@ public: Quads = 0x7, QuadStrip = 0x8, Polygon = 0x9, - LinesAdjacency = 0xa, - LineStripAdjacency = 0xb, - TrianglesAdjacency = 0xc, - TriangleStripAdjacency = 0xd, - Patches = 0xe, + LinesAdjacency = 0xA, + LineStripAdjacency = 0xB, + TrianglesAdjacency = 0xC, + TriangleStripAdjacency = 0xD, + Patches = 0xE, + }; + + struct VertexArray { + union { + BitField<0, 16, u32> start; + BitField<16, 12, u32> count; + BitField<28, 3, PrimitiveTopology> topology; + }; }; - // Constants as from NVC0_3D_UNK1970_D3D - // https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h#L1598 enum class PrimitiveTopologyOverride : u32 { None = 0x0, Points = 0x1, @@ -374,11 +900,32 @@ public: LineStrip = 0x3, Triangles = 0x4, TriangleStrip = 0x5, - LinesAdjacency = 0xa, - LineStripAdjacency = 0xb, - TrianglesAdjacency = 0xc, - TriangleStripAdjacency = 0xd, - Patches = 0xe, + LinesAdjacency = 0xA, + LineStripAdjacency = 0xB, + TrianglesAdjacency = 0xC, + TriangleStripAdjacency = 0xD, + Patches = 0xE, + + LegacyPoints = 0x1001, + LegacyIndexedLines = 0x1002, + LegacyIndexedTriangles = 0x1003, + LegacyLines = 0x100F, + LegacyLineStrip = 0x1010, + LegacyIndexedLineStrip = 0x1011, + LegacyTriangles = 0x1012, + LegacyTriangleStrip = 0x1013, + LegacyIndexedTriangleStrip = 0x1014, + LegacyTriangleFan = 0x1015, + LegacyIndexedTriangleFan = 0x1016, + LegacyTriangleFanImm = 0x1017, + LegacyLinesImm = 0x1018, + LegacyIndexedTriangles2 = 0x101A, + LegacyIndexedLines2 = 0x101B, + }; + + enum class DepthMode : u32 { + MinusOneToOne = 0, + ZeroToOne = 1, }; enum class IndexFormat : u32 { @@ -388,183 +935,143 @@ public: }; enum class ComparisonOp : u32 { - // These values are used by Nouveau and most games, they correspond to the OpenGL token - // values for these operations. - Never = 0x200, - Less = 0x201, - Equal = 0x202, - LessEqual = 0x203, - Greater = 0x204, - NotEqual = 0x205, - GreaterEqual = 0x206, - Always = 0x207, + Never_D3D = 1, + Less_D3D = 2, + Equal_D3D = 3, + LessEqual_D3D = 4, + Greater_D3D = 5, + NotEqual_D3D = 6, + GreaterEqual_D3D = 7, + Always_D3D = 8, - // These values are used by some games, they seem to be NV04 values. - NeverOld = 1, - LessOld = 2, - EqualOld = 3, - LessEqualOld = 4, - GreaterOld = 5, - NotEqualOld = 6, - GreaterEqualOld = 7, - AlwaysOld = 8, + Never_GL = 0x200, + Less_GL = 0x201, + Equal_GL = 0x202, + LessEqual_GL = 0x203, + Greater_GL = 0x204, + NotEqual_GL = 0x205, + GreaterEqual_GL = 0x206, + Always_GL = 0x207, }; - enum class LogicOperation : u32 { - Clear = 0x1500, - And = 0x1501, - AndReverse = 0x1502, - Copy = 0x1503, - AndInverted = 0x1504, - NoOp = 0x1505, - Xor = 0x1506, - Or = 0x1507, - Nor = 0x1508, - Equiv = 0x1509, - Invert = 0x150A, - OrReverse = 0x150B, - CopyInverted = 0x150C, - OrInverted = 0x150D, - Nand = 0x150E, - Set = 0x150F, - }; - - enum class StencilOp : u32 { - Keep = 1, - Zero = 2, - Replace = 3, - Incr = 4, - Decr = 5, - Invert = 6, - IncrWrap = 7, - DecrWrap = 8, - KeepOGL = 0x1E00, - ZeroOGL = 0, - ReplaceOGL = 0x1E01, - IncrOGL = 0x1E02, - DecrOGL = 0x1E03, - InvertOGL = 0x150A, - IncrWrapOGL = 0x8507, - DecrWrapOGL = 0x8508, - }; - - enum class CounterReset : u32 { - SampleCnt = 0x01, - Unk02 = 0x02, - Unk03 = 0x03, - Unk04 = 0x04, - EmittedPrimitives = 0x10, // Not tested - Unk11 = 0x11, - Unk12 = 0x12, - Unk13 = 0x13, - Unk15 = 0x15, - Unk16 = 0x16, - Unk17 = 0x17, - Unk18 = 0x18, - Unk1A = 0x1A, - Unk1B = 0x1B, - Unk1C = 0x1C, - Unk1D = 0x1D, - Unk1E = 0x1E, - GeneratedPrimitives = 0x1F, + enum class ClearReport : u32 { + ZPassPixelCount = 0x01, + ZCullStats = 0x02, + StreamingPrimitvesNeededMinusSucceeded = 0x03, + AlphaBetaClocks = 0x04, + StreamingPrimitivesSucceeded = 0x10, + StreamingPrimitivesNeeded = 0x11, + VerticesGenerated = 0x12, + PrimitivesGenerated = 0x13, + VertexShaderInvocations = 0x15, + TessellationInitInvocations = 0x16, + TessellationShaderInvocations = 0x17, + TessellationShaderPrimitivesGenerated = 0x18, + GeometryShaderInvocations = 0x1A, + GeometryShaderPrimitivesGenerated = 0x1B, + ClipperInvocations = 0x1C, + ClipperPrimitivesGenerated = 0x1D, + PixelShaderInvocations = 0x1E, + VtgPrimitivesOut = 0x1F, }; enum class FrontFace : u32 { - ClockWise = 0x0900, - CounterClockWise = 0x0901, + ClockWise = 0x900, + CounterClockWise = 0x901, }; enum class CullFace : u32 { - Front = 0x0404, - Back = 0x0405, - FrontAndBack = 0x0408, + Front = 0x404, + Back = 0x405, + FrontAndBack = 0x408, }; struct Blend { enum class Equation : u32 { - Add = 1, - Subtract = 2, - ReverseSubtract = 3, - Min = 4, - Max = 5, + Add_D3D = 1, + Subtract_D3D = 2, + ReverseSubtract_D3D = 3, + Min_D3D = 4, + Max_D3D = 5, - // These values are used by Nouveau and some games. - AddGL = 0x8006, - MinGL = 0x8007, - MaxGL = 0x8008, - SubtractGL = 0x800a, - ReverseSubtractGL = 0x800b + Add_GL = 0x8006, + Min_GL = 0x8007, + Max_GL = 0x8008, + Subtract_GL = 0x800A, + ReverseSubtract_GL = 0x800B }; enum class Factor : u32 { - Zero = 0x1, - One = 0x2, - SourceColor = 0x3, - OneMinusSourceColor = 0x4, - SourceAlpha = 0x5, - OneMinusSourceAlpha = 0x6, - DestAlpha = 0x7, - OneMinusDestAlpha = 0x8, - DestColor = 0x9, - OneMinusDestColor = 0xa, - SourceAlphaSaturate = 0xb, - Source1Color = 0x10, - OneMinusSource1Color = 0x11, - Source1Alpha = 0x12, - OneMinusSource1Alpha = 0x13, - ConstantColor = 0x61, - OneMinusConstantColor = 0x62, - ConstantAlpha = 0x63, - OneMinusConstantAlpha = 0x64, + Zero_D3D = 0x1, + One_D3D = 0x2, + SourceColor_D3D = 0x3, + OneMinusSourceColor_D3D = 0x4, + SourceAlpha_D3D = 0x5, + OneMinusSourceAlpha_D3D = 0x6, + DestAlpha_D3D = 0x7, + OneMinusDestAlpha_D3D = 0x8, + DestColor_D3D = 0x9, + OneMinusDestColor_D3D = 0xA, + SourceAlphaSaturate_D3D = 0xB, + BothSourceAlpha_D3D = 0xC, + OneMinusBothSourceAlpha_D3D = 0xD, + BlendFactor_D3D = 0xE, + OneMinusBlendFactor_D3D = 0xF, + Source1Color_D3D = 0x10, + OneMinusSource1Color_D3D = 0x11, + Source1Alpha_D3D = 0x12, + OneMinusSource1Alpha_D3D = 0x13, - // These values are used by Nouveau and some games. - ZeroGL = 0x4000, - OneGL = 0x4001, - SourceColorGL = 0x4300, - OneMinusSourceColorGL = 0x4301, - SourceAlphaGL = 0x4302, - OneMinusSourceAlphaGL = 0x4303, - DestAlphaGL = 0x4304, - OneMinusDestAlphaGL = 0x4305, - DestColorGL = 0x4306, - OneMinusDestColorGL = 0x4307, - SourceAlphaSaturateGL = 0x4308, - ConstantColorGL = 0xc001, - OneMinusConstantColorGL = 0xc002, - ConstantAlphaGL = 0xc003, - OneMinusConstantAlphaGL = 0xc004, - Source1ColorGL = 0xc900, - OneMinusSource1ColorGL = 0xc901, - Source1AlphaGL = 0xc902, - OneMinusSource1AlphaGL = 0xc903, + Zero_GL = 0x4000, + One_GL = 0x4001, + SourceColor_GL = 0x4300, + OneMinusSourceColor_GL = 0x4301, + SourceAlpha_GL = 0x4302, + OneMinusSourceAlpha_GL = 0x4303, + DestAlpha_GL = 0x4304, + OneMinusDestAlpha_GL = 0x4305, + DestColor_GL = 0x4306, + OneMinusDestColor_GL = 0x4307, + SourceAlphaSaturate_GL = 0x4308, + ConstantColor_GL = 0xC001, + OneMinusConstantColor_GL = 0xC002, + ConstantAlpha_GL = 0xC003, + OneMinusConstantAlpha_GL = 0xC004, + Source1Color_GL = 0xC900, + OneMinusSource1Color_GL = 0xC901, + Source1Alpha_GL = 0xC902, + OneMinusSource1Alpha_GL = 0xC903, }; u32 separate_alpha; - Equation equation_rgb; - Factor factor_source_rgb; - Factor factor_dest_rgb; - Equation equation_a; - Factor factor_source_a; - Factor factor_dest_a; - INSERT_PADDING_WORDS_NOINIT(1); + Equation color_op; + Factor color_source; + Factor color_dest; + Equation alpha_op; + Factor alpha_source; + u32 enable_global_color_key; + Factor alpha_dest; + + u32 single_rop_control_enable; + u32 enable[NumRenderTargets]; }; - enum class TessellationPrimitive : u32 { - Isolines = 0, - Triangles = 1, - Quads = 2, - }; - - enum class TessellationSpacing : u32 { - Equal = 0, - FractionalOdd = 1, - FractionalEven = 2, + struct BlendPerTarget { + u32 separate_alpha; + Blend::Equation color_op; + Blend::Factor color_source; + Blend::Factor color_dest; + Blend::Equation alpha_op; + Blend::Factor alpha_source; + Blend::Factor alpha_dest; + INSERT_PADDING_BYTES_NOINIT(0x4); }; + static_assert(sizeof(BlendPerTarget) == 0x20); enum class PolygonMode : u32 { - Point = 0x1b00, - Line = 0x1b01, - Fill = 0x1b02, + Point = 0x1B00, + Line = 0x1B01, + Fill = 0x1B02, }; enum class ShadowRamControl : u32 { @@ -589,18 +1096,22 @@ public: NegativeW = 7, }; - enum class SamplerIndex : u32 { + enum class SamplerBinding : u32 { Independently = 0, - ViaHeaderIndex = 1, + ViaHeaderBinding = 1, }; struct TileMode { + enum class DimensionControl : u32 { + DepthDefinesArray = 0, + DepthDefinesDepth = 1, + }; union { BitField<0, 4, u32> block_width; BitField<4, 4, u32> block_height; BitField<8, 4, u32> block_depth; BitField<12, 1, u32> is_pitch_linear; - BitField<16, 1, u32> is_3d; + BitField<16, 1, DimensionControl> dim_control; }; }; static_assert(sizeof(TileMode) == 4); @@ -616,23 +1127,24 @@ public: BitField<0, 16, u32> depth; BitField<16, 1, u32> volume; }; - u32 layer_stride; + u32 array_pitch; u32 base_layer; - INSERT_PADDING_WORDS_NOINIT(7); + u32 mark_ieee_clean; + INSERT_PADDING_BYTES_NOINIT(0x18); GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; } }; + static_assert(sizeof(RenderTargetConfig) == 0x40); struct ColorMask { union { u32 raw; - BitField<0, 4, u32> R; - BitField<4, 4, u32> G; - BitField<8, 4, u32> B; - BitField<12, 4, u32> A; + BitField<0, 1, u32> R; + BitField<4, 1, u32> G; + BitField<8, 1, u32> B; + BitField<12, 1, u32> A; }; }; @@ -643,6 +1155,7 @@ public: f32 translate_x; f32 translate_y; f32 translate_z; + union { u32 raw; BitField<0, 3, ViewportSwizzle> x; @@ -650,7 +1163,11 @@ public: BitField<8, 3, ViewportSwizzle> z; BitField<12, 3, ViewportSwizzle> w; } swizzle; - INSERT_PADDING_WORDS_NOINIT(1); + + union { + BitField<0, 5, u32> x; + BitField<8, 5, u32> y; + } snap_grid_precision; Common::Rectangle GetRect() const { return { @@ -677,21 +1194,14 @@ public: return translate_y + std::fabs(scale_y) - GetY(); } }; + static_assert(sizeof(ViewportTransform) == 0x20); - struct ScissorTest { - u32 enable; - union { - BitField<0, 16, u32> min_x; - BitField<16, 16, u32> max_x; + struct Viewport { + enum class PixelCenter : u32 { + HalfIntegers = 0, + Integers = 1, }; - union { - BitField<0, 16, u32> min_y; - BitField<16, 16, u32> max_y; - }; - u32 fill; - }; - struct ViewPort { union { BitField<0, 16, u32> x; BitField<16, 16, u32> width; @@ -703,726 +1213,1807 @@ public: float depth_range_near; float depth_range_far; }; + static_assert(sizeof(Viewport) == 0x10); - struct TransformFeedbackBinding { - u32 buffer_enable; + struct Window { + union { + BitField<0, 16, u32> x_min; + BitField<16, 16, u32> x_max; + }; + union { + BitField<0, 16, u32> y_min; + BitField<16, 16, u32> y_max; + }; + }; + static_assert(sizeof(Window) == 0x8); + + struct ClipIdExtent { + union { + BitField<0, 16, u32> x; + BitField<16, 16, u32> width; + }; + union { + BitField<0, 16, u32> y; + BitField<16, 16, u32> height; + }; + }; + static_assert(sizeof(ClipIdExtent) == 0x8); + + enum class VisibleCallLimit : u32 { + Limit0 = 0, + Limit1 = 1, + Limit2 = 2, + Limit4 = 3, + Limit8 = 4, + Limit16 = 5, + Limit32 = 6, + Limit64 = 7, + Limit128 = 8, + None = 15, + }; + + struct StatisticsCounter { + union { + BitField<0, 1, u32> da_vertices; + BitField<1, 1, u32> da_primitives; + BitField<2, 1, u32> vs_invocations; + BitField<3, 1, u32> gs_invocations; + BitField<4, 1, u32> gs_primitives; + BitField<5, 1, u32> streaming_primitives_succeeded; + BitField<6, 1, u32> streaming_primitives_needed; + BitField<7, 1, u32> clipper_invocations; + BitField<8, 1, u32> clipper_primitives; + BitField<9, 1, u32> ps_invocations; + BitField<11, 1, u32> ti_invocations; + BitField<12, 1, u32> ts_invocations; + BitField<13, 1, u32> ts_primitives; + BitField<14, 1, u32> total_streaming_primitives_needed_succeeded; + BitField<10, 1, u32> vtg_primitives_out; + BitField<15, 1, u32> alpha_beta_clocks; + }; + }; + + struct ClearRect { + union { + BitField<0, 16, u32> x_min; + BitField<16, 16, u32> x_max; + }; + union { + BitField<0, 16, u32> y_min; + BitField<16, 16, u32> y_max; + }; + }; + + struct VertexBuffer { + u32 first; + u32 count; + }; + + struct InvalidateShaderCacheNoWFI { + union { + BitField<0, 1, u32> instruction; + BitField<4, 1, u32> global_data; + BitField<12, 1, u32> constant; + }; + }; + + struct ZCullSerialization { + enum class Applied : u32 { + Always = 0, + LateZ = 1, + OutOfGamutZ = 2, + LateZOrOutOfGamutZ = 3, + }; + union { + BitField<0, 1, u32> enable; + BitField<4, 2, Applied> applied; + }; + }; + + struct ZCullDirFormat { + enum class Zdir : u32 { + Less = 0, + Greater = 1, + }; + enum class Zformat : u32 { + MSB = 0, + FP = 1, + Ztrick = 2, + Zf32 = 3, + }; + + union { + BitField<0, 16, Zdir> dir; + BitField<16, 16, Zformat> format; + }; + }; + + struct IteratedBlend { + union { + BitField<0, 1, u32> enable; + BitField<1, 1, u32> enable_alpha; + }; + u32 pass_count; + }; + + struct ZCullCriterion { + enum class Sfunc : u32 { + Never = 0, + Less = 1, + Equal = 2, + LessOrEqual = 3, + Greater = 4, + NotEqual = 5, + GreaterOrEqual = 6, + Always = 7, + }; + + union { + BitField<0, 8, Sfunc> sfunc; + BitField<8, 1, u32> no_invalidate; + BitField<9, 1, u32> force_match; + BitField<16, 8, u32> sref; + BitField<24, 8, u32> smask; + }; + }; + + struct LoadIteratedBlend { + enum class Test : u32 { + False = 0, + True = 1, + Equal = 2, + NotEqual = 3, + LessThan = 4, + LessOrEqual = 5, + Greater = 6, + GreaterOrEqual = 7, + }; + enum class Operation : u32 { + AddProducts = 0, + SubProducts = 1, + Min = 2, + Max = 3, + Reciprocal = 4, + Add = 5, + Sub = 6, + }; + enum class OperandA : u32 { + SrcRGB = 0, + DstRGB = 1, + SrcAAA = 2, + DstAAA = 3, + Temp0_RGB = 4, + Temp1_RGB = 5, + Temp2_RGB = 6, + PBR_RGB = 7, + }; + enum class OperandB : u32 { + Zero = 0, + One = 1, + SrcRGB = 2, + SrcAAA = 3, + OneMinusSrcAAA = 4, + DstRGB = 5, + DstAAA = 6, + OneMinusDstAAA = 7, + Temp0_RGB = 9, + Temp1_RGB = 10, + Temp2_RGB = 11, + PBR_RGB = 12, + ConstRGB = 13, + ZeroATimesB = 14, + }; + enum class Swizzle : u32 { + RGB = 0, + GBR = 1, + RRR = 2, + GGG = 3, + BBB = 4, + RToA = 5, + }; + enum class WriteMask : u32 { + RGB = 0, + ROnly = 1, + GOnly = 2, + BOnly = 3, + }; + enum class Pass : u32 { + Temp0 = 0, + Temp1 = 1, + Temp2 = 2, + None = 3, + }; + + u32 instruction_ptr; + union { + BitField<0, 3, Test> test; + BitField<3, 3, Operation> operation; + BitField<6, 3, u32> const_input; + BitField<9, 3, OperandA> operand_a; + BitField<12, 4, OperandB> operand_b; + BitField<16, 3, OperandA> operand_c; + BitField<19, 4, OperandB> operand_d; + BitField<23, 3, Swizzle> output_swizzle; + BitField<26, 2, WriteMask> output_mask; + BitField<28, 2, Pass> output_pass; + BitField<31, 1, u32> test_enabled; + }; + }; + + struct ScissorTest { + u32 enable; + union { + BitField<0, 16, u32> min_x; + BitField<16, 16, u32> max_x; + }; + union { + BitField<0, 16, u32> min_y; + BitField<16, 16, u32> max_y; + }; + INSERT_PADDING_BYTES_NOINIT(0x4); + }; + static_assert(sizeof(ScissorTest) == 0x10); + + struct VPCPerf { + union { + BitField<0, 8, u32> culled_small_lines; + BitField<8, 8, u32> culled_small_triangles; + BitField<16, 8, u32> nonculled_lines_and_points; + BitField<24, 8, u32> nonculled_triangles; + }; + }; + + struct ConstantColorRendering { + u32 enabled; + u32 red; + u32 green; + u32 blue; + u32 alpha; + }; + + struct VertexStreamSubstitute { u32 address_high; u32 address_low; - s32 buffer_size; - s32 buffer_offset; - INSERT_PADDING_WORDS_NOINIT(3); GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; } }; - static_assert(sizeof(TransformFeedbackBinding) == 32); - struct TransformFeedbackLayout { - u32 stream; - u32 varying_count; - u32 stride; - INSERT_PADDING_WORDS_NOINIT(1); + struct VTGWarpWatermarks { + union { + BitField<0, 16, u32> low; + BitField<16, 16, u32> high; + }; }; - static_assert(sizeof(TransformFeedbackLayout) == 16); - bool IsShaderConfigEnabled(std::size_t index) const { - // The VertexB is always enabled. - if (index == static_cast(Regs::ShaderProgram::VertexB)) { - return true; + struct SampleMask { + struct Target { + union { + BitField<0, 1, u32> raster_out; + BitField<4, 1, u32> color_target; + }; + u32 target; + }; + struct Pos { + u32 x0_y0; + u32 x1_y0; + u32 x0_y1; + u32 x1_y1; + }; + }; + + enum class NonMultisampledZ : u32 { + PerSample = 0, + PixelCenter = 1, + }; + + enum class TIRMode : u32 { + Disabled = 0, + RasterNTargetM = 1, + }; + + enum class AntiAliasRaster : u32 { + Mode1x1 = 0, + Mode2x2 = 2, + Mode4x2_D3D = 4, + Mode2x1_D3D = 5, + Mode4x4 = 6, + }; + + struct SurfaceClipIDMemory { + u32 address_high; + u32 address_low; + + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; } - return shader_config[index].enable != 0; - } + }; - bool IsShaderConfigEnabled(Regs::ShaderProgram type) const { - return IsShaderConfigEnabled(static_cast(type)); - } + struct TIRModulation { + enum class Component : u32 { + None = 0, + RGB = 1, + AlphaOnly = 2, + RGBA = 3, + }; + enum class Function : u32 { + Linear = 0, + Table = 1, + }; + Component component; + Function function; + }; - union { - struct { - INSERT_PADDING_WORDS_NOINIT(0x44); + struct Zeta { + u32 address_high; + u32 address_low; + Tegra::DepthFormat format; + TileMode tile_mode; + u32 array_pitch; - u32 wait_for_idle; + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + }; - struct { - u32 upload_address; - u32 data; - u32 entry; - u32 bind; - } macros; + struct SurfaceClip { + union { + BitField<0, 16, u32> x; + BitField<16, 16, u32> width; + }; + union { + BitField<0, 16, u32> y; + BitField<16, 16, u32> height; + }; + }; - ShadowRamControl shadow_ram_control; + enum class L2CacheControlPolicy : u32 { + First = 0, + Normal = 1, + Last = 2, + }; - INSERT_PADDING_WORDS_NOINIT(0x16); + struct L2CacheVAFRequests { + union { + BitField<0, 1, u32> system_memory_volatile; + BitField<4, 2, L2CacheControlPolicy> policy; + }; + }; - Upload::Registers upload; - struct { - union { - BitField<0, 1, u32> linear; - }; - } exec_upload; + enum class ViewportMulticast : u32 { + ViewportOrder = 0, + PrimitiveOrder = 1, + }; - u32 data_upload; + struct TIRModulationCoeff { + union { + BitField<0, 8, u32> table_v0; + BitField<8, 8, u32> table_v1; + BitField<16, 8, u32> table_v2; + BitField<24, 8, u32> table_v3; + }; + }; + static_assert(sizeof(TIRModulationCoeff) == 0x4); - INSERT_PADDING_WORDS_NOINIT(0x16); + struct ReduceColorThreshold { + union { + BitField<0, 8, u32> all_hit_once; + BitField<16, 8, u32> all_covered; + }; + }; - u32 force_early_fragment_tests; + struct ClearControl { + union { + BitField<0, 1, u32> respect_stencil_mask; + BitField<4, 1, u32> use_clear_rect; + BitField<8, 1, u32> use_scissor; + BitField<12, 1, u32> use_viewport_clip0; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0x2D); - - struct { - union { - BitField<0, 16, u32> sync_point; - BitField<16, 1, u32> unknown; - BitField<20, 1, u32> increment; - }; - } sync_info; - - INSERT_PADDING_WORDS_NOINIT(0x15); + struct L2CacheRopNonInterlockedReads { + union { + BitField<4, 2, L2CacheControlPolicy> policy; + }; + }; + struct VertexOutputAttributeSkipMasks { + struct Attributes { union { - BitField<0, 2, TessellationPrimitive> prim; - BitField<4, 2, TessellationSpacing> spacing; - BitField<8, 1, u32> cw; - BitField<9, 1, u32> connected; - } tess_mode; + BitField<0, 1, u32> attribute0_comp0; + BitField<1, 1, u32> attribute0_comp1; + BitField<2, 1, u32> attribute0_comp2; + BitField<3, 1, u32> attribute0_comp3; + BitField<4, 1, u32> attribute1_comp0; + BitField<5, 1, u32> attribute1_comp1; + BitField<6, 1, u32> attribute1_comp2; + BitField<7, 1, u32> attribute1_comp3; + BitField<8, 1, u32> attribute2_comp0; + BitField<9, 1, u32> attribute2_comp1; + BitField<10, 1, u32> attribute2_comp2; + BitField<11, 1, u32> attribute2_comp3; + BitField<12, 1, u32> attribute3_comp0; + BitField<13, 1, u32> attribute3_comp1; + BitField<14, 1, u32> attribute3_comp2; + BitField<15, 1, u32> attribute3_comp3; + BitField<16, 1, u32> attribute4_comp0; + BitField<17, 1, u32> attribute4_comp1; + BitField<18, 1, u32> attribute4_comp2; + BitField<19, 1, u32> attribute4_comp3; + BitField<20, 1, u32> attribute5_comp0; + BitField<21, 1, u32> attribute5_comp1; + BitField<22, 1, u32> attribute5_comp2; + BitField<23, 1, u32> attribute5_comp3; + BitField<24, 1, u32> attribute6_comp0; + BitField<25, 1, u32> attribute6_comp1; + BitField<26, 1, u32> attribute6_comp2; + BitField<27, 1, u32> attribute6_comp3; + BitField<28, 1, u32> attribute7_comp0; + BitField<29, 1, u32> attribute7_comp1; + BitField<30, 1, u32> attribute7_comp2; + BitField<31, 1, u32> attribute7_comp3; + }; + }; - std::array tess_level_outer; - std::array tess_level_inner; + std::array a; + std::array b; + }; - INSERT_PADDING_WORDS_NOINIT(0x10); + struct TIRControl { + union { + BitField<0, 1, u32> z_pass_pixel_count_use_raster_samples; + BitField<4, 1, u32> alpha_coverage_use_raster_samples; + BitField<1, 1, u32> reduce_coverage; + }; + }; - u32 rasterize_enable; + enum class FillViaTriangleMode : u32 { + Disabled = 0, + FillAll = 1, + FillBoundingBox = 2, + }; - std::array tfb_bindings; + struct PsTicketDispenserValue { + union { + BitField<0, 8, u32> index; + BitField<8, 16, u32> value; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0xC0); + struct RegisterWatermarks { + union { + BitField<0, 16, u32> low; + BitField<16, 16, u32> high; + }; + }; - std::array tfb_layouts; + enum class InvalidateCacheLines : u32 { + All = 0, + One = 1, + }; - INSERT_PADDING_WORDS_NOINIT(0x1); + struct InvalidateTextureDataCacheNoWfi { + union { + BitField<0, 1, InvalidateCacheLines> lines; + BitField<4, 22, u32> tag; + }; + }; - u32 tfb_enabled; + struct ZCullRegionEnable { + union { + BitField<0, 1, u32> enable_z; + BitField<4, 1, u32> enable_stencil; + BitField<1, 1, u32> rect_clear; + BitField<2, 1, u32> use_rt_array_index; + BitField<5, 16, u32> rt_array_index; + BitField<3, 1, u32> make_conservative; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0x2E); + enum class FillMode : u32 { + Point = 1, + Wireframe = 2, + Solid = 3, + }; - std::array rt; + enum class ShadeMode : u32 { + Flat = 0x1, + Gouraud = 0x2, + GL_Flat = 0x1D00, + GL_Smooth = 0x1D01, + }; - std::array viewport_transform; + enum class AlphaToCoverageDither : u32 { + Footprint_1x1 = 0, + Footprint_2x2 = 1, + Footprint_1x1_Virtual = 2, + }; - std::array viewports; + struct InlineIndex4x8 { + union { + BitField<0, 30, u32> count; + BitField<30, 2, u32> start; + }; + union { + BitField<0, 8, u32> index0; + BitField<8, 8, u32> index1; + BitField<16, 8, u32> index2; + BitField<24, 8, u32> index3; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0x1D); + enum class D3DCullMode : u32 { + None = 0, + CW = 1, + CCW = 2, + }; - struct { - u32 first; - u32 count; - } vertex_buffer; + struct BlendColor { + f32 r; + f32 g; + f32 b; + f32 a; + }; - DepthMode depth_mode; + struct StencilOp { + enum class Op : u32 { + Keep_D3D = 1, + Zero_D3D = 2, + Replace_D3D = 3, + IncrSaturate_D3D = 4, + DecrSaturate_D3D = 5, + Invert_D3D = 6, + Incr_D3D = 7, + Decr_D3D = 8, - float clear_color[4]; - float clear_depth; + Keep_GL = 0x1E00, + Zero_GL = 0, + Replace_GL = 0x1E01, + IncrSaturate_GL = 0x1E02, + DecrSaturate_GL = 0x1E03, + Invert_GL = 0x150A, + Incr_GL = 0x8507, + Decr_GL = 0x8508, + }; - INSERT_PADDING_WORDS_NOINIT(0x3); + Op fail; + Op zfail; + Op zpass; + ComparisonOp func; + }; - s32 clear_stencil; + struct PsSaturate { + // Opposite of DepthMode + enum class Depth : u32 { + ZeroToOne = 0, + MinusOneToOne = 1, + }; - INSERT_PADDING_WORDS_NOINIT(0x2); + union { + BitField<0, 1, u32> output0_enable; + BitField<1, 1, Depth> output0_range; + BitField<4, 1, u32> output1_enable; + BitField<5, 1, Depth> output1_range; + BitField<8, 1, u32> output2_enable; + BitField<9, 1, Depth> output2_range; + BitField<12, 1, u32> output3_enable; + BitField<13, 1, Depth> output3_range; + BitField<16, 1, u32> output4_enable; + BitField<17, 1, Depth> output4_range; + BitField<20, 1, u32> output5_enable; + BitField<21, 1, Depth> output5_range; + BitField<24, 1, u32> output6_enable; + BitField<25, 1, Depth> output6_range; + BitField<28, 1, u32> output7_enable; + BitField<29, 1, Depth> output7_range; + }; - PolygonMode polygon_mode_front; - PolygonMode polygon_mode_back; + bool AnyEnabled() const { + return output0_enable || output1_enable || output2_enable || output3_enable || + output4_enable || output5_enable || output6_enable || output7_enable; + } + }; - INSERT_PADDING_WORDS_NOINIT(0x3); + struct WindowOrigin { + enum class Mode : u32 { + UpperLeft = 0, + LowerLeft = 1, + }; + union { + BitField<0, 1, Mode> mode; + BitField<4, 1, u32> flip_y; + }; + }; - u32 polygon_offset_point_enable; - u32 polygon_offset_line_enable; - u32 polygon_offset_fill_enable; - - u32 patch_vertices; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - u32 fragment_barrier; - - INSERT_PADDING_WORDS_NOINIT(0x7); - - std::array scissor_test; - - INSERT_PADDING_WORDS_NOINIT(0x15); - - s32 stencil_back_func_ref; - u32 stencil_back_mask; - u32 stencil_back_func_mask; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - u32 invalidate_texture_data_cache; - - INSERT_PADDING_WORDS_NOINIT(0x1); - - u32 tiled_cache_barrier; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - u32 color_mask_common; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - f32 depth_bounds[2]; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - u32 rt_separate_frag_data; - - INSERT_PADDING_WORDS_NOINIT(0x1); - - u32 multisample_raster_enable; - u32 multisample_raster_samples; - std::array multisample_sample_mask; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - struct { - u32 address_high; - u32 address_low; - Tegra::DepthFormat format; - TileMode tile_mode; - u32 layer_stride; - - GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); - } - } zeta; - - struct { - union { - BitField<0, 16, u32> x; - BitField<16, 16, u32> width; - }; - union { - BitField<0, 16, u32> y; - BitField<16, 16, u32> height; - }; - } render_area; - - INSERT_PADDING_WORDS_NOINIT(0x3F); + struct IteratedBlendConstants { + u32 r; + u32 g; + u32 b; + INSERT_PADDING_BYTES_NOINIT(0x4); + }; + static_assert(sizeof(IteratedBlendConstants) == 0x10); + struct UserClip { + struct Enable { union { - BitField<0, 4, u32> stencil; - BitField<4, 4, u32> unknown; - BitField<8, 4, u32> scissor; - BitField<12, 4, u32> viewport; - } clear_flags; - - INSERT_PADDING_WORDS_NOINIT(0x10); - - u32 fill_rectangle; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - u32 conservative_raster_enable; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - std::array vertex_attrib_format; - - std::array multisample_sample_locations; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - union { - BitField<0, 1, u32> enable; - BitField<4, 3, u32> target; - } multisample_coverage_to_color; - - INSERT_PADDING_WORDS_NOINIT(0x8); - - struct { - union { - BitField<0, 4, u32> count; - BitField<4, 3, u32> map_0; - BitField<7, 3, u32> map_1; - BitField<10, 3, u32> map_2; - BitField<13, 3, u32> map_3; - BitField<16, 3, u32> map_4; - BitField<19, 3, u32> map_5; - BitField<22, 3, u32> map_6; - BitField<25, 3, u32> map_7; - }; - - u32 Map(std::size_t index) const { - const std::array maps{map_0, map_1, map_2, map_3, - map_4, map_5, map_6, map_7}; - ASSERT(index < maps.size()); - return maps[index]; - } - } rt_control; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - u32 zeta_width; - u32 zeta_height; - union { - BitField<0, 16, u32> zeta_depth; - BitField<16, 1, u32> zeta_volume; + u32 raw; + BitField<0, 1, u32> plane0; + BitField<1, 1, u32> plane1; + BitField<2, 1, u32> plane2; + BitField<3, 1, u32> plane3; + BitField<4, 1, u32> plane4; + BitField<5, 1, u32> plane5; + BitField<6, 1, u32> plane6; + BitField<7, 1, u32> plane7; }; - SamplerIndex sampler_index; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - std::array gp_passthrough_mask; - - INSERT_PADDING_WORDS_NOINIT(0x1B); - - u32 depth_test_enable; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - u32 independent_blend_enable; - - u32 depth_write_enabled; - - u32 alpha_test_enabled; - - INSERT_PADDING_WORDS_NOINIT(0x6); - - u32 d3d_cull_mode; - - ComparisonOp depth_test_func; - float alpha_test_ref; - ComparisonOp alpha_test_func; - u32 draw_tfb_stride; - struct { - float r; - float g; - float b; - float a; - } blend_color; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - struct { - u32 separate_alpha; - Blend::Equation equation_rgb; - Blend::Factor factor_source_rgb; - Blend::Factor factor_dest_rgb; - Blend::Equation equation_a; - Blend::Factor factor_source_a; - INSERT_PADDING_WORDS_NOINIT(1); - Blend::Factor factor_dest_a; - - u32 enable_common; - u32 enable[NumRenderTargets]; - } blend; - - u32 stencil_enable; - StencilOp stencil_front_op_fail; - StencilOp stencil_front_op_zfail; - StencilOp stencil_front_op_zpass; - ComparisonOp stencil_front_func_func; - s32 stencil_front_func_ref; - u32 stencil_front_func_mask; - u32 stencil_front_mask; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - u32 frag_color_clamp; - - union { - BitField<0, 1, u32> y_negate; - BitField<4, 1, u32> triangle_rast_flip; - } screen_y_control; - - float line_width_smooth; - float line_width_aliased; - - INSERT_PADDING_WORDS_NOINIT(0x1B); - - u32 invalidate_sampler_cache_no_wfi; - u32 invalidate_texture_header_cache_no_wfi; - - INSERT_PADDING_WORDS_NOINIT(0x2); - - u32 vb_element_base; - u32 vb_base_instance; - - INSERT_PADDING_WORDS_NOINIT(0x35); - - u32 clip_distance_enabled; - - u32 samplecnt_enable; - - float point_size; - - INSERT_PADDING_WORDS_NOINIT(0x1); - - u32 point_sprite_enable; - - INSERT_PADDING_WORDS_NOINIT(0x3); - - CounterReset counter_reset; - - u32 multisample_enable; - - u32 zeta_enable; - - union { - BitField<0, 1, u32> alpha_to_coverage; - BitField<4, 1, u32> alpha_to_one; - } multisample_control; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - struct { - u32 address_high; - u32 address_low; - ConditionMode mode; - - GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); - } - } condition; - - struct { - u32 address_high; - u32 address_low; - u32 limit; - - GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); - } - } tsc; - - INSERT_PADDING_WORDS_NOINIT(0x1); - - float polygon_offset_factor; - - u32 line_smooth_enable; - - struct { - u32 address_high; - u32 address_low; - u32 limit; - - GPUVAddr Address() const { - return static_cast((static_cast(address_high) << 32) | - address_low); - } - } tic; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - u32 stencil_two_side_enable; - StencilOp stencil_back_op_fail; - StencilOp stencil_back_op_zfail; - StencilOp stencil_back_op_zpass; - ComparisonOp stencil_back_func_func; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - u32 framebuffer_srgb; - - float polygon_offset_units; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - Tegra::Texture::MsaaMode multisample_mode; - - INSERT_PADDING_WORDS_NOINIT(0xC); - - union { - BitField<2, 1, u32> coord_origin; - BitField<3, 10, u32> enable; - } point_coord_replace; - - struct { - u32 code_address_high; - u32 code_address_low; - - GPUVAddr CodeAddress() const { - return static_cast( - (static_cast(code_address_high) << 32) | code_address_low); - } - } code_address; - INSERT_PADDING_WORDS_NOINIT(1); - - struct { - u32 vertex_end_gl; - union { - u32 vertex_begin_gl; - BitField<0, 16, PrimitiveTopology> topology; - BitField<26, 1, u32> instance_next; - BitField<27, 1, u32> instance_cont; - }; - } draw; - - INSERT_PADDING_WORDS_NOINIT(0xA); - - struct { - u32 enabled; - u32 index; - } primitive_restart; - - INSERT_PADDING_WORDS_NOINIT(0xE); - - u32 provoking_vertex_last; - - INSERT_PADDING_WORDS_NOINIT(0x50); - - struct { - u32 start_addr_high; - u32 start_addr_low; - u32 end_addr_high; - u32 end_addr_low; - IndexFormat format; - u32 first; - u32 count; - - unsigned FormatSizeInBytes() const { - switch (format) { - case IndexFormat::UnsignedByte: - return 1; - case IndexFormat::UnsignedShort: - return 2; - case IndexFormat::UnsignedInt: - return 4; - } - ASSERT(false); - return 1; - } - - GPUVAddr StartAddress() const { - return static_cast( - (static_cast(start_addr_high) << 32) | start_addr_low); - } - - GPUVAddr EndAddress() const { - return static_cast((static_cast(end_addr_high) << 32) | - end_addr_low); - } - - /// Adjust the index buffer offset so it points to the first desired index. - GPUVAddr IndexStart() const { - return StartAddress() + static_cast(first) * - static_cast(FormatSizeInBytes()); - } - } index_array; - - union { - BitField<0, 16, u32> first; - BitField<16, 16, u32> count; - } small_index; - - union { - BitField<0, 16, u32> first; - BitField<16, 16, u32> count; - } small_index_2; - - INSERT_PADDING_WORDS_NOINIT(0x5); - - INSERT_PADDING_WORDS_NOINIT(0x1F); - - float polygon_offset_clamp; - - struct { - u32 is_instanced[NumVertexArrays]; - - /// Returns whether the vertex array specified by index is supposed to be - /// accessed per instance or not. - bool IsInstancingEnabled(std::size_t index) const { - return is_instanced[index]; - } - } instanced_arrays; - - INSERT_PADDING_WORDS_NOINIT(0x4); - - union { - BitField<0, 1, u32> enable; - BitField<4, 8, u32> unk4; - } vp_point_size; - - INSERT_PADDING_WORDS_NOINIT(1); - - u32 cull_test_enabled; - FrontFace front_face; - CullFace cull_face; - - u32 pixel_center_integer; - - INSERT_PADDING_WORDS_NOINIT(0x1); - - u32 viewport_transform_enabled; - - INSERT_PADDING_WORDS_NOINIT(0x3); - - union { - BitField<0, 1, u32> depth_range_0_1; - BitField<3, 1, u32> depth_clamp_near; - BitField<4, 1, u32> depth_clamp_far; - BitField<11, 1, u32> depth_clamp_disabled; - } view_volume_clip_control; - - INSERT_PADDING_WORDS_NOINIT(0xC); - - PrimitiveTopologyOverride topology_override; - - INSERT_PADDING_WORDS_NOINIT(0x12); - - u32 depth_bounds_enable; - - INSERT_PADDING_WORDS_NOINIT(1); - - struct { - u32 enable; - LogicOperation operation; - } logic_op; - - INSERT_PADDING_WORDS_NOINIT(0x1); + bool AnyEnabled() const { + return plane0 || plane1 || plane2 || plane3 || plane4 || plane5 || plane6 || + plane7; + } + }; + + struct Op { + enum class ClipOrCull : u32 { + Clip = 0, + Cull = 1, + }; union { u32 raw; - BitField<0, 1, u32> Z; - BitField<1, 1, u32> S; - BitField<2, 1, u32> R; - BitField<3, 1, u32> G; - BitField<4, 1, u32> B; - BitField<5, 1, u32> A; - BitField<6, 4, u32> RT; - BitField<10, 11, u32> layer; - } clear_buffers; - INSERT_PADDING_WORDS_NOINIT(0xB); - std::array color_mask; - INSERT_PADDING_WORDS_NOINIT(0x38); + BitField<0, 1, ClipOrCull> plane0; + BitField<4, 1, ClipOrCull> plane1; + BitField<8, 1, ClipOrCull> plane2; + BitField<12, 1, ClipOrCull> plane3; + BitField<16, 1, ClipOrCull> plane4; + BitField<20, 1, ClipOrCull> plane5; + BitField<24, 1, ClipOrCull> plane6; + BitField<28, 1, ClipOrCull> plane7; + }; + }; + }; - struct { - u32 query_address_high; - u32 query_address_low; - u32 query_sequence; - union { - u32 raw; - BitField<0, 2, QueryOperation> operation; - BitField<4, 1, u32> fence; - BitField<12, 4, QueryUnit> unit; - BitField<16, 1, QuerySyncCondition> sync_cond; - BitField<23, 5, QuerySelect> select; - BitField<28, 1, u32> short_query; - } query_get; + struct AntiAliasAlphaControl { + union { + BitField<0, 1, u32> alpha_to_coverage; + BitField<4, 1, u32> alpha_to_one; + }; + }; - GPUVAddr QueryAddress() const { - return static_cast( - (static_cast(query_address_high) << 32) | query_address_low); - } - } query; + struct RenderEnable { + enum class Override : u32 { + UseRenderEnable = 0, + AlwaysRender = 1, + NeverRender = 2, + }; - INSERT_PADDING_WORDS_NOINIT(0x3C); + enum class Mode : u32 { + False = 0, + True = 1, + Conditional = 2, + IfEqual = 3, + IfNotEqual = 4, + }; - struct { - union { - BitField<0, 12, u32> stride; - BitField<12, 1, u32> enable; - }; - u32 start_high; - u32 start_low; - u32 divisor; + u32 address_high; + u32 address_low; + Mode mode; - GPUVAddr StartAddress() const { - return static_cast((static_cast(start_high) << 32) | - start_low); - } + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + }; - bool IsEnabled() const { - return enable != 0 && StartAddress() != 0; - } + struct TexSampler { + u32 address_high; + u32 address_low; + u32 limit; - } vertex_array[NumVertexArrays]; + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + }; - Blend independent_blend[NumRenderTargets]; + struct TexHeader { + u32 address_high; + u32 address_low; + u32 limit; - struct { - u32 limit_high; - u32 limit_low; + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + }; - GPUVAddr LimitAddress() const { - return static_cast((static_cast(limit_high) << 32) | - limit_low); - } - } vertex_array_limit[NumVertexArrays]; + enum class ZCullRegionFormat : u32 { + Z_4x4 = 0, + ZS_4x4 = 1, + Z_4x2 = 2, + Z_2x4 = 3, + Z_16x8_4x4 = 4, + Z_8x8_4x2 = 5, + Z_8x8_2x4 = 6, + Z_16x16_4x8 = 7, + Z_4x8_2x2 = 8, + ZS_16x8_4x2 = 9, + ZS_16x8_2x4 = 10, + ZS_8x8_2x2 = 11, + Z_4x8_1x1 = 12, + }; - struct { - union { - BitField<0, 1, u32> enable; - BitField<4, 4, ShaderProgram> program; - }; - u32 offset; - INSERT_PADDING_WORDS_NOINIT(14); - } shader_config[MaxShaderProgram]; + struct RtLayer { + enum class Control { + LayerSelectsLayer = 0, + GeometryShaderSelectsLayer = 1, + }; - INSERT_PADDING_WORDS_NOINIT(0x60); + union { + BitField<0, 16, u32> layer; + BitField<16, 1, u32> control; + }; + }; - u32 firmware[0x20]; + struct InlineIndex2x16 { + union { + BitField<0, 31, u32> count; + BitField<31, 1, u32> start_odd; + }; + union { + BitField<0, 16, u32> even; + BitField<16, 16, u32> odd; + }; + }; - struct { - u32 cb_size; - u32 cb_address_high; - u32 cb_address_low; - u32 cb_pos; - std::array cb_data; + struct VertexGlobalBaseOffset { + u32 address_high; + u32 address_low; - GPUVAddr BufferAddress() const { - return static_cast( - (static_cast(cb_address_high) << 32) | cb_address_low); - } - } const_buffer; + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + }; - INSERT_PADDING_WORDS_NOINIT(0x10); + struct ZCullRegionPixelOffset { + u32 width; + u32 height; + }; - struct { - union { - u32 raw_config; - BitField<0, 1, u32> valid; - BitField<4, 5, u32> index; - }; - INSERT_PADDING_WORDS_NOINIT(7); - } cb_bind[MaxShaderStage]; + struct PointSprite { + enum class RMode : u32 { + Zero = 0, + FromR = 1, + FromS = 2, + }; + enum class Origin : u32 { + Bottom = 0, + Top = 1, + }; + enum class Texture : u32 { + Passthrough = 0, + Generate = 1, + }; - INSERT_PADDING_WORDS_NOINIT(0x56); + union { + BitField<0, 2, RMode> rmode; + BitField<2, 1, Origin> origin; + BitField<3, 1, Texture> texture0; + BitField<4, 1, Texture> texture1; + BitField<5, 1, Texture> texture2; + BitField<6, 1, Texture> texture3; + BitField<7, 1, Texture> texture4; + BitField<8, 1, Texture> texture5; + BitField<9, 1, Texture> texture6; + BitField<10, 1, Texture> texture7; + BitField<11, 1, Texture> texture8; + BitField<12, 1, Texture> texture9; + }; + }; - u32 tex_cb_index; + struct ProgramRegion { + u32 address_high; + u32 address_low; - INSERT_PADDING_WORDS_NOINIT(0x7D); + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + }; - std::array, NumTransformFeedbackBuffers> tfb_varying_locs; + struct DefaultAttributes { + enum class Diffuse : u32 { + Vector_0001 = 0, + Vector_1111 = 1, + }; + enum class Specular : u32 { + Vector_0000 = 0, + Vector_0001 = 1, + }; + enum class Vector : u32 { + Vector_0000 = 0, + Vector_0001 = 1, + }; + enum class FixedFncTexture : u32 { + Vector_0000 = 0, + Vector_0001 = 1, + }; + enum class DX9Color0 : u32 { + Vector_0000 = 0, + Vector_1111 = 1, + }; + enum class DX9Color1To15 : u32 { + Vector_0000 = 0, + Vector_0001 = 1, + }; - INSERT_PADDING_WORDS_NOINIT(0x298); + union { + BitField<0, 1, Diffuse> color_front_diffuse; + BitField<1, 1, Specular> color_front_specular; + BitField<2, 1, Vector> generic_vector; + BitField<3, 1, FixedFncTexture> fixed_fnc_texture; + BitField<4, 1, DX9Color0> dx9_color0; + BitField<5, 1, DX9Color1To15> dx9_color1_to_15; + }; + }; - struct { - /// Compressed address of a buffer that holds information about bound SSBOs. - /// This address is usually bound to c0 in the shaders. - u32 buffer_address; + struct Draw { + enum class PrimitiveId : u32 { + First = 0, + Unchanged = 1, + }; + enum class InstanceId : u32 { + First = 0, + Subsequent = 1, + Unchanged = 2, + }; + enum class SplitMode : u32 { + NormalBeginNormal = 0, + NormalBeginOpen = 1, + OpenBeginOpen = 2, + OpenBeginNormal = 3, + }; - GPUVAddr BufferAddress() const { - return static_cast(buffer_address) << 8; - } - } ssbo_info; + u32 end; + union { + u32 begin; + BitField<0, 16, PrimitiveTopology> topology; + BitField<24, 1, PrimitiveId> primitive_id; + BitField<26, 2, InstanceId> instance_id; + BitField<29, 2, SplitMode> split_mode; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0x11); + struct VertexIdCopy { + union { + BitField<0, 1, u32> enable; + BitField<4, 8, u32> attribute_slot; + }; + }; - struct { - u32 address[MaxShaderStage]; - u32 size[MaxShaderStage]; - } tex_info_buffers; + struct ShaderBasedCull { + union { + BitField<1, 1, u32> batch_cull_enable; + BitField<0, 1, u32> before_fetch_enable; + }; + }; - INSERT_PADDING_WORDS_NOINIT(0xCC); + struct ClassVersion { + union { + BitField<0, 16, u32> current; + BitField<16, 16, u32> oldest_supported; + }; + }; + + struct PrimitiveRestart { + u32 enabled; + u32 index; + }; + + struct OutputVertexId { + union { + BitField<12, 1, u32> uses_array_start; + }; + }; + + enum class PointCenterMode : u32 { + GL = 0, + D3D = 1, + }; + + enum class LineSmoothParams : u32 { + Falloff_1_00 = 0, + Falloff_1_33 = 1, + Falloff_1_66 = 2, + }; + + struct LineSmoothEdgeTable { + union { + BitField<0, 8, u32> v0; + BitField<8, 8, u32> v1; + BitField<16, 8, u32> v2; + BitField<24, 8, u32> v3; + }; + }; + + struct LineStippleParams { + union { + BitField<0, 8, u32> factor; + BitField<8, 16, u32> pattern; + }; + }; + + enum class ProvokingVertex : u32 { + First = 0, + Last = 1, + }; + + struct ShaderControl { + enum class Partial : u32 { + Zero = 0, + Infinity = 1, + }; + enum class FP32NanBehavior : u32 { + Legacy = 0, + FP64Compatible = 1, + }; + enum class FP32F2INanBehavior : u32 { + PassZero = 0, + PassIndefinite = 1, + }; + + union { + BitField<0, 1, Partial> default_partial; + BitField<1, 1, FP32NanBehavior> fp32_nan_behavior; + BitField<2, 1, FP32F2INanBehavior> fp32_f2i_nan_behavior; + }; + }; + + struct SphVersion { + union { + BitField<0, 16, u32> current; + BitField<16, 16, u32> oldest_supported; + }; + }; + + struct AlphaToCoverageOverride { + union { + BitField<0, 1, u32> qualify_by_anti_alias_enable; + BitField<1, 1, u32> qualify_by_ps_sample_mask_enable; + }; + }; + + struct AamVersion { + union { + BitField<0, 16, u32> current; + BitField<16, 16, u32> oldest_supported; + }; + }; + + struct IndexBuffer { + u32 start_addr_high; + u32 start_addr_low; + u32 limit_addr_high; + u32 limit_addr_low; + IndexFormat format; + u32 first; + u32 count; + + unsigned FormatSizeInBytes() const { + switch (format) { + case IndexFormat::UnsignedByte: + return 1; + case IndexFormat::UnsignedShort: + return 2; + case IndexFormat::UnsignedInt: + return 4; + } + ASSERT(false); + return 1; + } + + GPUVAddr StartAddress() const { + return (GPUVAddr{start_addr_high} << 32) | GPUVAddr{start_addr_low}; + } + + GPUVAddr EndAddress() const { + return (GPUVAddr{limit_addr_high} << 32) | GPUVAddr{limit_addr_low}; + } + + /// Adjust the index buffer offset so it points to the first desired index. + GPUVAddr IndexStart() const { + return StartAddress() + size_t{first} * size_t{FormatSizeInBytes()}; + } + }; + + struct IndexBufferSmall { + union { + u32 raw; + BitField<0, 16, u32> first; + BitField<16, 12, u32> count; + BitField<28, 4, PrimitiveTopology> topology; + }; + }; + + struct VertexStreamInstances { + std::array is_instanced; + + /// Returns whether the vertex array specified by index is supposed to be + /// accessed per instance or not. + bool IsInstancingEnabled(std::size_t index) const { + return is_instanced[index]; + } + }; + + struct AttributePointSize { + union { + BitField<0, 1, u32> enabled; + BitField<4, 8, u32> slot; + }; + }; + + struct ViewportClipControl { + enum class GeometryGuardband : u32 { + Scale256 = 0, + Scale1 = 1, + }; + enum class GeometryClip : u32 { + WZero = 0, + Passthrough = 1, + FrustumXY = 2, + FrustumXYZ = 3, + WZeroNoZCull = 4, + FrustumZ = 5, + WZeroTriFillOrClip = 6, + }; + enum class GeometryGuardbandZ : u32 { + SameAsXY = 0, + Scale256 = 1, + Scale1 = 2, + }; + + union { + BitField<0, 1, u32> depth_0_to_1; + BitField<3, 1, u32> pixel_min_z; + BitField<4, 1, u32> pixel_max_z; + BitField<7, 1, GeometryGuardband> geometry_guardband; + BitField<11, 3, GeometryClip> geometry_clip; + BitField<1, 2, GeometryGuardbandZ> geometry_guardband_z; + }; + }; + + enum class PrimitiveTopologyControl : u32 { + UseInBeginMethods = 0, + UseSeparateState = 1, + }; + + struct WindowClip { + enum class Type : u32 { + Inclusive = 0, + Exclusive = 1, + ClipAll = 2, + }; + + u32 enable; + Type type; + }; + + enum class InvalidateZCull : u32 { + Invalidate = 0, + }; + + struct ZCull { + union { + BitField<0, 1, u32> z_enable; + BitField<1, 1, u32> stencil_enable; + }; + union { + BitField<0, 1, u32> z_min_enbounded; + BitField<1, 1, u32> z_max_unbounded; + }; + }; + + struct LogicOp { + enum class Op : u32 { + Clear = 0x1500, + And = 0x1501, + AndReverse = 0x1502, + Copy = 0x1503, + AndInverted = 0x1504, + NoOp = 0x1505, + Xor = 0x1506, + Or = 0x1507, + Nor = 0x1508, + Equiv = 0x1509, + Invert = 0x150A, + OrReverse = 0x150B, + CopyInverted = 0x150C, + OrInverted = 0x150D, + Nand = 0x150E, + Set = 0x150F, + }; + + u32 enable; + Op op; + }; + + struct ClearSurface { + union { + u32 raw; + BitField<0, 1, u32> Z; + BitField<1, 1, u32> S; + BitField<2, 1, u32> R; + BitField<3, 1, u32> G; + BitField<4, 1, u32> B; + BitField<5, 1, u32> A; + BitField<6, 4, u32> RT; + BitField<10, 16, u32> layer; + }; + }; + + struct ReportSemaphore { + struct Compare { + u32 initial_sequence; + u32 initial_mode; + u32 unknown1; + u32 unknown2; + u32 current_sequence; + u32 current_mode; + }; + + enum class Operation : u32 { + Release = 0, + Acquire = 1, + ReportOnly = 2, + Trap = 3, + }; + + enum class Release : u32 { + AfterAllPreceedingReads = 0, + AfterAllPreceedingWrites = 1, + }; + + enum class Acquire : u32 { + BeforeAnyFollowingWrites = 0, + BeforeAnyFollowingReads = 1, + }; + + enum class Location : u32 { + None = 0, + VertexFetch = 1, + VertexShader = 2, + VPC = 4, + StreamingOutput = 5, + GeometryShader = 6, + ZCull = 7, + TessellationInit = 8, + TessellationShader = 9, + PixelShader = 10, + DepthTest = 12, + All = 15, + }; + + enum class Comparison : u32 { + NotEqual = 0, + GreaterOrEqual = 1, + }; + + enum class Report : u32 { + Payload = 0, // "None" in docs, but confirmed via hardware to return the payload + VerticesGenerated = 1, + ZPassPixelCount = 2, + PrimitivesGenerated = 3, + AlphaBetaClocks = 4, + VertexShaderInvocations = 5, + StreamingPrimitivesNeededMinusSucceeded = 6, + GeometryShaderInvocations = 7, + GeometryShaderPrimitivesGenerated = 9, + ZCullStats0 = 10, + StreamingPrimitivesSucceeded = 11, + ZCullStats1 = 12, + StreamingPrimitivesNeeded = 13, + ZCullStats2 = 14, + ClipperInvocations = 15, + ZCullStats3 = 16, + ClipperPrimitivesGenerated = 17, + VtgPrimitivesOut = 18, + PixelShaderInvocations = 19, + ZPassPixelCount64 = 21, + IEEECleanColorTarget = 24, + IEEECleanZetaTarget = 25, + StreamingByteCount = 26, + TessellationInitInvocations = 27, + BoundingRectangle = 28, + TessellationShaderInvocations = 29, + TotalStreamingPrimitivesNeededMinusSucceeded = 30, + TessellationShaderPrimitivesGenerated = 31, + }; + + u32 address_high; + u32 address_low; + u32 payload; + union { + u32 raw; + BitField<0, 2, Operation> operation; + BitField<4, 1, Release> release; + BitField<8, 1, Acquire> acquire; + BitField<12, 4, Location> location; + BitField<16, 1, Comparison> comparison; + BitField<20, 1, u32> awaken_enable; + BitField<23, 5, Report> report; + BitField<28, 1, u32> short_query; + BitField<5, 3, u32> sub_report; + BitField<21, 1, u32> dword_number; + BitField<2, 1, u32> disable_flush; + BitField<3, 1, u32> reduction_enable; + BitField<9, 3, ReductionOp> reduction_op; + BitField<17, 2, u32> format_signed; + } query; + + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + }; + + struct VertexStream { + union { + BitField<0, 12, u32> stride; + BitField<12, 1, u32> enable; + }; + u32 address_high; + u32 address_low; + u32 frequency; + + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + + bool IsEnabled() const { + return enable != 0 && Address() != 0; + } + }; + static_assert(sizeof(VertexStream) == 0x10); + + struct VertexStreamLimit { + u32 address_high; + u32 address_low; + + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + }; + static_assert(sizeof(VertexStreamLimit) == 0x8); + + enum class ShaderType : u32 { + VertexA = 0, + VertexB = 1, + TessellationInit = 2, + Tessellation = 3, + Geometry = 4, + Pixel = 5, + }; + + struct Pipeline { + union { + BitField<0, 1, u32> enable; + BitField<4, 4, ShaderType> program; + }; + u32 offset; + u32 reservedA; + u32 register_count; + u32 binding_group; + std::array reserved; + INSERT_PADDING_BYTES_NOINIT(0x1C); + }; + static_assert(sizeof(Pipeline) == 0x40); + + bool IsShaderConfigEnabled(std::size_t index) const { + // The VertexB is always enabled. + if (index == static_cast(ShaderType::VertexB)) { + return true; + } + return pipelines[index].enable != 0; + } + + bool IsShaderConfigEnabled(ShaderType type) const { + return IsShaderConfigEnabled(static_cast(type)); + } + + struct ConstantBuffer { + u32 size; + u32 address_high; + u32 address_low; + u32 offset; + std::array buffer; + + GPUVAddr Address() const { + return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low}; + } + }; + + struct BindGroup { + std::array reserved; + union { + u32 raw_config; + BitField<0, 1, u32> valid; + BitField<4, 5, u32> shader_slot; + }; + INSERT_PADDING_BYTES_NOINIT(0xC); + }; + static_assert(sizeof(BindGroup) == 0x20); + + struct StreamOutLayout { + union { + BitField<0, 8, u32> attribute0; + BitField<8, 8, u32> attribute1; + BitField<16, 8, u32> attribute2; + BitField<24, 8, u32> attribute3; + }; + }; + + struct ShaderPerformance { + struct ControlA { + union { + BitField<0, 2, u32> event0; + BitField<2, 3, u32> bit0; + BitField<5, 2, u32> event1; + BitField<7, 3, u32> bit1; + BitField<10, 2, u32> event2; + BitField<12, 3, u32> bit2; + BitField<15, 2, u32> event3; + BitField<17, 3, u32> bit3; + BitField<20, 2, u32> event4; + BitField<22, 3, u32> bit4; + BitField<25, 2, u32> event5; + BitField<27, 3, u32> bit5; + BitField<30, 2, u32> spare; + }; + }; + + struct ControlB { + union { + BitField<0, 1, u32> edge; + BitField<1, 2, u32> mode; + BitField<3, 1, u32> windowed; + BitField<4, 16, u32> func; + }; + }; + + std::array values_upper; + std::array values; + std::array events; + std::array control_a; + std::array control_b; + u32 trap_control_mask; + u32 start_shader_mask; + u32 stop_shader_mask; + }; + + // clang-format off + union { + struct { + ID object_id; ///< 0x0000 + INSERT_PADDING_BYTES_NOINIT(0xFC); + u32 nop; ///< 0x0100 + Notify notify; ///< 0x0104 + u32 wait_for_idle; ///< 0x0110 + LoadMME load_mme; ///< 0x0114 + ShadowRamControl shadow_ram_control; ///< 0x0124 + PeerSemaphore peer; ///< 0x0128 + GlobalRender global_render; ///< 0x0130 + u32 go_idle; ///< 0x013C + u32 trigger; ///< 0x0140 + u32 trigger_wfi; ///< 0x0144 + INSERT_PADDING_BYTES_NOINIT(0x8); + u32 instrumentation_method_header; ///< 0x0150 + u32 instrumentation_method_data; ///< 0x0154 + INSERT_PADDING_BYTES_NOINIT(0x28); + Upload::Registers upload; ///< 0x0180 + LaunchDMA launch_dma; ///< 0x01B0 + u32 inline_data; ///< 0x01B4 + INSERT_PADDING_BYTES_NOINIT(0x24); + I2M i2m; ///< 0x01DC + u32 run_ds_now; ///< 0x0200 + OpportunisticEarlyZ opportunistic_early_z; ///< 0x0204 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 aliased_line_width_enabled; ///< 0x020C + u32 mandated_early_z; ///< 0x0210 + GeometryShaderDmFifo gs_dm_fifo; ///< 0x0214 + L2CacheControl l2_cache_control; ///< 0x0218 + InvalidateShaderCache invalidate_shader_cache; ///< 0x021C + INSERT_PADDING_BYTES_NOINIT(0xA8); + SyncInfo sync_info; ///< 0x02C8 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 prim_circular_buffer_throttle; ///< 0x02D0 + u32 flush_invalidate_rop_mini_cache; ///< 0x02D4 + SurfaceClipBlockId surface_clip_block_id; ///< 0x02D8 + u32 alpha_circular_buffer_size; ///< 0x02DC + DecompressSurface decompress_surface; ///< 0x02E0 + ZCullRopBypass zcull_rop_bypass; ///< 0x02E4 + ZCullSubregion zcull_subregion; ///< 0x02E8 + RasterBoundingBox raster_bounding_box; ///< 0x02EC + u32 peer_semaphore_release; ///< 0x02F0 + u32 iterated_blend_optimization; ///< 0x02F4 + ZCullSubregionAllocation zcull_subregion_allocation; ///< 0x02F8 + ZCullSubregionAlgorithm zcull_subregion_algorithm; ///< 0x02FC + PixelShaderOutputSampleMaskUsage ps_output_sample_mask_usage; ///< 0x0300 + u32 draw_zero_index; ///< 0x0304 + L1Configuration l1_configuration; ///< 0x0308 + u32 render_enable_control_load_const_buffer; ///< 0x030C + SPAVersion spa_version; ///< 0x0310 + u32 ieee_clean_update; ///< 0x0314 + SnapGrid snap_grid; ///< 0x0318 + Tessellation tessellation; ///< 0x0320 + SubTilingPerf sub_tiling_perf; ///< 0x0360 + ZCullSubregionReport zcull_subregion_report; ///< 0x036C + BalancedPrimitiveWorkload balanced_primitive_workload; ///< 0x0374 + u32 max_patches_per_batch; ///< 0x0378 + u32 rasterize_enable; ///< 0x037C + TransformFeedback transform_feedback; ///< 0x0380 + u32 raster_input; ///< 0x0740 + u32 transform_feedback_enabled; ///< 0x0744 + u32 primitive_restart_topology_change_enable; ///< 0x0748 + u32 alpha_fraction; ///< 0x074C + INSERT_PADDING_BYTES_NOINIT(0x4); + HybridAntiAliasControl hybrid_aa_control; ///< 0x0754 + INSERT_PADDING_BYTES_NOINIT(0x24); + ShaderLocalMemory shader_local_memory; ///< 0x077C + u32 color_zero_bandwidth_clear; ///< 0x07A4 + u32 z_zero_bandwidth_clear; ///< 0x07A8 + u32 isbe_save_restore_program_offset; ///< 0x07AC + INSERT_PADDING_BYTES_NOINIT(0x10); + ZCullRegion zcull_region; ///< 0x07C0 + ZetaReadOnly zeta_read_only; ///< 0x07F8 + INSERT_PADDING_BYTES_NOINIT(0x4); + std::array rt; ///< 0x0800 + std::array viewport_transform; ///< 0x0A00 + std::array viewports; ///< 0x0C00 + std::array windows; ///< 0x0D00 + std::array clip_id_extent; ///< 0x0D40 + u32 max_geometry_instances_per_task; ///< 0x0D60 + VisibleCallLimit visible_call_limit; ///< 0x0D64 + StatisticsCounter statistics_count; ///< 0x0D68 + ClearRect clear_rect; ///< 0x0D6C + VertexBuffer vertex_buffer; ///< 0x0D74 + DepthMode depth_mode; ///< 0x0D7C + std::array clear_color; ///< 0x0D80 + f32 clear_depth; ///< 0x0D90 + u32 shader_cache_icache_prefetch; ///< 0x0D94 + u32 force_transition_to_beta; ///< 0x0D98 + u32 reduce_colour_thresholds; ///< 0x0D9C + s32 clear_stencil; ///< 0x0DA0 + InvalidateShaderCacheNoWFI invalidate_shader_cache_no_wfi; ///< 0x0DA4 + ZCullSerialization zcull_serialization; ///< 0x0DA8 + PolygonMode polygon_mode_front; ///< 0x0DAC + PolygonMode polygon_mode_back; ///< 0x0DB0 + u32 polygon_smooth; ///< 0x0DB4 + u32 zeta_mark_clean_ieee; ///< 0x0DB8 + ZCullDirFormat zcull_dir_format; ///< 0x0DBC + u32 polygon_offset_point_enable; ///< 0x0DC0 + u32 polygon_offset_line_enable; ///< 0x0DC4 + u32 polygon_offset_fill_enable; ///< 0x0DC8 + u32 patch_vertices; ///< 0x0DCC + IteratedBlend iterated_blend; ///< 0x0DD0 + ZCullCriterion zcull_criteria; ///< 0x0DD8 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 fragment_barrier; ///< 0x0DE0 + u32 sm_timeout; ///< 0x0DE4 + u32 primitive_restart_array; ///< 0x0DE8 + INSERT_PADDING_BYTES_NOINIT(0x4); + LoadIteratedBlend load_iterated_blend; ///< 0x0DF0 + u32 window_offset_x; ///< 0x0DF8 + u32 window_offset_y; ///< 0x0DFC + std::array scissor_test; ///< 0x0E00 + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 select_texture_headers; ///< 0x0F10 + VPCPerf vpc_perf; ///< 0x0F14 + u32 pm_local_trigger; ///< 0x0F18 + u32 post_z_pixel_imask; ///< 0x0F1C + INSERT_PADDING_BYTES_NOINIT(0x20); + ConstantColorRendering const_color_rendering; ///< 0x0F40 + s32 stencil_back_ref; ///< 0x0F54 + u32 stencil_back_mask; ///< 0x0F58 + u32 stencil_back_func_mask; ///< 0x0F5C + INSERT_PADDING_BYTES_NOINIT(0x14); + u32 invalidate_texture_data_cache; ///< 0x0F74 Assumed - Not in official docs. + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 tiled_cache_barrier; ///< 0x0F7C Assumed - Not in official docs. + INSERT_PADDING_BYTES_NOINIT(0x4); + VertexStreamSubstitute vertex_stream_substitute; ///< 0x0F84 + u32 line_mode_clip_generated_edge_do_not_draw; ///< 0x0F8C + u32 color_mask_common; ///< 0x0F90 + INSERT_PADDING_BYTES_NOINIT(0x4); + VTGWarpWatermarks vtg_warp_watermarks; ///< 0x0F98 + f32 depth_bounds[2]; ///< 0x0F9C + SampleMask::Target sample_mask_target; ///< 0x0FA4 + u32 color_target_mrt_enable; ///< 0x0FAC + NonMultisampledZ non_multisampled_z; ///< 0x0FB0 + TIRMode tir_mode; ///< 0x0FB4 + AntiAliasRaster anti_alias_raster; ///< 0x0FB8 + SampleMask::Pos sample_mask_pos; ///< 0x0FBC + SurfaceClipIDMemory surface_clip_id_memory; ///< 0x0FCC + TIRModulation tir_modulation; ///< 0x0FD4 + u32 blend_control_allow_float_pixel_kills; ///< 0x0FDC + Zeta zeta; ///< 0x0FE0 + SurfaceClip surface_clip; ///< 0x0FF4 + u32 tiled_cache_treat_heavy_as_light; ///< 0x0FFC + L2CacheVAFRequests l2_cache_vaf; ///< 0x1000 + ViewportMulticast viewport_multicast; ///< 0x1004 + u32 tessellation_cut_height; ///< 0x1008 + u32 max_gs_instances_per_task; ///< 0x100C + u32 max_gs_output_vertices_per_task; ///< 0x1010 + u32 reserved_sw_method0; ///< 0x1014 + u32 gs_output_cb_storage_multiplier; ///< 0x1018 + u32 beta_cb_storage_constant; ///< 0x101C + u32 ti_output_cb_storage_multiplier; ///< 0x1020 + u32 alpha_cb_storage_constraint; ///< 0x1024 + u32 reserved_sw_method1; ///< 0x1028 + u32 reserved_sw_method2; ///< 0x102C + std::array tir_modulation_coeff; ///< 0x1030 + std::array spare_nop; ///< 0x1044 + INSERT_PADDING_BYTES_NOINIT(0x30); + std::array reserved_sw_method3_to_7; ///< 0x10B0 + ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC + std::array reserved_sw_method10_to_13; ///< 0x10D0 + ReduceColorThreshold reduce_color_thresholds_unorm10; ///< 0x10E0 + ReduceColorThreshold reduce_color_thresholds_unorm16; ///< 0x10E4 + ReduceColorThreshold reduce_color_thresholds_fp11; ///< 0x10E8 + ReduceColorThreshold reduce_color_thresholds_fp16; ///< 0x10EC + ReduceColorThreshold reduce_color_thresholds_srgb8; ///< 0x10F0 + u32 unbind_all_constant_buffers; ///< 0x10F4 + ClearControl clear_control; ///< 0x10F8 + L2CacheRopNonInterlockedReads l2_cache_rop_non_interlocked_reads; ///< 0x10FC + u32 reserved_sw_method14; ///< 0x1100 + u32 reserved_sw_method15; ///< 0x1104 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 no_operation_data_high; ///< 0x110C + u32 depth_bias_control; ///< 0x1110 + u32 pm_trigger_end; ///< 0x1114 + u32 vertex_id_base; ///< 0x1118 + u32 stencil_compression_enabled; ///< 0x111C + VertexOutputAttributeSkipMasks vertex_output_attribute_skip_masks; ///< 0x1120 + TIRControl tir_control; ///< 0x1130 + u32 mutable_method_treat_mutable_as_heavy; ///< 0x1134 + u32 post_ps_use_pre_ps_coverage; ///< 0x1138 + FillViaTriangleMode fill_via_triangle_mode; ///< 0x113C + u32 blend_per_format_snorm8_unorm16_snorm16_enabled; ///< 0x1140 + u32 flush_pending_writes_sm_gloal_store; ///< 0x1144 + u32 conservative_raster_enable; ///< 0x1148 Assumed - Not in official docs. + INSERT_PADDING_BYTES_NOINIT(0x14); + std::array vertex_attrib_format; ///< 0x1160 + std::array multisample_sample_locations; ///< 0x11E0 + u32 offset_render_target_index_by_viewport_index; ///< 0x11F0 + u32 force_heavy_method_sync; ///< 0x11F4 + MultisampleCoverageToColor multisample_coverage_to_color; ///< 0x11F8 + DecompressZetaSurface decompress_zeta_surface; ///< 0x11FC + INSERT_PADDING_BYTES_NOINIT(0x8); + ZetaSparse zeta_sparse; ///< 0x1208 + u32 invalidate_sampler_cache; ///< 0x120C + u32 invalidate_texture_header_cache; ///< 0x1210 + VertexArray vertex_array_instance_first; ///< 0x1214 + VertexArray vertex_array_instance_subsequent; ///< 0x1218 + RtControl rt_control; ///< 0x121C + CompressionThresholdSamples compression_threshold_samples; ///< 0x1220 + PixelShaderInterlockControl ps_interlock_control; ///< 0x1224 + ZetaSize zeta_size; ///< 0x1228 + SamplerBinding sampler_binding; ///< 0x1234 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 draw_auto_byte_count; ///< 0x123C + std::array post_vtg_shader_attrib_skip_mask; ///< 0x1240 + PsTicketDispenserValue ps_ticket_dispenser_value; ///< 0x1260 + INSERT_PADDING_BYTES_NOINIT(0x1C); + u32 circular_buffer_size; ///< 0x1280 + RegisterWatermarks vtg_register_watermarks; ///< 0x1284 + InvalidateTextureDataCacheNoWfi invalidate_texture_cache_no_wfi; ///< 0x1288 + INSERT_PADDING_BYTES_NOINIT(0x4); + L2CacheRopNonInterlockedReads l2_cache_rop_interlocked_reads; ///< 0x1290 + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 primitive_restart_topology_change_index; ///< 0x12A4 + INSERT_PADDING_BYTES_NOINIT(0x20); + ZCullRegionEnable zcull_region_enable; ///< 0x12C8 + u32 depth_test_enable; ///< 0x12CC + FillMode fill_mode; ///< 0x12D0 + ShadeMode shade_mode; ///< 0x12D4 + L2CacheRopNonInterlockedReads l2_cache_rop_non_interlocked_writes; ///< 0x12D8 + L2CacheRopNonInterlockedReads l2_cache_rop_interlocked_writes; ///< 0x12DC + AlphaToCoverageDither alpha_to_coverage_dither; ///< 0x12E0 + u32 blend_per_target_enabled; ///< 0x12E4 + u32 depth_write_enabled; ///< 0x12E8 + u32 alpha_test_enabled; ///< 0x12EC + INSERT_PADDING_BYTES_NOINIT(0x10); + InlineIndex4x8 inline_index_4x8; ///< 0x1300 + D3DCullMode d3d_cull_mode; ///< 0x1308 + ComparisonOp depth_test_func; ///< 0x130C + f32 alpha_test_ref; ///< 0x1310 + ComparisonOp alpha_test_func; ///< 0x1314 + u32 draw_auto_stride; ///< 0x1318 + BlendColor blend_color; ///< 0x131C + INSERT_PADDING_BYTES_NOINIT(0x4); + InvalidateCacheLines invalidate_sampler_cache_lines; ///< 0x1330 + InvalidateCacheLines invalidate_texture_header_cache_lines; ///< 0x1334 + InvalidateCacheLines invalidate_texture_data_cache_lines; ///< 0x1338 + Blend blend; ///< 0x133C + u32 stencil_enable; ///< 0x1380 + StencilOp stencil_front_op; ///< 0x1384 + s32 stencil_front_ref; ///< 0x1394 + s32 stencil_front_func_mask; ///< 0x1398 + s32 stencil_front_mask; ///< 0x139C + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 draw_auto_start_byte_count; ///< 0x13A4 + PsSaturate frag_color_clamp; ///< 0x13A8 + WindowOrigin window_origin; ///< 0x13AC + f32 line_width_smooth; ///< 0x13B0 + f32 line_width_aliased; ///< 0x13B4 + INSERT_PADDING_BYTES_NOINIT(0x60); + u32 line_override_multisample; ///< 0x1418 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 alpha_hysteresis_rounds; ///< 0x1420 + InvalidateCacheLines invalidate_sampler_cache_no_wfi; ///< 0x1424 + InvalidateCacheLines invalidate_texture_header_cache_no_wfi; ///< 0x1428 + INSERT_PADDING_BYTES_NOINIT(0x8); + u32 global_base_vertex_index; ///< 0x1434 + u32 global_base_instance_index; ///< 0x1438 + INSERT_PADDING_BYTES_NOINIT(0x14); + RegisterWatermarks ps_warp_watermarks; ///< 0x1450 + RegisterWatermarks ps_regster_watermarks; ///< 0x1454 + INSERT_PADDING_BYTES_NOINIT(0xC); + u32 store_zcull; ///< 0x1464 + INSERT_PADDING_BYTES_NOINIT(0x18); + std::array + iterated_blend_constants; ///< 0x1480 + u32 load_zcull; ///< 0x1500 + u32 surface_clip_id_height; ///< 0x1504 + Window surface_clip_id_clear_rect; ///< 0x1508 + UserClip::Enable user_clip_enable; ///< 0x1510 + u32 zpass_pixel_count_enable; ///< 0x1514 + f32 point_size; ///< 0x1518 + u32 zcull_stats_enable; ///< 0x151C + u32 point_sprite_enable; ///< 0x1520 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 shader_exceptions_enable; ///< 0x1528 + INSERT_PADDING_BYTES_NOINIT(0x4); + ClearReport clear_report_value; ///< 0x1530 + u32 anti_alias_enable; ///< 0x1534 + u32 zeta_enable; ///< 0x1538 + AntiAliasAlphaControl anti_alias_alpha_control; ///< 0x153C + INSERT_PADDING_BYTES_NOINIT(0x10); + RenderEnable render_enable; ///< 0x1550 + TexSampler tex_sampler; ///< 0x155C + INSERT_PADDING_BYTES_NOINIT(0x4); + f32 slope_scale_depth_bias; ///< 0x156C + u32 line_anti_alias_enable; ///< 0x1570 + TexHeader tex_header; ///< 0x1574 + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 active_zcull_region_id; ///< 0x1590 + u32 stencil_two_side_enable; ///< 0x1594 + StencilOp stencil_back_op; ///< 0x1598 + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 framebuffer_srgb; ///< 0x15B8 + f32 depth_bias; ///< 0x15BC + INSERT_PADDING_BYTES_NOINIT(0x8); + ZCullRegionFormat zcull_region_format; ///< 0x15C8 + RtLayer rt_layer; ///< 0x15CC + Tegra::Texture::MsaaMode anti_alias_samples_mode; ///< 0x15D0 + INSERT_PADDING_BYTES_NOINIT(0x10); + u32 edge_flag; ///< 0x15E4 + u32 draw_inline_index; ///< 0x15E8 + InlineIndex2x16 inline_index_2x16; ///< 0x15EC + VertexGlobalBaseOffset vertex_global_base_offset; ///< 0x15F4 + ZCullRegionPixelOffset zcull_region_pixel_offset; ///< 0x15FC + PointSprite point_sprite; ///< 0x1604 + ProgramRegion program_region; ///< 0x1608 + DefaultAttributes default_attributes; ///< 0x1610 + Draw draw; ///< 0x1614 + VertexIdCopy vertex_id_copy; ///< 0x161C + u32 add_to_primitive_id; ///< 0x1620 + u32 load_to_primitive_id; ///< 0x1624 + INSERT_PADDING_BYTES_NOINIT(0x4); + ShaderBasedCull shader_based_cull; ///< 0x162C + INSERT_PADDING_BYTES_NOINIT(0x8); + ClassVersion class_version; ///< 0x1638 + INSERT_PADDING_BYTES_NOINIT(0x8); + PrimitiveRestart primitive_restart; ///< 0x1644 + OutputVertexId output_vertex_id; ///< 0x164C + INSERT_PADDING_BYTES_NOINIT(0x8); + u32 anti_alias_point_enable; ///< 0x1658 + PointCenterMode point_center_mode; ///< 0x165C + INSERT_PADDING_BYTES_NOINIT(0x8); + LineSmoothParams line_smooth_params; ///< 0x1668 + u32 line_stipple_enable; ///< 0x166C + std::array line_smooth_edge_table; ///< 0x1670 + LineStippleParams line_stipple_params; ///< 0x1680 + ProvokingVertex provoking_vertex; ///< 0x1684 + u32 two_sided_light_enabled; ///< 0x1688 + u32 polygon_stipple_enabled; ///< 0x168C + ShaderControl shader_control; ///< 0x1690 + INSERT_PADDING_BYTES_NOINIT(0xC); + ClassVersion class_version_check; ///< 0x16A0 + SphVersion sph_version; ///< 0x16A4 + SphVersion sph_version_check; ///< 0x16A8 + INSERT_PADDING_BYTES_NOINIT(0x8); + AlphaToCoverageOverride alpha_to_coverage_override; ///< 0x16B4 + INSERT_PADDING_BYTES_NOINIT(0x48); + std::array polygon_stipple_pattern; ///< 0x1700 + INSERT_PADDING_BYTES_NOINIT(0x10); + AamVersion aam_version; ///< 0x1790 + AamVersion aam_version_check; ///< 0x1794 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 zeta_layer_offset; ///< 0x179C + INSERT_PADDING_BYTES_NOINIT(0x28); + IndexBuffer index_buffer; ///< 0x17C8 + IndexBufferSmall index_buffer32_first; ///< 0x17E4 + IndexBufferSmall index_buffer16_first; ///< 0x17E8 + IndexBufferSmall index_buffer8_first; ///< 0x17EC + IndexBufferSmall index_buffer32_subsequent; ///< 0x17F0 + IndexBufferSmall index_buffer16_subsequent; ///< 0x17F4 + IndexBufferSmall index_buffer8_subsequent; ///< 0x17F8 + INSERT_PADDING_BYTES_NOINIT(0x80); + f32 depth_bias_clamp; ///< 0x187C + VertexStreamInstances vertex_stream_instances; ///< 0x1880 + INSERT_PADDING_BYTES_NOINIT(0x10); + AttributePointSize point_size_attribute; ///< 0x1910 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 gl_cull_test_enabled; ///< 0x1918 + FrontFace gl_front_face; ///< 0x191C + CullFace gl_cull_face; ///< 0x1920 + Viewport::PixelCenter viewport_pixel_center; ///< 0x1924 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 viewport_scale_offset_enabled; ///< 0x192C + INSERT_PADDING_BYTES_NOINIT(0xC); + ViewportClipControl viewport_clip_control; ///< 0x193C + UserClip::Op user_clip_op; ///< 0x1940 + RenderEnable::Override render_enable_override; ///< 0x1944 + PrimitiveTopologyControl primitive_topology_control; ///< 0x1948 + WindowClip window_clip_enable; ///< 0x194C + INSERT_PADDING_BYTES_NOINIT(0x4); + InvalidateZCull invalidate_zcull; ///< 0x1958 + INSERT_PADDING_BYTES_NOINIT(0xC); + ZCull zcull; ///< 0x1968 + PrimitiveTopologyOverride topology_override; ///< 0x1970 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 zcull_sync; ///< 0x1978 + u32 clip_id_test_enable; ///< 0x197C + u32 surface_clip_id_width; ///< 0x1980 + u32 clip_id; ///< 0x1984 + INSERT_PADDING_BYTES_NOINIT(0x34); + u32 depth_bounds_enable; ///< 0x19BC + u32 blend_float_zero_times_anything_is_zero; ///< 0x19C0 + LogicOp logic_op; ///< 0x19C4 + u32 z_compression_enable; ///< 0x19CC + ClearSurface clear_surface; ///< 0x19D0 + u32 clear_clip_id_surface; ///< 0x19D4 + INSERT_PADDING_BYTES_NOINIT(0x8); + std::array color_compression_enable; ///< 0x19E0 + std::array color_mask; ///< 0x1A00 + INSERT_PADDING_BYTES_NOINIT(0xC); + u32 pipe_nop; ///< 0x1A2C + std::array spare; ///< 0x1A30 + INSERT_PADDING_BYTES_NOINIT(0xC0); + ReportSemaphore report_semaphore; ///< 0x1B00 + INSERT_PADDING_BYTES_NOINIT(0xF0); + std::array vertex_streams; ///< 0x1C00 + BlendPerTarget blend_per_target[NumRenderTargets]; ///< 0x1E00 + std::array vertex_stream_limits; ///< 0x1F00 + std::array pipelines; ///< 0x2000 + INSERT_PADDING_BYTES_NOINIT(0x180); + u32 falcon[32]; ///< 0x2300 + ConstantBuffer const_buffer; ///< 0x2380 + INSERT_PADDING_BYTES_NOINIT(0x30); + BindGroup bind_groups[MaxShaderStage]; ///< 0x2400 + INSERT_PADDING_BYTES_NOINIT(0x160); + u32 color_clamp_enable; ///< 0x2600 + INSERT_PADDING_BYTES_NOINIT(0x4); + u32 bindless_texture_const_buffer_slot; ///< 0x2608 + u32 trap_handler; ///< 0x260C + INSERT_PADDING_BYTES_NOINIT(0x1F0); + std::array, NumTransformFeedbackBuffers> + stream_out_layout; ///< 0x2800 + INSERT_PADDING_BYTES_NOINIT(0x93C); + ShaderPerformance shader_performance; ///< 0x333C + INSERT_PADDING_BYTES_NOINIT(0x18); + std::array shadow_scratch; ///< 0x3400 }; std::array reg_array; }; }; + // clang-format on Regs regs{}; @@ -1438,8 +3029,6 @@ public: }; std::array shader_stages; - - u32 current_instance = 0; ///< Current instance to be used to simulate instanced rendering. }; State state{}; @@ -1454,11 +3043,6 @@ public: void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) override; - /// Write the value to the register identified by method. - void CallMethodFromMME(u32 method, u32 method_argument); - - void FlushMMEInlineDraw(); - bool ShouldExecute() const { return execute_on; } @@ -1471,21 +3055,6 @@ public: return *rasterizer; } - enum class MMEDrawMode : u32 { - Undefined, - Array, - Indexed, - }; - - struct MMEDrawState { - MMEDrawMode current_mode{MMEDrawMode::Undefined}; - u32 current_count{}; - u32 instance_count{}; - bool instance_mode{}; - bool gl_begin_consume{}; - u32 gl_end_count{}; - } mme_draw; - struct DirtyState { using Flags = std::bitset::max()>; using Table = std::array; @@ -1495,6 +3064,9 @@ public: Tables tables{}; } dirty; + std::unique_ptr draw_manager; + friend class DrawManager; + private: void InitializeRegisterDefaults(); @@ -1529,9 +3101,6 @@ private: /// Handles firmware blob 4 void ProcessFirmwareCall4(); - /// Handles a write to the CLEAR_BUFFERS register. - void ProcessClearBuffers(); - /// Handles a write to the QUERY_GET register. void ProcessQueryGet(); @@ -1554,15 +3123,6 @@ private: /// Handles a write to the CB_BIND register. void ProcessCBBind(size_t stage_index); - /// Handles a write to the VERTEX_END_GL register, triggering a draw. - void DrawArrays(); - - /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro) - void ProcessTopologyOverride(); - - // Handles a instance drawcall from MME - void StepInstance(MMEDrawMode expected_mode, u32 count); - /// Returns a query's value or an empty object if the value will be deferred through a cache. std::optional GetQueryResult(); @@ -1574,8 +3134,6 @@ private: /// Start offsets of each macro in macro_memory std::array macro_positions{}; - std::array mme_inline{}; - /// Macro method that is currently being executed / being fed parameters. u32 executing_macro = 0; /// Parameters that have been submitted to the macro call so far. @@ -1587,151 +3145,355 @@ private: Upload::State upload_state; bool execute_on{true}; - bool use_topology_override{false}; }; #define ASSERT_REG_POSITION(field_name, position) \ - static_assert(offsetof(Maxwell3D::Regs, field_name) == position * 4, \ + static_assert(offsetof(Maxwell3D::Regs, field_name) == position, \ "Field " #field_name " has invalid position") -ASSERT_REG_POSITION(wait_for_idle, 0x44); -ASSERT_REG_POSITION(macros, 0x45); -ASSERT_REG_POSITION(shadow_ram_control, 0x49); -ASSERT_REG_POSITION(upload, 0x60); -ASSERT_REG_POSITION(exec_upload, 0x6C); -ASSERT_REG_POSITION(data_upload, 0x6D); -ASSERT_REG_POSITION(force_early_fragment_tests, 0x84); -ASSERT_REG_POSITION(sync_info, 0xB2); -ASSERT_REG_POSITION(tess_mode, 0xC8); -ASSERT_REG_POSITION(tess_level_outer, 0xC9); -ASSERT_REG_POSITION(tess_level_inner, 0xCD); -ASSERT_REG_POSITION(rasterize_enable, 0xDF); -ASSERT_REG_POSITION(tfb_bindings, 0xE0); -ASSERT_REG_POSITION(tfb_layouts, 0x1C0); -ASSERT_REG_POSITION(tfb_enabled, 0x1D1); -ASSERT_REG_POSITION(rt, 0x200); -ASSERT_REG_POSITION(viewport_transform, 0x280); -ASSERT_REG_POSITION(viewports, 0x300); -ASSERT_REG_POSITION(vertex_buffer, 0x35D); -ASSERT_REG_POSITION(depth_mode, 0x35F); -ASSERT_REG_POSITION(clear_color[0], 0x360); -ASSERT_REG_POSITION(clear_depth, 0x364); -ASSERT_REG_POSITION(clear_stencil, 0x368); -ASSERT_REG_POSITION(polygon_mode_front, 0x36B); -ASSERT_REG_POSITION(polygon_mode_back, 0x36C); -ASSERT_REG_POSITION(polygon_offset_point_enable, 0x370); -ASSERT_REG_POSITION(polygon_offset_line_enable, 0x371); -ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372); -ASSERT_REG_POSITION(patch_vertices, 0x373); -ASSERT_REG_POSITION(fragment_barrier, 0x378); -ASSERT_REG_POSITION(scissor_test, 0x380); -ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); -ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); -ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); -ASSERT_REG_POSITION(invalidate_texture_data_cache, 0x3DD); -ASSERT_REG_POSITION(tiled_cache_barrier, 0x3DF); -ASSERT_REG_POSITION(color_mask_common, 0x3E4); -ASSERT_REG_POSITION(depth_bounds, 0x3E7); -ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); -ASSERT_REG_POSITION(multisample_raster_enable, 0x3ED); -ASSERT_REG_POSITION(multisample_raster_samples, 0x3EE); -ASSERT_REG_POSITION(multisample_sample_mask, 0x3EF); -ASSERT_REG_POSITION(zeta, 0x3F8); -ASSERT_REG_POSITION(render_area, 0x3FD); -ASSERT_REG_POSITION(clear_flags, 0x43E); -ASSERT_REG_POSITION(fill_rectangle, 0x44F); -ASSERT_REG_POSITION(conservative_raster_enable, 0x452); -ASSERT_REG_POSITION(vertex_attrib_format, 0x458); -ASSERT_REG_POSITION(multisample_sample_locations, 0x478); -ASSERT_REG_POSITION(multisample_coverage_to_color, 0x47E); -ASSERT_REG_POSITION(rt_control, 0x487); -ASSERT_REG_POSITION(zeta_width, 0x48a); -ASSERT_REG_POSITION(zeta_height, 0x48b); -ASSERT_REG_POSITION(zeta_depth, 0x48c); -ASSERT_REG_POSITION(sampler_index, 0x48D); -ASSERT_REG_POSITION(gp_passthrough_mask, 0x490); -ASSERT_REG_POSITION(depth_test_enable, 0x4B3); -ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); -ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); -ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB); -ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); -ASSERT_REG_POSITION(depth_test_func, 0x4C3); -ASSERT_REG_POSITION(alpha_test_ref, 0x4C4); -ASSERT_REG_POSITION(alpha_test_func, 0x4C5); -ASSERT_REG_POSITION(draw_tfb_stride, 0x4C6); -ASSERT_REG_POSITION(blend_color, 0x4C7); -ASSERT_REG_POSITION(blend, 0x4CF); -ASSERT_REG_POSITION(stencil_enable, 0x4E0); -ASSERT_REG_POSITION(stencil_front_op_fail, 0x4E1); -ASSERT_REG_POSITION(stencil_front_op_zfail, 0x4E2); -ASSERT_REG_POSITION(stencil_front_op_zpass, 0x4E3); -ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4); -ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5); -ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); -ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); -ASSERT_REG_POSITION(frag_color_clamp, 0x4EA); -ASSERT_REG_POSITION(screen_y_control, 0x4EB); -ASSERT_REG_POSITION(line_width_smooth, 0x4EC); -ASSERT_REG_POSITION(line_width_aliased, 0x4ED); -ASSERT_REG_POSITION(invalidate_sampler_cache_no_wfi, 0x509); -ASSERT_REG_POSITION(invalidate_texture_header_cache_no_wfi, 0x50A); -ASSERT_REG_POSITION(vb_element_base, 0x50D); -ASSERT_REG_POSITION(vb_base_instance, 0x50E); -ASSERT_REG_POSITION(clip_distance_enabled, 0x544); -ASSERT_REG_POSITION(samplecnt_enable, 0x545); -ASSERT_REG_POSITION(point_size, 0x546); -ASSERT_REG_POSITION(point_sprite_enable, 0x548); -ASSERT_REG_POSITION(counter_reset, 0x54C); -ASSERT_REG_POSITION(multisample_enable, 0x54D); -ASSERT_REG_POSITION(zeta_enable, 0x54E); -ASSERT_REG_POSITION(multisample_control, 0x54F); -ASSERT_REG_POSITION(condition, 0x554); -ASSERT_REG_POSITION(tsc, 0x557); -ASSERT_REG_POSITION(polygon_offset_factor, 0x55B); -ASSERT_REG_POSITION(line_smooth_enable, 0x55C); -ASSERT_REG_POSITION(tic, 0x55D); -ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); -ASSERT_REG_POSITION(stencil_back_op_fail, 0x566); -ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567); -ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568); -ASSERT_REG_POSITION(stencil_back_func_func, 0x569); -ASSERT_REG_POSITION(framebuffer_srgb, 0x56E); -ASSERT_REG_POSITION(polygon_offset_units, 0x56F); -ASSERT_REG_POSITION(multisample_mode, 0x574); -ASSERT_REG_POSITION(point_coord_replace, 0x581); -ASSERT_REG_POSITION(code_address, 0x582); -ASSERT_REG_POSITION(draw, 0x585); -ASSERT_REG_POSITION(primitive_restart, 0x591); -ASSERT_REG_POSITION(provoking_vertex_last, 0x5A1); -ASSERT_REG_POSITION(index_array, 0x5F2); -ASSERT_REG_POSITION(small_index, 0x5F9); -ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F); -ASSERT_REG_POSITION(instanced_arrays, 0x620); -ASSERT_REG_POSITION(vp_point_size, 0x644); -ASSERT_REG_POSITION(cull_test_enabled, 0x646); -ASSERT_REG_POSITION(front_face, 0x647); -ASSERT_REG_POSITION(cull_face, 0x648); -ASSERT_REG_POSITION(pixel_center_integer, 0x649); -ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B); -ASSERT_REG_POSITION(view_volume_clip_control, 0x64F); -ASSERT_REG_POSITION(topology_override, 0x65C); -ASSERT_REG_POSITION(depth_bounds_enable, 0x66F); -ASSERT_REG_POSITION(logic_op, 0x671); -ASSERT_REG_POSITION(clear_buffers, 0x674); -ASSERT_REG_POSITION(color_mask, 0x680); -ASSERT_REG_POSITION(query, 0x6C0); -ASSERT_REG_POSITION(vertex_array[0], 0x700); -ASSERT_REG_POSITION(independent_blend, 0x780); -ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0); -ASSERT_REG_POSITION(shader_config[0], 0x800); -ASSERT_REG_POSITION(firmware, 0x8C0); -ASSERT_REG_POSITION(const_buffer, 0x8E0); -ASSERT_REG_POSITION(cb_bind[0], 0x904); -ASSERT_REG_POSITION(tex_cb_index, 0x982); -ASSERT_REG_POSITION(tfb_varying_locs, 0xA00); -ASSERT_REG_POSITION(ssbo_info, 0xD18); -ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A); -ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F); +ASSERT_REG_POSITION(object_id, 0x0000); +ASSERT_REG_POSITION(nop, 0x0100); +ASSERT_REG_POSITION(notify, 0x0104); +ASSERT_REG_POSITION(wait_for_idle, 0x0110); +ASSERT_REG_POSITION(load_mme, 0x0114); +ASSERT_REG_POSITION(shadow_ram_control, 0x0124); +ASSERT_REG_POSITION(peer, 0x0128); +ASSERT_REG_POSITION(global_render, 0x0130); +ASSERT_REG_POSITION(go_idle, 0x013C); +ASSERT_REG_POSITION(trigger, 0x0140); +ASSERT_REG_POSITION(trigger_wfi, 0x0144); +ASSERT_REG_POSITION(instrumentation_method_header, 0x0150); +ASSERT_REG_POSITION(instrumentation_method_data, 0x0154); +ASSERT_REG_POSITION(upload, 0x0180); +ASSERT_REG_POSITION(launch_dma, 0x01B0); +ASSERT_REG_POSITION(inline_data, 0x01B4); +ASSERT_REG_POSITION(i2m, 0x01DC); +ASSERT_REG_POSITION(run_ds_now, 0x0200); +ASSERT_REG_POSITION(opportunistic_early_z, 0x0204); +ASSERT_REG_POSITION(aliased_line_width_enabled, 0x020C); +ASSERT_REG_POSITION(mandated_early_z, 0x0210); +ASSERT_REG_POSITION(gs_dm_fifo, 0x0214); +ASSERT_REG_POSITION(l2_cache_control, 0x0218); +ASSERT_REG_POSITION(invalidate_shader_cache, 0x021C); +ASSERT_REG_POSITION(sync_info, 0x02C8); +ASSERT_REG_POSITION(prim_circular_buffer_throttle, 0x02D0); +ASSERT_REG_POSITION(flush_invalidate_rop_mini_cache, 0x02D4); +ASSERT_REG_POSITION(surface_clip_block_id, 0x02D8); +ASSERT_REG_POSITION(alpha_circular_buffer_size, 0x02DC); +ASSERT_REG_POSITION(decompress_surface, 0x02E0); +ASSERT_REG_POSITION(zcull_rop_bypass, 0x02E4); +ASSERT_REG_POSITION(zcull_subregion, 0x02E8); +ASSERT_REG_POSITION(raster_bounding_box, 0x02EC); +ASSERT_REG_POSITION(peer_semaphore_release, 0x02F0); +ASSERT_REG_POSITION(iterated_blend_optimization, 0x02F4); +ASSERT_REG_POSITION(zcull_subregion_allocation, 0x02F8); +ASSERT_REG_POSITION(zcull_subregion_algorithm, 0x02FC); +ASSERT_REG_POSITION(ps_output_sample_mask_usage, 0x0300); +ASSERT_REG_POSITION(draw_zero_index, 0x0304); +ASSERT_REG_POSITION(l1_configuration, 0x0308); +ASSERT_REG_POSITION(render_enable_control_load_const_buffer, 0x030C); +ASSERT_REG_POSITION(spa_version, 0x0310); +ASSERT_REG_POSITION(ieee_clean_update, 0x0314); +ASSERT_REG_POSITION(snap_grid, 0x0318); +ASSERT_REG_POSITION(tessellation, 0x0320); +ASSERT_REG_POSITION(sub_tiling_perf, 0x0360); +ASSERT_REG_POSITION(zcull_subregion_report, 0x036C); +ASSERT_REG_POSITION(balanced_primitive_workload, 0x0374); +ASSERT_REG_POSITION(max_patches_per_batch, 0x0378); +ASSERT_REG_POSITION(rasterize_enable, 0x037C); +ASSERT_REG_POSITION(transform_feedback, 0x0380); +ASSERT_REG_POSITION(transform_feedback.controls, 0x0700); +ASSERT_REG_POSITION(raster_input, 0x0740); +ASSERT_REG_POSITION(transform_feedback_enabled, 0x0744); +ASSERT_REG_POSITION(primitive_restart_topology_change_enable, 0x0748); +ASSERT_REG_POSITION(alpha_fraction, 0x074C); +ASSERT_REG_POSITION(hybrid_aa_control, 0x0754); +ASSERT_REG_POSITION(shader_local_memory, 0x077C); +ASSERT_REG_POSITION(color_zero_bandwidth_clear, 0x07A4); +ASSERT_REG_POSITION(z_zero_bandwidth_clear, 0x07A8); +ASSERT_REG_POSITION(isbe_save_restore_program_offset, 0x07AC); +ASSERT_REG_POSITION(zcull_region, 0x07C0); +ASSERT_REG_POSITION(zeta_read_only, 0x07F8); +ASSERT_REG_POSITION(rt, 0x0800); +ASSERT_REG_POSITION(viewport_transform, 0x0A00); +ASSERT_REG_POSITION(viewports, 0x0C00); +ASSERT_REG_POSITION(windows, 0x0D00); +ASSERT_REG_POSITION(clip_id_extent, 0x0D40); +ASSERT_REG_POSITION(max_geometry_instances_per_task, 0x0D60); +ASSERT_REG_POSITION(visible_call_limit, 0x0D64); +ASSERT_REG_POSITION(statistics_count, 0x0D68); +ASSERT_REG_POSITION(clear_rect, 0x0D6C); +ASSERT_REG_POSITION(vertex_buffer, 0x0D74); +ASSERT_REG_POSITION(depth_mode, 0x0D7C); +ASSERT_REG_POSITION(clear_color, 0x0D80); +ASSERT_REG_POSITION(clear_depth, 0x0D90); +ASSERT_REG_POSITION(shader_cache_icache_prefetch, 0x0D94); +ASSERT_REG_POSITION(force_transition_to_beta, 0x0D98); +ASSERT_REG_POSITION(reduce_colour_thresholds, 0x0D9C); +ASSERT_REG_POSITION(clear_stencil, 0x0DA0); +ASSERT_REG_POSITION(invalidate_shader_cache_no_wfi, 0x0DA4); +ASSERT_REG_POSITION(zcull_serialization, 0x0DA8); +ASSERT_REG_POSITION(polygon_mode_front, 0x0DAC); +ASSERT_REG_POSITION(polygon_mode_back, 0x0DB0); +ASSERT_REG_POSITION(polygon_smooth, 0x0DB4); +ASSERT_REG_POSITION(zeta_mark_clean_ieee, 0x0DB8); +ASSERT_REG_POSITION(zcull_dir_format, 0x0DBC); +ASSERT_REG_POSITION(polygon_offset_point_enable, 0x0DC0); +ASSERT_REG_POSITION(polygon_offset_line_enable, 0x0DC4); +ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x0DC8); +ASSERT_REG_POSITION(patch_vertices, 0x0DCC); +ASSERT_REG_POSITION(iterated_blend, 0x0DD0); +ASSERT_REG_POSITION(zcull_criteria, 0x0DD8); +ASSERT_REG_POSITION(fragment_barrier, 0x0DE0); +ASSERT_REG_POSITION(sm_timeout, 0x0DE4); +ASSERT_REG_POSITION(primitive_restart_array, 0x0DE8); +ASSERT_REG_POSITION(load_iterated_blend, 0x0DF0); +ASSERT_REG_POSITION(window_offset_x, 0x0DF8); +ASSERT_REG_POSITION(window_offset_y, 0x0DFC); +ASSERT_REG_POSITION(scissor_test, 0x0E00); +ASSERT_REG_POSITION(select_texture_headers, 0x0F10); +ASSERT_REG_POSITION(vpc_perf, 0x0F14); +ASSERT_REG_POSITION(pm_local_trigger, 0x0F18); +ASSERT_REG_POSITION(post_z_pixel_imask, 0x0F1C); +ASSERT_REG_POSITION(const_color_rendering, 0x0F40); +ASSERT_REG_POSITION(stencil_back_ref, 0x0F54); +ASSERT_REG_POSITION(stencil_back_mask, 0x0F58); +ASSERT_REG_POSITION(stencil_back_func_mask, 0x0F5C); +ASSERT_REG_POSITION(invalidate_texture_data_cache, 0x0F74); +ASSERT_REG_POSITION(tiled_cache_barrier, 0x0F7C); +ASSERT_REG_POSITION(vertex_stream_substitute, 0x0F84); +ASSERT_REG_POSITION(line_mode_clip_generated_edge_do_not_draw, 0x0F8C); +ASSERT_REG_POSITION(color_mask_common, 0x0F90); +ASSERT_REG_POSITION(vtg_warp_watermarks, 0x0F98); +ASSERT_REG_POSITION(depth_bounds, 0x0F9C); +ASSERT_REG_POSITION(sample_mask_target, 0x0FA4); +ASSERT_REG_POSITION(color_target_mrt_enable, 0x0FAC); +ASSERT_REG_POSITION(non_multisampled_z, 0x0FB0); +ASSERT_REG_POSITION(tir_mode, 0x0FB4); +ASSERT_REG_POSITION(anti_alias_raster, 0x0FB8); +ASSERT_REG_POSITION(sample_mask_pos, 0x0FBC); +ASSERT_REG_POSITION(surface_clip_id_memory, 0x0FCC); +ASSERT_REG_POSITION(tir_modulation, 0x0FD4); +ASSERT_REG_POSITION(blend_control_allow_float_pixel_kills, 0x0FDC); +ASSERT_REG_POSITION(zeta, 0x0FE0); +ASSERT_REG_POSITION(surface_clip, 0x0FF4); +ASSERT_REG_POSITION(tiled_cache_treat_heavy_as_light, 0x0FFC); +ASSERT_REG_POSITION(l2_cache_vaf, 0x1000); +ASSERT_REG_POSITION(viewport_multicast, 0x1004); +ASSERT_REG_POSITION(tessellation_cut_height, 0x1008); +ASSERT_REG_POSITION(max_gs_instances_per_task, 0x100C); +ASSERT_REG_POSITION(max_gs_output_vertices_per_task, 0x1010); +ASSERT_REG_POSITION(reserved_sw_method0, 0x1014); +ASSERT_REG_POSITION(gs_output_cb_storage_multiplier, 0x1018); +ASSERT_REG_POSITION(beta_cb_storage_constant, 0x101C); +ASSERT_REG_POSITION(ti_output_cb_storage_multiplier, 0x1020); +ASSERT_REG_POSITION(alpha_cb_storage_constraint, 0x1024); +ASSERT_REG_POSITION(reserved_sw_method1, 0x1028); +ASSERT_REG_POSITION(reserved_sw_method2, 0x102C); +ASSERT_REG_POSITION(tir_modulation_coeff, 0x1030); +ASSERT_REG_POSITION(spare_nop, 0x1044); +ASSERT_REG_POSITION(reserved_sw_method3_to_7, 0x10B0); +ASSERT_REG_POSITION(reduce_color_thresholds_unorm8, 0x10CC); +ASSERT_REG_POSITION(reserved_sw_method10_to_13, 0x10D0); +ASSERT_REG_POSITION(reduce_color_thresholds_unorm10, 0x10E0); +ASSERT_REG_POSITION(reduce_color_thresholds_unorm16, 0x10E4); +ASSERT_REG_POSITION(reduce_color_thresholds_fp11, 0x10E8); +ASSERT_REG_POSITION(reduce_color_thresholds_fp16, 0x10EC); +ASSERT_REG_POSITION(reduce_color_thresholds_srgb8, 0x10F0); +ASSERT_REG_POSITION(unbind_all_constant_buffers, 0x10F4); +ASSERT_REG_POSITION(clear_control, 0x10F8); +ASSERT_REG_POSITION(l2_cache_rop_non_interlocked_reads, 0x10FC); +ASSERT_REG_POSITION(reserved_sw_method14, 0x1100); +ASSERT_REG_POSITION(reserved_sw_method15, 0x1104); +ASSERT_REG_POSITION(no_operation_data_high, 0x110C); +ASSERT_REG_POSITION(depth_bias_control, 0x1110); +ASSERT_REG_POSITION(pm_trigger_end, 0x1114); +ASSERT_REG_POSITION(vertex_id_base, 0x1118); +ASSERT_REG_POSITION(stencil_compression_enabled, 0x111C); +ASSERT_REG_POSITION(vertex_output_attribute_skip_masks, 0x1120); +ASSERT_REG_POSITION(tir_control, 0x1130); +ASSERT_REG_POSITION(mutable_method_treat_mutable_as_heavy, 0x1134); +ASSERT_REG_POSITION(post_ps_use_pre_ps_coverage, 0x1138); +ASSERT_REG_POSITION(fill_via_triangle_mode, 0x113C); +ASSERT_REG_POSITION(blend_per_format_snorm8_unorm16_snorm16_enabled, 0x1140); +ASSERT_REG_POSITION(flush_pending_writes_sm_gloal_store, 0x1144); +ASSERT_REG_POSITION(conservative_raster_enable, 0x1148); +ASSERT_REG_POSITION(vertex_attrib_format, 0x1160); +ASSERT_REG_POSITION(multisample_sample_locations, 0x11E0); +ASSERT_REG_POSITION(offset_render_target_index_by_viewport_index, 0x11F0); +ASSERT_REG_POSITION(force_heavy_method_sync, 0x11F4); +ASSERT_REG_POSITION(multisample_coverage_to_color, 0x11F8); +ASSERT_REG_POSITION(decompress_zeta_surface, 0x11FC); +ASSERT_REG_POSITION(zeta_sparse, 0x1208); +ASSERT_REG_POSITION(invalidate_sampler_cache, 0x120C); +ASSERT_REG_POSITION(invalidate_texture_header_cache, 0x1210); +ASSERT_REG_POSITION(vertex_array_instance_first, 0x1214); +ASSERT_REG_POSITION(vertex_array_instance_subsequent, 0x1218); +ASSERT_REG_POSITION(rt_control, 0x121C); +ASSERT_REG_POSITION(compression_threshold_samples, 0x1220); +ASSERT_REG_POSITION(ps_interlock_control, 0x1224); +ASSERT_REG_POSITION(zeta_size, 0x1228); +ASSERT_REG_POSITION(sampler_binding, 0x1234); +ASSERT_REG_POSITION(draw_auto_byte_count, 0x123C); +ASSERT_REG_POSITION(post_vtg_shader_attrib_skip_mask, 0x1240); +ASSERT_REG_POSITION(ps_ticket_dispenser_value, 0x1260); +ASSERT_REG_POSITION(circular_buffer_size, 0x1280); +ASSERT_REG_POSITION(vtg_register_watermarks, 0x1284); +ASSERT_REG_POSITION(invalidate_texture_cache_no_wfi, 0x1288); +ASSERT_REG_POSITION(l2_cache_rop_interlocked_reads, 0x1290); +ASSERT_REG_POSITION(primitive_restart_topology_change_index, 0x12A4); +ASSERT_REG_POSITION(zcull_region_enable, 0x12C8); +ASSERT_REG_POSITION(depth_test_enable, 0x12CC); +ASSERT_REG_POSITION(fill_mode, 0x12D0); +ASSERT_REG_POSITION(shade_mode, 0x12D4); +ASSERT_REG_POSITION(l2_cache_rop_non_interlocked_writes, 0x12D8); +ASSERT_REG_POSITION(l2_cache_rop_interlocked_writes, 0x12DC); +ASSERT_REG_POSITION(alpha_to_coverage_dither, 0x12E0); +ASSERT_REG_POSITION(blend_per_target_enabled, 0x12E4); +ASSERT_REG_POSITION(depth_write_enabled, 0x12E8); +ASSERT_REG_POSITION(alpha_test_enabled, 0x12EC); +ASSERT_REG_POSITION(inline_index_4x8, 0x1300); +ASSERT_REG_POSITION(d3d_cull_mode, 0x1308); +ASSERT_REG_POSITION(depth_test_func, 0x130C); +ASSERT_REG_POSITION(alpha_test_ref, 0x1310); +ASSERT_REG_POSITION(alpha_test_func, 0x1314); +ASSERT_REG_POSITION(draw_auto_stride, 0x1318); +ASSERT_REG_POSITION(blend_color, 0x131C); +ASSERT_REG_POSITION(invalidate_sampler_cache_lines, 0x1330); +ASSERT_REG_POSITION(invalidate_texture_header_cache_lines, 0x1334); +ASSERT_REG_POSITION(invalidate_texture_data_cache_lines, 0x1338); +ASSERT_REG_POSITION(blend, 0x133C); +ASSERT_REG_POSITION(stencil_enable, 0x1380); +ASSERT_REG_POSITION(stencil_front_op, 0x1384); +ASSERT_REG_POSITION(stencil_front_ref, 0x1394); +ASSERT_REG_POSITION(stencil_front_func_mask, 0x1398); +ASSERT_REG_POSITION(stencil_front_mask, 0x139C); +ASSERT_REG_POSITION(draw_auto_start_byte_count, 0x13A4); +ASSERT_REG_POSITION(frag_color_clamp, 0x13A8); +ASSERT_REG_POSITION(window_origin, 0x13AC); +ASSERT_REG_POSITION(line_width_smooth, 0x13B0); +ASSERT_REG_POSITION(line_width_aliased, 0x13B4); +ASSERT_REG_POSITION(line_override_multisample, 0x1418); +ASSERT_REG_POSITION(alpha_hysteresis_rounds, 0x1420); +ASSERT_REG_POSITION(invalidate_sampler_cache_no_wfi, 0x1424); +ASSERT_REG_POSITION(invalidate_texture_header_cache_no_wfi, 0x1428); +ASSERT_REG_POSITION(global_base_vertex_index, 0x1434); +ASSERT_REG_POSITION(global_base_instance_index, 0x1438); +ASSERT_REG_POSITION(ps_warp_watermarks, 0x1450); +ASSERT_REG_POSITION(ps_regster_watermarks, 0x1454); +ASSERT_REG_POSITION(store_zcull, 0x1464); +ASSERT_REG_POSITION(iterated_blend_constants, 0x1480); +ASSERT_REG_POSITION(load_zcull, 0x1500); +ASSERT_REG_POSITION(surface_clip_id_height, 0x1504); +ASSERT_REG_POSITION(surface_clip_id_clear_rect, 0x1508); +ASSERT_REG_POSITION(user_clip_enable, 0x1510); +ASSERT_REG_POSITION(zpass_pixel_count_enable, 0x1514); +ASSERT_REG_POSITION(point_size, 0x1518); +ASSERT_REG_POSITION(zcull_stats_enable, 0x151C); +ASSERT_REG_POSITION(point_sprite_enable, 0x1520); +ASSERT_REG_POSITION(shader_exceptions_enable, 0x1528); +ASSERT_REG_POSITION(clear_report_value, 0x1530); +ASSERT_REG_POSITION(anti_alias_enable, 0x1534); +ASSERT_REG_POSITION(zeta_enable, 0x1538); +ASSERT_REG_POSITION(anti_alias_alpha_control, 0x153C); +ASSERT_REG_POSITION(render_enable, 0x1550); +ASSERT_REG_POSITION(tex_sampler, 0x155C); +ASSERT_REG_POSITION(slope_scale_depth_bias, 0x156C); +ASSERT_REG_POSITION(line_anti_alias_enable, 0x1570); +ASSERT_REG_POSITION(tex_header, 0x1574); +ASSERT_REG_POSITION(active_zcull_region_id, 0x1590); +ASSERT_REG_POSITION(stencil_two_side_enable, 0x1594); +ASSERT_REG_POSITION(stencil_back_op, 0x1598); +ASSERT_REG_POSITION(framebuffer_srgb, 0x15B8); +ASSERT_REG_POSITION(depth_bias, 0x15BC); +ASSERT_REG_POSITION(zcull_region_format, 0x15C8); +ASSERT_REG_POSITION(rt_layer, 0x15CC); +ASSERT_REG_POSITION(anti_alias_samples_mode, 0x15D0); +ASSERT_REG_POSITION(edge_flag, 0x15E4); +ASSERT_REG_POSITION(draw_inline_index, 0x15E8); +ASSERT_REG_POSITION(inline_index_2x16, 0x15EC); +ASSERT_REG_POSITION(vertex_global_base_offset, 0x15F4); +ASSERT_REG_POSITION(zcull_region_pixel_offset, 0x15FC); +ASSERT_REG_POSITION(point_sprite, 0x1604); +ASSERT_REG_POSITION(program_region, 0x1608); +ASSERT_REG_POSITION(default_attributes, 0x1610); +ASSERT_REG_POSITION(draw, 0x1614); +ASSERT_REG_POSITION(vertex_id_copy, 0x161C); +ASSERT_REG_POSITION(add_to_primitive_id, 0x1620); +ASSERT_REG_POSITION(load_to_primitive_id, 0x1624); +ASSERT_REG_POSITION(shader_based_cull, 0x162C); +ASSERT_REG_POSITION(class_version, 0x1638); +ASSERT_REG_POSITION(primitive_restart, 0x1644); +ASSERT_REG_POSITION(output_vertex_id, 0x164C); +ASSERT_REG_POSITION(anti_alias_point_enable, 0x1658); +ASSERT_REG_POSITION(point_center_mode, 0x165C); +ASSERT_REG_POSITION(line_smooth_params, 0x1668); +ASSERT_REG_POSITION(line_stipple_enable, 0x166C); +ASSERT_REG_POSITION(line_smooth_edge_table, 0x1670); +ASSERT_REG_POSITION(line_stipple_params, 0x1680); +ASSERT_REG_POSITION(provoking_vertex, 0x1684); +ASSERT_REG_POSITION(two_sided_light_enabled, 0x1688); +ASSERT_REG_POSITION(polygon_stipple_enabled, 0x168C); +ASSERT_REG_POSITION(shader_control, 0x1690); +ASSERT_REG_POSITION(class_version_check, 0x16A0); +ASSERT_REG_POSITION(sph_version, 0x16A4); +ASSERT_REG_POSITION(sph_version_check, 0x16A8); +ASSERT_REG_POSITION(alpha_to_coverage_override, 0x16B4); +ASSERT_REG_POSITION(polygon_stipple_pattern, 0x1700); +ASSERT_REG_POSITION(aam_version, 0x1790); +ASSERT_REG_POSITION(aam_version_check, 0x1794); +ASSERT_REG_POSITION(zeta_layer_offset, 0x179C); +ASSERT_REG_POSITION(index_buffer, 0x17C8); +ASSERT_REG_POSITION(index_buffer32_first, 0x17E4); +ASSERT_REG_POSITION(index_buffer16_first, 0x17E8); +ASSERT_REG_POSITION(index_buffer8_first, 0x17EC); +ASSERT_REG_POSITION(index_buffer32_subsequent, 0x17F0); +ASSERT_REG_POSITION(index_buffer16_subsequent, 0x17F4); +ASSERT_REG_POSITION(index_buffer8_subsequent, 0x17F8); +ASSERT_REG_POSITION(depth_bias_clamp, 0x187C); +ASSERT_REG_POSITION(vertex_stream_instances, 0x1880); +ASSERT_REG_POSITION(point_size_attribute, 0x1910); +ASSERT_REG_POSITION(gl_cull_test_enabled, 0x1918); +ASSERT_REG_POSITION(gl_front_face, 0x191C); +ASSERT_REG_POSITION(gl_cull_face, 0x1920); +ASSERT_REG_POSITION(viewport_pixel_center, 0x1924); +ASSERT_REG_POSITION(viewport_scale_offset_enabled, 0x192C); +ASSERT_REG_POSITION(viewport_clip_control, 0x193C); +ASSERT_REG_POSITION(user_clip_op, 0x1940); +ASSERT_REG_POSITION(render_enable_override, 0x1944); +ASSERT_REG_POSITION(primitive_topology_control, 0x1948); +ASSERT_REG_POSITION(window_clip_enable, 0x194C); +ASSERT_REG_POSITION(invalidate_zcull, 0x1958); +ASSERT_REG_POSITION(zcull, 0x1968); +ASSERT_REG_POSITION(topology_override, 0x1970); +ASSERT_REG_POSITION(zcull_sync, 0x1978); +ASSERT_REG_POSITION(clip_id_test_enable, 0x197C); +ASSERT_REG_POSITION(surface_clip_id_width, 0x1980); +ASSERT_REG_POSITION(clip_id, 0x1984); +ASSERT_REG_POSITION(depth_bounds_enable, 0x19BC); +ASSERT_REG_POSITION(blend_float_zero_times_anything_is_zero, 0x19C0); +ASSERT_REG_POSITION(logic_op, 0x19C4); +ASSERT_REG_POSITION(z_compression_enable, 0x19CC); +ASSERT_REG_POSITION(clear_surface, 0x19D0); +ASSERT_REG_POSITION(clear_clip_id_surface, 0x19D4); +ASSERT_REG_POSITION(color_compression_enable, 0x19E0); +ASSERT_REG_POSITION(color_mask, 0x1A00); +ASSERT_REG_POSITION(pipe_nop, 0x1A2C); +ASSERT_REG_POSITION(spare, 0x1A30); +ASSERT_REG_POSITION(report_semaphore, 0x1B00); +ASSERT_REG_POSITION(vertex_streams, 0x1C00); +ASSERT_REG_POSITION(blend_per_target, 0x1E00); +ASSERT_REG_POSITION(vertex_stream_limits, 0x1F00); +ASSERT_REG_POSITION(pipelines, 0x2000); +ASSERT_REG_POSITION(falcon, 0x2300); +ASSERT_REG_POSITION(const_buffer, 0x2380); +ASSERT_REG_POSITION(bind_groups, 0x2400); +ASSERT_REG_POSITION(color_clamp_enable, 0x2600); +ASSERT_REG_POSITION(bindless_texture_const_buffer_slot, 0x2608); +ASSERT_REG_POSITION(trap_handler, 0x260C); +ASSERT_REG_POSITION(stream_out_layout, 0x2800); +ASSERT_REG_POSITION(shader_performance, 0x333C); +ASSERT_REG_POSITION(shadow_scratch, 0x3400); #undef ASSERT_REG_POSITION diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 0efe582..a189e60 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/algorithm.h" #include "common/assert.h" #include "common/logging/log.h" #include "common/microprofile.h" @@ -40,8 +41,8 @@ void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) { - for (size_t i = 0; i < amount; ++i) { - CallMethod(method, base_start[i], methods_pending - static_cast(i) <= 1); + for (u32 i = 0; i < amount; ++i) { + CallMethod(method, base_start[i], methods_pending - i <= 1); } } @@ -54,90 +55,128 @@ void MaxwellDMA::Launch() { const LaunchDMA& launch = regs.launch_dma; ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); - ASSERT(regs.dst_params.origin.x == 0); - ASSERT(regs.dst_params.origin.y == 0); - const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH; - const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH; + if (launch.multi_line_enable) { + const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH; + const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH; - if (!is_src_pitch && !is_dst_pitch) { - // If both the source and the destination are in block layout, assert. - UNIMPLEMENTED_MSG("Tiled->Tiled DMA transfers are not yet implemented"); - return; - } + if (!is_src_pitch && !is_dst_pitch) { + // If both the source and the destination are in block layout, assert. + CopyBlockLinearToBlockLinear(); + ReleaseSemaphore(); + return; + } - if (is_src_pitch && is_dst_pitch) { - CopyPitchToPitch(); - } else { - ASSERT(launch.multi_line_enable == 1); - - if (!is_src_pitch && is_dst_pitch) { - CopyBlockLinearToPitch(); + if (is_src_pitch && is_dst_pitch) { + for (u32 line = 0; line < regs.line_count; ++line) { + const GPUVAddr source_line = + regs.offset_in + static_cast(line) * regs.pitch_in; + const GPUVAddr dest_line = + regs.offset_out + static_cast(line) * regs.pitch_out; + memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in); + } } else { - CopyPitchToBlockLinear(); + if (!is_src_pitch && is_dst_pitch) { + CopyBlockLinearToPitch(); + } else { + CopyPitchToBlockLinear(); + } + } + } else { + // TODO: allow multisized components. + auto& accelerate = rasterizer->AccessAccelerateDMA(); + const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A; + if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) { + ASSERT(regs.remap_const.component_size_minus_one == 3); + accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value); + std::vector tmp_buffer(regs.line_length_in, regs.remap_consta_value); + memory_manager.WriteBlockUnsafe(regs.offset_out, + reinterpret_cast(tmp_buffer.data()), + regs.line_length_in * sizeof(u32)); + } else { + const auto convert_linear_2_blocklinear_addr = [](u64 address) { + return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) | + ((address & 0x180) >> 1) | ((address & 0x20) << 3); + }; + const auto src_kind = memory_manager.GetPageKind(regs.offset_in); + const auto dst_kind = memory_manager.GetPageKind(regs.offset_out); + const bool is_src_pitch = IsPitchKind(src_kind); + const bool is_dst_pitch = IsPitchKind(dst_kind); + if (!is_src_pitch && is_dst_pitch) { + UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); + UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); + UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); + std::vector tmp_buffer(16); + for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { + memory_manager.ReadBlockUnsafe( + convert_linear_2_blocklinear_addr(regs.offset_in + offset), + tmp_buffer.data(), tmp_buffer.size()); + memory_manager.WriteBlock(regs.offset_out + offset, tmp_buffer.data(), + tmp_buffer.size()); + } + } else if (is_src_pitch && !is_dst_pitch) { + UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); + UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); + UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); + std::vector tmp_buffer(16); + for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { + memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(), + tmp_buffer.size()); + memory_manager.WriteBlock( + convert_linear_2_blocklinear_addr(regs.offset_out + offset), + tmp_buffer.data(), tmp_buffer.size()); + } + } else { + if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { + std::vector tmp_buffer(regs.line_length_in); + memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), + regs.line_length_in); + memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), + regs.line_length_in); + } + } } } + ReleaseSemaphore(); } -void MaxwellDMA::CopyPitchToPitch() { - // When `multi_line_enable` bit is enabled we copy a 2D image of dimensions - // (line_length_in, line_count). - // Otherwise the copy is performed as if we were copying a 1D buffer of length line_length_in. - const bool remap_enabled = regs.launch_dma.remap_enable != 0; - if (regs.launch_dma.multi_line_enable) { - UNIMPLEMENTED_IF(remap_enabled); - - // Perform a line-by-line copy. - // We're going to take a subrect of size (line_length_in, line_count) from the source - // rectangle. There is no need to manually flush/invalidate the regions because CopyBlock - // does that for us. - for (u32 line = 0; line < regs.line_count; ++line) { - const GPUVAddr source_line = regs.offset_in + static_cast(line) * regs.pitch_in; - const GPUVAddr dest_line = regs.offset_out + static_cast(line) * regs.pitch_out; - memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in); - } - return; - } - // TODO: allow multisized components. - auto& accelerate = rasterizer->AccessAccelerateDMA(); - const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A; - const bool is_buffer_clear = remap_enabled && is_const_a_dst; - if (is_buffer_clear) { - ASSERT(regs.remap_const.component_size_minus_one == 3); - accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value); - std::vector tmp_buffer(regs.line_length_in, regs.remap_consta_value); - memory_manager.WriteBlockUnsafe(regs.offset_out, reinterpret_cast(tmp_buffer.data()), - regs.line_length_in * sizeof(u32)); - return; - } - UNIMPLEMENTED_IF(remap_enabled); - if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { - std::vector tmp_buffer(regs.line_length_in); - memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), regs.line_length_in); - memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), regs.line_length_in); - } -} - void MaxwellDMA::CopyBlockLinearToPitch() { UNIMPLEMENTED_IF(regs.src_params.block_size.width != 0); - UNIMPLEMENTED_IF(regs.src_params.block_size.depth != 0); UNIMPLEMENTED_IF(regs.src_params.layer != 0); + const bool is_remapping = regs.launch_dma.remap_enable != 0; + // Optimized path for micro copies. const size_t dst_size = static_cast(regs.pitch_out) * regs.line_count; - if (dst_size < GOB_SIZE && regs.pitch_out <= GOB_SIZE_X && + if (!is_remapping && dst_size < GOB_SIZE && regs.pitch_out <= GOB_SIZE_X && regs.src_params.height > GOB_SIZE_Y) { FastCopyBlockLinearToPitch(); return; } // Deswizzle the input and copy it over. - UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); - const u32 bytes_per_pixel = - regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1; const Parameters& src_params = regs.src_params; - const u32 width = src_params.width; + + const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1; + const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1; + + const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size; + + u32 width = src_params.width; + u32 x_elements = regs.line_length_in; + u32 x_offset = src_params.origin.x; + u32 bpp_shift = 0U; + if (!is_remapping) { + bpp_shift = Common::FoldRight( + 4U, [](u32 x, u32 y) { return std::min(x, static_cast(std::countr_zero(y))); }, + width, x_elements, x_offset, static_cast(regs.offset_in)); + width >>= bpp_shift; + x_elements >>= bpp_shift; + x_offset >>= bpp_shift; + } + + const u32 bytes_per_pixel = base_bpp << bpp_shift; const u32 height = src_params.height; const u32 depth = src_params.depth; const u32 block_height = src_params.block_size.height; @@ -155,30 +194,45 @@ void MaxwellDMA::CopyBlockLinearToPitch() { memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); - UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel, - block_height, src_params.origin.x, src_params.origin.y, write_buffer.data(), - read_buffer.data()); + UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, + src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, + regs.pitch_out); memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); } void MaxwellDMA::CopyPitchToBlockLinear() { UNIMPLEMENTED_IF_MSG(regs.dst_params.block_size.width != 0, "Block width is not one"); - UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); + UNIMPLEMENTED_IF(regs.dst_params.layer != 0); + + const bool is_remapping = regs.launch_dma.remap_enable != 0; + const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1; + const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1; const auto& dst_params = regs.dst_params; - const u32 bytes_per_pixel = - regs.launch_dma.remap_enable ? regs.pitch_in / regs.line_length_in : 1; - const u32 width = dst_params.width; + + const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size; + + u32 width = dst_params.width; + u32 x_elements = regs.line_length_in; + u32 x_offset = dst_params.origin.x; + u32 bpp_shift = 0U; + if (!is_remapping) { + bpp_shift = Common::FoldRight( + 4U, [](u32 x, u32 y) { return std::min(x, static_cast(std::countr_zero(y))); }, + width, x_elements, x_offset, static_cast(regs.offset_out)); + width >>= bpp_shift; + x_elements >>= bpp_shift; + x_offset >>= bpp_shift; + } + + const u32 bytes_per_pixel = base_bpp << bpp_shift; const u32 height = dst_params.height; const u32 depth = dst_params.depth; const u32 block_height = dst_params.block_size.height; const u32 block_depth = dst_params.block_size.depth; const size_t dst_size = CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); - const size_t dst_layer_size = - CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth); - const size_t src_size = static_cast(regs.pitch_in) * regs.line_count; if (read_buffer.size() < src_size) { @@ -188,32 +242,23 @@ void MaxwellDMA::CopyPitchToBlockLinear() { write_buffer.resize(dst_size); } + memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); if (Settings::IsGPULevelExtreme()) { - memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); } else { - memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size); memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size); } // If the input is linear and the output is tiled, swizzle the input and copy it over. - if (regs.dst_params.block_size.depth > 0) { - ASSERT(dst_params.layer == 0); - SwizzleSliceToVoxel(regs.line_length_in, regs.line_count, regs.pitch_in, width, height, - bytes_per_pixel, block_height, block_depth, dst_params.origin.x, - dst_params.origin.y, write_buffer.data(), read_buffer.data()); - } else { - SwizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_in, width, bytes_per_pixel, - write_buffer.data() + dst_layer_size * dst_params.layer, read_buffer.data(), - block_height, dst_params.origin.x, dst_params.origin.y); - } + SwizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, + dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth, + regs.pitch_in); memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); } void MaxwellDMA::FastCopyBlockLinearToPitch() { - const u32 bytes_per_pixel = - regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1; + const u32 bytes_per_pixel = 1U; const size_t src_size = GOB_SIZE; const size_t dst_size = static_cast(regs.pitch_out) * regs.line_count; u32 pos_x = regs.src_params.origin.x; @@ -239,9 +284,74 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() { memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size); } - UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width, - bytes_per_pixel, regs.src_params.block_size.height, pos_x, pos_y, - write_buffer.data(), read_buffer.data()); + UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, regs.src_params.width, + regs.src_params.height, 1, pos_x, pos_y, regs.line_length_in, regs.line_count, + regs.src_params.block_size.height, regs.src_params.block_size.depth, + regs.pitch_out); + + memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); +} + +void MaxwellDMA::CopyBlockLinearToBlockLinear() { + UNIMPLEMENTED_IF(regs.src_params.block_size.width != 0); + + const bool is_remapping = regs.launch_dma.remap_enable != 0; + + // Deswizzle the input and copy it over. + const Parameters& src = regs.src_params; + const Parameters& dst = regs.dst_params; + + const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1; + const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1; + + const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size; + + u32 src_width = src.width; + u32 dst_width = dst.width; + u32 x_elements = regs.line_length_in; + u32 src_x_offset = src.origin.x; + u32 dst_x_offset = dst.origin.x; + u32 bpp_shift = 0U; + if (!is_remapping) { + bpp_shift = Common::FoldRight( + 4U, [](u32 x, u32 y) { return std::min(x, static_cast(std::countr_zero(y))); }, + src_width, dst_width, x_elements, src_x_offset, dst_x_offset, + static_cast(regs.offset_in), static_cast(regs.offset_out)); + src_width >>= bpp_shift; + dst_width >>= bpp_shift; + x_elements >>= bpp_shift; + src_x_offset >>= bpp_shift; + dst_x_offset >>= bpp_shift; + } + + const u32 bytes_per_pixel = base_bpp << bpp_shift; + const size_t src_size = CalculateSize(true, bytes_per_pixel, src_width, src.height, src.depth, + src.block_size.height, src.block_size.depth); + const size_t dst_size = CalculateSize(true, bytes_per_pixel, dst_width, dst.height, dst.depth, + dst.block_size.height, dst.block_size.depth); + + const u32 pitch = x_elements * bytes_per_pixel; + const size_t mid_buffer_size = pitch * regs.line_count; + + if (read_buffer.size() < src_size) { + read_buffer.resize(src_size); + } + if (write_buffer.size() < dst_size) { + write_buffer.resize(dst_size); + } + + intermediate_buffer.resize(mid_buffer_size); + + memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); + memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); + + UnswizzleSubrect(intermediate_buffer, read_buffer, bytes_per_pixel, src_width, src.height, + src.depth, src_x_offset, src.origin.y, x_elements, regs.line_count, + src.block_size.height, src.block_size.depth, pitch); + + SwizzleSubrect(write_buffer, intermediate_buffer, bytes_per_pixel, dst_width, dst.height, + dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count, + dst.block_size.height, dst.block_size.depth, pitch); memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); } @@ -249,18 +359,27 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() { void MaxwellDMA::ReleaseSemaphore() { const auto type = regs.launch_dma.semaphore_type; const GPUVAddr address = regs.semaphore.address; + const u32 payload = regs.semaphore.payload; switch (type) { case LaunchDMA::SemaphoreType::NONE: break; - case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE: - memory_manager.Write(address, regs.semaphore.payload); + case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE: { + std::function operation( + [this, address, payload] { memory_manager.Write(address, payload); }); + rasterizer->SignalFence(std::move(operation)); break; - case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE: - memory_manager.Write(address, static_cast(regs.semaphore.payload)); - memory_manager.Write(address + 8, system.GPU().GetTicks()); + } + case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE: { + std::function operation([this, address, payload] { + memory_manager.Write(address + sizeof(u64), system.GPU().GetTicks()); + memory_manager.Write(address, payload); + }); + rasterizer->SignalFence(std::move(operation)); break; + } default: ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast(type.Value())); + break; } } diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 074bac9..d40d3d3 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -189,10 +189,16 @@ public: BitField<4, 3, Swizzle> dst_y; BitField<8, 3, Swizzle> dst_z; BitField<12, 3, Swizzle> dst_w; + BitField<0, 12, u32> dst_components_raw; BitField<16, 2, u32> component_size_minus_one; BitField<20, 2, u32> num_src_components_minus_one; BitField<24, 2, u32> num_dst_components_minus_one; }; + + Swizzle GetComponent(size_t i) const { + const u32 raw = dst_components_raw; + return static_cast((raw >> (i * 3)) & 0x7); + } }; static_assert(sizeof(RemapConst) == 12); @@ -213,12 +219,12 @@ private: /// registers. void Launch(); - void CopyPitchToPitch(); - void CopyBlockLinearToPitch(); void CopyPitchToBlockLinear(); + void CopyBlockLinearToBlockLinear(); + void FastCopyBlockLinearToPitch(); void ReleaseSemaphore(); @@ -230,6 +236,7 @@ private: std::vector read_buffer; std::vector write_buffer; + std::vector intermediate_buffer; static constexpr std::size_t NUM_REGS = 0x800; struct Regs { diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp new file mode 100644 index 0000000..7718a09 --- /dev/null +++ b/src/video_core/engines/puller.cpp @@ -0,0 +1,309 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/core.h" +#include "video_core/control/channel_state.h" +#include "video_core/dma_pusher.h" +#include "video_core/engines/fermi_2d.h" +#include "video_core/engines/kepler_compute.h" +#include "video_core/engines/kepler_memory.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/engines/maxwell_dma.h" +#include "video_core/engines/puller.h" +#include "video_core/gpu.h" +#include "video_core/memory_manager.h" +#include "video_core/rasterizer_interface.h" + +namespace Tegra::Engines { + +Puller::Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher_, + Control::ChannelState& channel_state_) + : gpu{gpu_}, memory_manager{memory_manager_}, dma_pusher{dma_pusher_}, channel_state{ + channel_state_} {} + +Puller::~Puller() = default; + +void Puller::ProcessBindMethod(const MethodCall& method_call) { + // Bind the current subchannel to the desired engine id. + LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, + method_call.argument); + const auto engine_id = static_cast(method_call.argument); + bound_engines[method_call.subchannel] = engine_id; + switch (engine_id) { + case EngineID::FERMI_TWOD_A: + dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel); + break; + case EngineID::MAXWELL_B: + dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel); + break; + case EngineID::KEPLER_COMPUTE_B: + dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel); + break; + case EngineID::MAXWELL_DMA_COPY_A: + dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel); + break; + case EngineID::KEPLER_INLINE_TO_MEMORY_B: + dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); + break; + } +} + +void Puller::ProcessFenceActionMethod() { + switch (regs.fence_action.op) { + case Puller::FenceOperation::Acquire: + // UNIMPLEMENTED_MSG("Channel Scheduling pending."); + // WaitFence(regs.fence_action.syncpoint_id, regs.fence_value); + rasterizer->ReleaseFences(); + break; + case Puller::FenceOperation::Increment: + rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); + break; + } +} + +void Puller::ProcessSemaphoreTriggerMethod() { + const auto semaphoreOperationMask = 0xF; + const auto op = + static_cast(regs.semaphore_trigger & semaphoreOperationMask); + if (op == GpuSemaphoreOperation::WriteLong) { + const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; + const u32 payload = regs.semaphore_sequence; + [this, sequence_address, payload] { + memory_manager.Write(sequence_address + sizeof(u64), gpu.GetTicks()); + memory_manager.Write(sequence_address, payload); + }(); + } else { + do { + const u32 word{memory_manager.Read(regs.semaphore_address.SemaphoreAddress())}; + regs.acquire_source = true; + regs.acquire_value = regs.semaphore_sequence; + if (op == GpuSemaphoreOperation::AcquireEqual) { + regs.acquire_active = true; + regs.acquire_mode = false; + if (word != regs.acquire_value) { + rasterizer->ReleaseFences(); + continue; + } + } else if (op == GpuSemaphoreOperation::AcquireGequal) { + regs.acquire_active = true; + regs.acquire_mode = true; + if (word < regs.acquire_value) { + rasterizer->ReleaseFences(); + continue; + } + } else if (op == GpuSemaphoreOperation::AcquireMask) { + if (word && regs.semaphore_sequence == 0) { + rasterizer->ReleaseFences(); + continue; + } + } else { + LOG_ERROR(HW_GPU, "Invalid semaphore operation"); + } + } while (false); + } +} + +void Puller::ProcessSemaphoreRelease() { + const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; + const u32 payload = regs.semaphore_release; + std::function operation([this, sequence_address, payload] { + memory_manager.Write(sequence_address, payload); + }); + rasterizer->SignalFence(std::move(operation)); +} + +void Puller::ProcessSemaphoreAcquire() { + u32 word = memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); + const auto value = regs.semaphore_acquire; + while (word != value) { + regs.acquire_active = true; + regs.acquire_value = value; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + rasterizer->ReleaseFences(); + word = memory_manager.Read(regs.semaphore_address.SemaphoreAddress()); + // TODO(kemathe73) figure out how to do the acquire_timeout + regs.acquire_mode = false; + regs.acquire_source = false; + } +} + +/// Calls a GPU puller method. +void Puller::CallPullerMethod(const MethodCall& method_call) { + regs.reg_array[method_call.method] = method_call.argument; + const auto method = static_cast(method_call.method); + + switch (method) { + case BufferMethods::BindObject: { + ProcessBindMethod(method_call); + break; + } + case BufferMethods::Nop: + case BufferMethods::SemaphoreAddressHigh: + case BufferMethods::SemaphoreAddressLow: + case BufferMethods::SemaphoreSequencePayload: + case BufferMethods::SyncpointPayload: + case BufferMethods::WrcacheFlush: + break; + case BufferMethods::RefCnt: + rasterizer->SignalReference(); + break; + case BufferMethods::SyncpointOperation: + ProcessFenceActionMethod(); + break; + case BufferMethods::WaitForIdle: + rasterizer->WaitForIdle(); + break; + case BufferMethods::SemaphoreOperation: { + ProcessSemaphoreTriggerMethod(); + break; + } + case BufferMethods::NonStallInterrupt: { + LOG_ERROR(HW_GPU, "Special puller engine method NonStallInterrupt not implemented"); + break; + } + case BufferMethods::MemOpA: { + LOG_ERROR(HW_GPU, "Memory Operation A"); + break; + } + case BufferMethods::MemOpB: { + // Implement this better. + rasterizer->InvalidateGPUCache(); + break; + } + case BufferMethods::MemOpC: + case BufferMethods::MemOpD: { + LOG_ERROR(HW_GPU, "Memory Operation C,D"); + break; + } + case BufferMethods::SemaphoreAcquire: { + ProcessSemaphoreAcquire(); + break; + } + case BufferMethods::SemaphoreRelease: { + ProcessSemaphoreRelease(); + break; + } + case BufferMethods::Yield: { + // TODO(Kmather73): Research and implement this method. + LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented"); + break; + } + default: + LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented", method); + break; + } +} + +/// Calls a GPU engine method. +void Puller::CallEngineMethod(const MethodCall& method_call) { + const EngineID engine = bound_engines[method_call.subchannel]; + + switch (engine) { + case EngineID::FERMI_TWOD_A: + channel_state.fermi_2d->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::MAXWELL_B: + channel_state.maxwell_3d->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::KEPLER_COMPUTE_B: + channel_state.kepler_compute->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::MAXWELL_DMA_COPY_A: + channel_state.maxwell_dma->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::KEPLER_INLINE_TO_MEMORY_B: + channel_state.kepler_memory->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented engine"); + break; + } +} + +/// Calls a GPU engine multivalue method. +void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, + u32 methods_pending) { + const EngineID engine = bound_engines[subchannel]; + + switch (engine) { + case EngineID::FERMI_TWOD_A: + channel_state.fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::MAXWELL_B: + channel_state.maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::KEPLER_COMPUTE_B: + channel_state.kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::MAXWELL_DMA_COPY_A: + channel_state.maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::KEPLER_INLINE_TO_MEMORY_B: + channel_state.kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented engine"); + break; + } +} + +/// Calls a GPU method. +void Puller::CallMethod(const MethodCall& method_call) { + LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, + method_call.subchannel); + + ASSERT(method_call.subchannel < bound_engines.size()); + + if (ExecuteMethodOnEngine(method_call.method)) { + CallEngineMethod(method_call); + } else { + CallPullerMethod(method_call); + } +} + +/// Calls a GPU multivalue method. +void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, + u32 methods_pending) { + LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel); + + ASSERT(subchannel < bound_engines.size()); + + if (ExecuteMethodOnEngine(method)) { + CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending); + } else { + for (u32 i = 0; i < amount; i++) { + CallPullerMethod(MethodCall{ + method, + base_start[i], + subchannel, + methods_pending - i, + }); + } + } +} + +void Puller::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { + rasterizer = rasterizer_; +} + +/// Determines where the method should be executed. +[[nodiscard]] bool Puller::ExecuteMethodOnEngine(u32 method) { + const auto buffer_method = static_cast(method); + return buffer_method >= BufferMethods::NonPullerMethods; +} + +} // namespace Tegra::Engines diff --git a/src/video_core/engines/puller.h b/src/video_core/engines/puller.h new file mode 100644 index 0000000..d4175ee --- /dev/null +++ b/src/video_core/engines/puller.h @@ -0,0 +1,177 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "video_core/engines/engine_interface.h" + +namespace Core { +class System; +} + +namespace Tegra { +class MemoryManager; +class DmaPusher; + +enum class EngineID { + FERMI_TWOD_A = 0x902D, // 2D Engine + MAXWELL_B = 0xB197, // 3D Engine + KEPLER_COMPUTE_B = 0xB1C0, + KEPLER_INLINE_TO_MEMORY_B = 0xA140, + MAXWELL_DMA_COPY_A = 0xB0B5, +}; + +namespace Control { +struct ChannelState; +} +} // namespace Tegra + +namespace VideoCore { +class RasterizerInterface; +} + +namespace Tegra::Engines { + +class Puller final { +public: + struct MethodCall { + u32 method{}; + u32 argument{}; + u32 subchannel{}; + u32 method_count{}; + + explicit MethodCall(u32 method_, u32 argument_, u32 subchannel_ = 0, u32 method_count_ = 0) + : method(method_), argument(argument_), subchannel(subchannel_), + method_count(method_count_) {} + + [[nodiscard]] bool IsLastCall() const { + return method_count <= 1; + } + }; + + enum class FenceOperation : u32 { + Acquire = 0, + Increment = 1, + }; + + union FenceAction { + u32 raw; + BitField<0, 1, FenceOperation> op; + BitField<8, 24, u32> syncpoint_id; + }; + + explicit Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher, + Control::ChannelState& channel_state); + ~Puller(); + + void CallMethod(const MethodCall& method_call); + + void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, + u32 methods_pending); + + void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); + + void CallPullerMethod(const MethodCall& method_call); + + void CallEngineMethod(const MethodCall& method_call); + + void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, + u32 methods_pending); + +private: + Tegra::GPU& gpu; + + MemoryManager& memory_manager; + DmaPusher& dma_pusher; + Control::ChannelState& channel_state; + VideoCore::RasterizerInterface* rasterizer = nullptr; + + static constexpr std::size_t NUM_REGS = 0x800; + struct Regs { + static constexpr size_t NUM_REGS = 0x40; + + union { + struct { + INSERT_PADDING_WORDS_NOINIT(0x4); + struct { + u32 address_high; + u32 address_low; + + [[nodiscard]] GPUVAddr SemaphoreAddress() const { + return static_cast((static_cast(address_high) << 32) | + address_low); + } + } semaphore_address; + + u32 semaphore_sequence; + u32 semaphore_trigger; + INSERT_PADDING_WORDS_NOINIT(0xC); + + // The pusher and the puller share the reference counter, the pusher only has read + // access + u32 reference_count; + INSERT_PADDING_WORDS_NOINIT(0x5); + + u32 semaphore_acquire; + u32 semaphore_release; + u32 fence_value; + FenceAction fence_action; + INSERT_PADDING_WORDS_NOINIT(0xE2); + + // Puller state + u32 acquire_mode; + u32 acquire_source; + u32 acquire_active; + u32 acquire_timeout; + u32 acquire_value; + }; + std::array reg_array; + }; + } regs{}; + + void ProcessBindMethod(const MethodCall& method_call); + void ProcessFenceActionMethod(); + void ProcessSemaphoreAcquire(); + void ProcessSemaphoreRelease(); + void ProcessSemaphoreTriggerMethod(); + [[nodiscard]] bool ExecuteMethodOnEngine(u32 method); + + /// Mapping of command subchannels to their bound engine ids + std::array bound_engines{}; + + enum class GpuSemaphoreOperation { + AcquireEqual = 0x1, + WriteLong = 0x2, + AcquireGequal = 0x4, + AcquireMask = 0x8, + }; + +#define ASSERT_REG_POSITION(field_name, position) \ + static_assert(offsetof(Regs, field_name) == position * 4, \ + "Field " #field_name " has invalid position") + + ASSERT_REG_POSITION(semaphore_address, 0x4); + ASSERT_REG_POSITION(semaphore_sequence, 0x6); + ASSERT_REG_POSITION(semaphore_trigger, 0x7); + ASSERT_REG_POSITION(reference_count, 0x14); + ASSERT_REG_POSITION(semaphore_acquire, 0x1A); + ASSERT_REG_POSITION(semaphore_release, 0x1B); + ASSERT_REG_POSITION(fence_value, 0x1C); + ASSERT_REG_POSITION(fence_action, 0x1D); + + ASSERT_REG_POSITION(acquire_mode, 0x100); + ASSERT_REG_POSITION(acquire_source, 0x101); + ASSERT_REG_POSITION(acquire_active, 0x102); + ASSERT_REG_POSITION(acquire_timeout, 0x103); + ASSERT_REG_POSITION(acquire_value, 0x104); + +#undef ASSERT_REG_POSITION +}; + +} // namespace Tegra::Engines diff --git a/src/video_core/engines/sw_blitter/blitter.cpp b/src/video_core/engines/sw_blitter/blitter.cpp new file mode 100644 index 0000000..2f1ea46 --- /dev/null +++ b/src/video_core/engines/sw_blitter/blitter.cpp @@ -0,0 +1,238 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include + +#include "video_core/engines/sw_blitter/blitter.h" +#include "video_core/engines/sw_blitter/converter.h" +#include "video_core/memory_manager.h" +#include "video_core/surface.h" +#include "video_core/textures/decoders.h" + +namespace Tegra { +class MemoryManager; +} + +using VideoCore::Surface::BytesPerBlock; +using VideoCore::Surface::PixelFormatFromRenderTargetFormat; + +namespace Tegra::Engines::Blitter { + +using namespace Texture; + +namespace { + +constexpr size_t ir_components = 4; + +void NearestNeighbor(std::span input, std::span output, u32 src_width, u32 src_height, + u32 dst_width, u32 dst_height, size_t bpp) { + const size_t dx_du = std::llround((static_cast(src_width) / dst_width) * (1ULL << 32)); + const size_t dy_dv = std::llround((static_cast(src_height) / dst_height) * (1ULL << 32)); + size_t src_y = 0; + for (u32 y = 0; y < dst_height; y++) { + size_t src_x = 0; + for (u32 x = 0; x < dst_width; x++) { + const size_t read_from = ((src_y * src_width + src_x) >> 32) * bpp; + const size_t write_to = (y * dst_width + x) * bpp; + + std::memcpy(&output[write_to], &input[read_from], bpp); + src_x += dx_du; + } + src_y += dy_dv; + } +} + +void NearestNeighborFast(std::span input, std::span output, u32 src_width, + u32 src_height, u32 dst_width, u32 dst_height) { + const size_t dx_du = std::llround((static_cast(src_width) / dst_width) * (1ULL << 32)); + const size_t dy_dv = std::llround((static_cast(src_height) / dst_height) * (1ULL << 32)); + size_t src_y = 0; + for (u32 y = 0; y < dst_height; y++) { + size_t src_x = 0; + for (u32 x = 0; x < dst_width; x++) { + const size_t read_from = ((src_y * src_width + src_x) >> 32) * ir_components; + const size_t write_to = (y * dst_width + x) * ir_components; + + std::memcpy(&output[write_to], &input[read_from], sizeof(f32) * ir_components); + src_x += dx_du; + } + src_y += dy_dv; + } +} + +void Bilinear(std::span input, std::span output, size_t src_width, + size_t src_height, size_t dst_width, size_t dst_height) { + const auto bilinear_sample = [](std::span x0_y0, std::span x1_y0, + std::span x0_y1, std::span x1_y1, + f32 weight_x, f32 weight_y) { + std::array result{}; + for (size_t i = 0; i < ir_components; i++) { + const f32 a = std::lerp(x0_y0[i], x1_y0[i], weight_x); + const f32 b = std::lerp(x0_y1[i], x1_y1[i], weight_x); + result[i] = std::lerp(a, b, weight_y); + } + return result; + }; + const f32 dx_du = + dst_width > 1 ? static_cast(src_width - 1) / static_cast(dst_width - 1) : 0.f; + const f32 dy_dv = + dst_height > 1 ? static_cast(src_height - 1) / static_cast(dst_height - 1) : 0.f; + for (u32 y = 0; y < dst_height; y++) { + for (u32 x = 0; x < dst_width; x++) { + const f32 x_low = std::floor(static_cast(x) * dx_du); + const f32 y_low = std::floor(static_cast(y) * dy_dv); + const f32 x_high = std::ceil(static_cast(x) * dx_du); + const f32 y_high = std::ceil(static_cast(y) * dy_dv); + const f32 weight_x = (static_cast(x) * dx_du) - x_low; + const f32 weight_y = (static_cast(y) * dy_dv) - y_low; + + const auto read_src = [&](f32 in_x, f32 in_y) { + const size_t read_from = + ((static_cast(in_x) * src_width + static_cast(in_y)) >> 32) * + ir_components; + return std::span(&input[read_from], ir_components); + }; + + auto x0_y0 = read_src(x_low, y_low); + auto x1_y0 = read_src(x_high, y_low); + auto x0_y1 = read_src(x_low, y_high); + auto x1_y1 = read_src(x_high, y_high); + + const auto result = bilinear_sample(x0_y0, x1_y0, x0_y1, x1_y1, weight_x, weight_y); + + const size_t write_to = (y * dst_width + x) * ir_components; + + std::memcpy(&output[write_to], &result, sizeof(f32) * ir_components); + } + } +} + +} // namespace + +struct SoftwareBlitEngine::BlitEngineImpl { + std::vector tmp_buffer; + std::vector src_buffer; + std::vector dst_buffer; + std::vector intermediate_src; + std::vector intermediate_dst; + ConverterFactory converter_factory; +}; + +SoftwareBlitEngine::SoftwareBlitEngine(MemoryManager& memory_manager_) + : memory_manager{memory_manager_} { + impl = std::make_unique(); +} + +SoftwareBlitEngine::~SoftwareBlitEngine() = default; + +bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, + Fermi2D::Config& config) { + const auto get_surface_size = [](Fermi2D::Surface& surface, u32 bytes_per_pixel) { + if (surface.linear == Fermi2D::MemoryLayout::BlockLinear) { + return CalculateSize(true, bytes_per_pixel, surface.width, surface.height, + surface.depth, surface.block_height, surface.block_depth); + } + return static_cast(surface.pitch * surface.height); + }; + const auto process_pitch_linear = [](bool unpack, std::span input, + std::span output, u32 extent_x, u32 extent_y, + u32 pitch, u32 x0, u32 y0, size_t bpp) { + const size_t base_offset = x0 * bpp; + const size_t copy_size = extent_x * bpp; + for (u32 y = y0; y < extent_y; y++) { + const size_t first_offset = y * pitch + base_offset; + const size_t second_offset = y * extent_x * bpp; + u8* write_to = unpack ? &output[first_offset] : &output[second_offset]; + const u8* read_from = unpack ? &input[second_offset] : &input[first_offset]; + std::memcpy(write_to, read_from, copy_size); + } + }; + + const u32 src_extent_x = config.src_x1 - config.src_x0; + const u32 src_extent_y = config.src_y1 - config.src_y0; + + const u32 dst_extent_x = config.dst_x1 - config.dst_x0; + const u32 dst_extent_y = config.dst_y1 - config.dst_y0; + const auto src_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); + const auto dst_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(dst.format)); + const size_t src_size = get_surface_size(src, src_bytes_per_pixel); + impl->tmp_buffer.resize(src_size); + memory_manager.ReadBlock(src.Address(), impl->tmp_buffer.data(), src_size); + + const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel; + + const size_t dst_copy_size = dst_extent_x * dst_extent_y * dst_bytes_per_pixel; + + impl->src_buffer.resize(src_copy_size); + + const bool no_passthrough = + src.format != dst.format || src_extent_x != dst_extent_x || src_extent_y != dst_extent_y; + + const auto convertion_phase_same_format = [&]() { + NearestNeighbor(impl->src_buffer, impl->dst_buffer, src_extent_x, src_extent_y, + dst_extent_x, dst_extent_y, dst_bytes_per_pixel); + }; + + const auto convertion_phase_ir = [&]() { + auto* input_converter = impl->converter_factory.GetFormatConverter(src.format); + impl->intermediate_src.resize((src_copy_size / src_bytes_per_pixel) * ir_components); + impl->intermediate_dst.resize((dst_copy_size / dst_bytes_per_pixel) * ir_components); + input_converter->ConvertTo(impl->src_buffer, impl->intermediate_src); + + if (config.filter != Fermi2D::Filter::Bilinear) { + NearestNeighborFast(impl->intermediate_src, impl->intermediate_dst, src_extent_x, + src_extent_y, dst_extent_x, dst_extent_y); + } else { + Bilinear(impl->intermediate_src, impl->intermediate_dst, src_extent_x, src_extent_y, + dst_extent_x, dst_extent_y); + } + + auto* output_converter = impl->converter_factory.GetFormatConverter(dst.format); + output_converter->ConvertFrom(impl->intermediate_dst, impl->dst_buffer); + }; + + // Do actuall Blit + + impl->dst_buffer.resize(dst_copy_size); + if (src.linear == Fermi2D::MemoryLayout::BlockLinear) { + UnswizzleSubrect(impl->src_buffer, impl->tmp_buffer, src_bytes_per_pixel, src.width, + src.height, src.depth, config.src_x0, config.src_y0, src_extent_x, + src_extent_y, src.block_height, src.block_depth, + src_extent_x * src_bytes_per_pixel); + } else { + process_pitch_linear(false, impl->tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y, + src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel); + } + + // Conversion Phase + if (no_passthrough) { + if (src.format != dst.format || config.filter == Fermi2D::Filter::Bilinear) { + convertion_phase_ir(); + } else { + convertion_phase_same_format(); + } + } else { + impl->dst_buffer.swap(impl->src_buffer); + } + + const size_t dst_size = get_surface_size(dst, dst_bytes_per_pixel); + impl->tmp_buffer.resize(dst_size); + memory_manager.ReadBlock(dst.Address(), impl->tmp_buffer.data(), dst_size); + + if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) { + SwizzleSubrect(impl->tmp_buffer, impl->dst_buffer, dst_bytes_per_pixel, dst.width, + dst.height, dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, + dst_extent_y, dst.block_height, dst.block_depth, + dst_extent_x * dst_bytes_per_pixel); + } else { + process_pitch_linear(true, impl->dst_buffer, impl->tmp_buffer, dst_extent_x, dst_extent_y, + dst.pitch, config.dst_x0, config.dst_y0, + static_cast(dst_bytes_per_pixel)); + } + memory_manager.WriteBlock(dst.Address(), impl->tmp_buffer.data(), dst_size); + return true; +} + +} // namespace Tegra::Engines::Blitter diff --git a/src/video_core/engines/sw_blitter/blitter.h b/src/video_core/engines/sw_blitter/blitter.h new file mode 100644 index 0000000..85b55c8 --- /dev/null +++ b/src/video_core/engines/sw_blitter/blitter.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "video_core/engines/fermi_2d.h" + +namespace Tegra { +class MemoryManager; +} + +namespace Tegra::Engines::Blitter { + +class SoftwareBlitEngine { +public: + explicit SoftwareBlitEngine(MemoryManager& memory_manager_); + ~SoftwareBlitEngine(); + + bool Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, Fermi2D::Config& copy_config); + +private: + MemoryManager& memory_manager; + struct BlitEngineImpl; + std::unique_ptr impl; +}; + +} // namespace Tegra::Engines::Blitter diff --git a/src/video_core/engines/sw_blitter/converter.cpp b/src/video_core/engines/sw_blitter/converter.cpp new file mode 100644 index 0000000..2419b56 --- /dev/null +++ b/src/video_core/engines/sw_blitter/converter.cpp @@ -0,0 +1,1234 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include + +#include "common/assert.h" +#include "common/bit_cast.h" +#include "video_core/engines/sw_blitter/converter.h" +#include "video_core/surface.h" +#include "video_core/textures/decoders.h" + +#ifdef _MSC_VER +#define FORCE_INLINE __forceinline +#else +#define FORCE_INLINE inline __attribute__((always_inline)) +#endif + +namespace Tegra::Engines::Blitter { + +enum class Swizzle : size_t { + R = 0, + G = 1, + B = 2, + A = 3, + None, +}; + +enum class ComponentType : u32 { + SNORM = 1, + UNORM = 2, + SINT = 3, + UINT = 4, + SNORM_FORCE_FP16 = 5, + UNORM_FORCE_FP16 = 6, + FLOAT = 7, + SRGB = 8, +}; + +namespace { + +/* + * Note: Use generate_converters.py to generate the structs and searches for new render target + * formats and copy paste them to this file in order to update. just call "python + * generate_converters.py" and get the code from the output. modify the file to add new formats. + */ + +constexpr std::array SRGB_TO_RGB_LUT = { + 0.000000e+00f, 3.035270e-04f, 6.070540e-04f, 9.105810e-04f, 1.214108e-03f, 1.517635e-03f, + 1.821162e-03f, 2.124689e-03f, 2.428216e-03f, 2.731743e-03f, 3.035270e-03f, 3.346536e-03f, + 3.676507e-03f, 4.024717e-03f, 4.391442e-03f, 4.776953e-03f, 5.181517e-03f, 5.605392e-03f, + 6.048833e-03f, 6.512091e-03f, 6.995410e-03f, 7.499032e-03f, 8.023193e-03f, 8.568126e-03f, + 9.134059e-03f, 9.721218e-03f, 1.032982e-02f, 1.096009e-02f, 1.161224e-02f, 1.228649e-02f, + 1.298303e-02f, 1.370208e-02f, 1.444384e-02f, 1.520851e-02f, 1.599629e-02f, 1.680738e-02f, + 1.764195e-02f, 1.850022e-02f, 1.938236e-02f, 2.028856e-02f, 2.121901e-02f, 2.217389e-02f, + 2.315337e-02f, 2.415763e-02f, 2.518686e-02f, 2.624122e-02f, 2.732089e-02f, 2.842604e-02f, + 2.955684e-02f, 3.071344e-02f, 3.189603e-02f, 3.310477e-02f, 3.433981e-02f, 3.560131e-02f, + 3.688945e-02f, 3.820437e-02f, 3.954624e-02f, 4.091520e-02f, 4.231141e-02f, 4.373503e-02f, + 4.518620e-02f, 4.666509e-02f, 4.817183e-02f, 4.970657e-02f, 5.126946e-02f, 5.286065e-02f, + 5.448028e-02f, 5.612849e-02f, 5.780543e-02f, 5.951124e-02f, 6.124605e-02f, 6.301001e-02f, + 6.480327e-02f, 6.662594e-02f, 6.847817e-02f, 7.036009e-02f, 7.227185e-02f, 7.421357e-02f, + 7.618538e-02f, 7.818742e-02f, 8.021982e-02f, 8.228271e-02f, 8.437621e-02f, 8.650046e-02f, + 8.865558e-02f, 9.084171e-02f, 9.305897e-02f, 9.530747e-02f, 9.758735e-02f, 9.989873e-02f, + 1.022417e-01f, 1.046165e-01f, 1.070231e-01f, 1.094617e-01f, 1.119324e-01f, 1.144354e-01f, + 1.169707e-01f, 1.195384e-01f, 1.221388e-01f, 1.247718e-01f, 1.274377e-01f, 1.301365e-01f, + 1.328683e-01f, 1.356333e-01f, 1.384316e-01f, 1.412633e-01f, 1.441285e-01f, 1.470273e-01f, + 1.499598e-01f, 1.529261e-01f, 1.559265e-01f, 1.589608e-01f, 1.620294e-01f, 1.651322e-01f, + 1.682694e-01f, 1.714411e-01f, 1.746474e-01f, 1.778884e-01f, 1.811642e-01f, 1.844750e-01f, + 1.878208e-01f, 1.912017e-01f, 1.946178e-01f, 1.980693e-01f, 2.015563e-01f, 2.050787e-01f, + 2.086369e-01f, 2.122308e-01f, 2.158605e-01f, 2.195262e-01f, 2.232280e-01f, 2.269659e-01f, + 2.307401e-01f, 2.345506e-01f, 2.383976e-01f, 2.422811e-01f, 2.462013e-01f, 2.501583e-01f, + 2.541521e-01f, 2.581829e-01f, 2.622507e-01f, 2.663556e-01f, 2.704978e-01f, 2.746773e-01f, + 2.788943e-01f, 2.831487e-01f, 2.874408e-01f, 2.917706e-01f, 2.961383e-01f, 3.005438e-01f, + 3.049873e-01f, 3.094689e-01f, 3.139887e-01f, 3.185468e-01f, 3.231432e-01f, 3.277781e-01f, + 3.324515e-01f, 3.371636e-01f, 3.419144e-01f, 3.467041e-01f, 3.515326e-01f, 3.564001e-01f, + 3.613068e-01f, 3.662526e-01f, 3.712377e-01f, 3.762621e-01f, 3.813260e-01f, 3.864294e-01f, + 3.915725e-01f, 3.967552e-01f, 4.019778e-01f, 4.072402e-01f, 4.125426e-01f, 4.178851e-01f, + 4.232677e-01f, 4.286905e-01f, 4.341536e-01f, 4.396572e-01f, 4.452012e-01f, 4.507858e-01f, + 4.564110e-01f, 4.620770e-01f, 4.677838e-01f, 4.735315e-01f, 4.793202e-01f, 4.851499e-01f, + 4.910209e-01f, 4.969330e-01f, 5.028865e-01f, 5.088813e-01f, 5.149177e-01f, 5.209956e-01f, + 5.271151e-01f, 5.332764e-01f, 5.394795e-01f, 5.457245e-01f, 5.520114e-01f, 5.583404e-01f, + 5.647115e-01f, 5.711249e-01f, 5.775805e-01f, 5.840784e-01f, 5.906188e-01f, 5.972018e-01f, + 6.038274e-01f, 6.104956e-01f, 6.172066e-01f, 6.239604e-01f, 6.307572e-01f, 6.375968e-01f, + 6.444797e-01f, 6.514056e-01f, 6.583748e-01f, 6.653873e-01f, 6.724432e-01f, 6.795425e-01f, + 6.866853e-01f, 6.938717e-01f, 7.011019e-01f, 7.083758e-01f, 7.156935e-01f, 7.230551e-01f, + 7.304608e-01f, 7.379104e-01f, 7.454042e-01f, 7.529422e-01f, 7.605245e-01f, 7.681512e-01f, + 7.758222e-01f, 7.835378e-01f, 7.912979e-01f, 7.991027e-01f, 8.069522e-01f, 8.148466e-01f, + 8.227857e-01f, 8.307699e-01f, 8.387990e-01f, 8.468732e-01f, 8.549926e-01f, 8.631572e-01f, + 8.713671e-01f, 8.796224e-01f, 8.879231e-01f, 8.962694e-01f, 9.046612e-01f, 9.130986e-01f, + 9.215819e-01f, 9.301109e-01f, 9.386857e-01f, 9.473065e-01f, 9.559733e-01f, 9.646863e-01f, + 9.734453e-01f, 9.822506e-01f, 9.911021e-01f, 1.000000e+00f}; + +constexpr std::array RGB_TO_SRGB_LUT = { + 0.000000e+00f, 4.984009e-02f, 8.494473e-02f, 1.107021e-01f, 1.318038e-01f, 1.500052e-01f, + 1.661857e-01f, 1.808585e-01f, 1.943532e-01f, 2.068957e-01f, 2.186491e-01f, 2.297351e-01f, + 2.402475e-01f, 2.502604e-01f, 2.598334e-01f, 2.690152e-01f, 2.778465e-01f, 2.863614e-01f, + 2.945889e-01f, 3.025538e-01f, 3.102778e-01f, 3.177796e-01f, 3.250757e-01f, 3.321809e-01f, + 3.391081e-01f, 3.458689e-01f, 3.524737e-01f, 3.589320e-01f, 3.652521e-01f, 3.714419e-01f, + 3.775084e-01f, 3.834581e-01f, 3.892968e-01f, 3.950301e-01f, 4.006628e-01f, 4.061998e-01f, + 4.116451e-01f, 4.170030e-01f, 4.222770e-01f, 4.274707e-01f, 4.325873e-01f, 4.376298e-01f, + 4.426010e-01f, 4.475037e-01f, 4.523403e-01f, 4.571131e-01f, 4.618246e-01f, 4.664766e-01f, + 4.710712e-01f, 4.756104e-01f, 4.800958e-01f, 4.845292e-01f, 4.889122e-01f, 4.932462e-01f, + 4.975329e-01f, 5.017734e-01f, 5.059693e-01f, 5.101216e-01f, 5.142317e-01f, 5.183006e-01f, + 5.223295e-01f, 5.263194e-01f, 5.302714e-01f, 5.341862e-01f, 5.380651e-01f, 5.419087e-01f, + 5.457181e-01f, 5.494938e-01f, 5.532369e-01f, 5.569480e-01f, 5.606278e-01f, 5.642771e-01f, + 5.678965e-01f, 5.714868e-01f, 5.750484e-01f, 5.785821e-01f, 5.820884e-01f, 5.855680e-01f, + 5.890211e-01f, 5.924487e-01f, 5.958509e-01f, 5.992285e-01f, 6.025819e-01f, 6.059114e-01f, + 6.092176e-01f, 6.125010e-01f, 6.157619e-01f, 6.190008e-01f, 6.222180e-01f, 6.254140e-01f, + 6.285890e-01f, 6.317436e-01f, 6.348780e-01f, 6.379926e-01f, 6.410878e-01f, 6.441637e-01f, + 6.472208e-01f, 6.502595e-01f, 6.532799e-01f, 6.562824e-01f, 6.592672e-01f, 6.622347e-01f, + 6.651851e-01f, 6.681187e-01f, 6.710356e-01f, 6.739363e-01f, 6.768209e-01f, 6.796897e-01f, + 6.825429e-01f, 6.853807e-01f, 6.882034e-01f, 6.910111e-01f, 6.938041e-01f, 6.965826e-01f, + 6.993468e-01f, 7.020969e-01f, 7.048331e-01f, 7.075556e-01f, 7.102645e-01f, 7.129600e-01f, + 7.156424e-01f, 7.183118e-01f, 7.209683e-01f, 7.236121e-01f, 7.262435e-01f, 7.288625e-01f, + 7.314693e-01f, 7.340640e-01f, 7.366470e-01f, 7.392181e-01f, 7.417776e-01f, 7.443256e-01f, + 7.468624e-01f, 7.493880e-01f, 7.519025e-01f, 7.544061e-01f, 7.568989e-01f, 7.593810e-01f, + 7.618526e-01f, 7.643137e-01f, 7.667645e-01f, 7.692052e-01f, 7.716358e-01f, 7.740564e-01f, + 7.764671e-01f, 7.788681e-01f, 7.812595e-01f, 7.836413e-01f, 7.860138e-01f, 7.883768e-01f, + 7.907307e-01f, 7.930754e-01f, 7.954110e-01f, 7.977377e-01f, 8.000556e-01f, 8.023647e-01f, + 8.046651e-01f, 8.069569e-01f, 8.092403e-01f, 8.115152e-01f, 8.137818e-01f, 8.160402e-01f, + 8.182903e-01f, 8.205324e-01f, 8.227665e-01f, 8.249926e-01f, 8.272109e-01f, 8.294214e-01f, + 8.316242e-01f, 8.338194e-01f, 8.360070e-01f, 8.381871e-01f, 8.403597e-01f, 8.425251e-01f, + 8.446831e-01f, 8.468339e-01f, 8.489776e-01f, 8.511142e-01f, 8.532437e-01f, 8.553662e-01f, + 8.574819e-01f, 8.595907e-01f, 8.616927e-01f, 8.637881e-01f, 8.658767e-01f, 8.679587e-01f, + 8.700342e-01f, 8.721032e-01f, 8.741657e-01f, 8.762218e-01f, 8.782716e-01f, 8.803151e-01f, + 8.823524e-01f, 8.843835e-01f, 8.864085e-01f, 8.884274e-01f, 8.904402e-01f, 8.924471e-01f, + 8.944480e-01f, 8.964431e-01f, 8.984324e-01f, 9.004158e-01f, 9.023935e-01f, 9.043654e-01f, + 9.063318e-01f, 9.082925e-01f, 9.102476e-01f, 9.121972e-01f, 9.141413e-01f, 9.160800e-01f, + 9.180133e-01f, 9.199412e-01f, 9.218637e-01f, 9.237810e-01f, 9.256931e-01f, 9.276000e-01f, + 9.295017e-01f, 9.313982e-01f, 9.332896e-01f, 9.351761e-01f, 9.370575e-01f, 9.389339e-01f, + 9.408054e-01f, 9.426719e-01f, 9.445336e-01f, 9.463905e-01f, 9.482424e-01f, 9.500897e-01f, + 9.519322e-01f, 9.537700e-01f, 9.556032e-01f, 9.574316e-01f, 9.592555e-01f, 9.610748e-01f, + 9.628896e-01f, 9.646998e-01f, 9.665055e-01f, 9.683068e-01f, 9.701037e-01f, 9.718961e-01f, + 9.736842e-01f, 9.754679e-01f, 9.772474e-01f, 9.790225e-01f, 9.807934e-01f, 9.825601e-01f, + 9.843225e-01f, 9.860808e-01f, 9.878350e-01f, 9.895850e-01f, 9.913309e-01f, 9.930727e-01f, + 9.948106e-01f, 9.965444e-01f, 9.982741e-01f, 1.000000e+00f}; + +} // namespace + +struct R32G32B32A32_FLOATTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT}; + static constexpr std::array component_sizes = {32, 32, 32, 32}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A}; +}; + +struct R32G32B32A32_SINTTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::SINT, ComponentType::SINT, ComponentType::SINT, ComponentType::SINT}; + static constexpr std::array component_sizes = {32, 32, 32, 32}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A}; +}; + +struct R32G32B32A32_UINTTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UINT, ComponentType::UINT, ComponentType::UINT, ComponentType::UINT}; + static constexpr std::array component_sizes = {32, 32, 32, 32}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A}; +}; + +struct R32G32B32X32_FLOATTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT}; + static constexpr std::array component_sizes = {32, 32, 32, 32}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::None}; +}; + +struct R32G32B32X32_SINTTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::SINT, ComponentType::SINT, ComponentType::SINT, ComponentType::SINT}; + static constexpr std::array component_sizes = {32, 32, 32, 32}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::None}; +}; + +struct R32G32B32X32_UINTTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UINT, ComponentType::UINT, ComponentType::UINT, ComponentType::UINT}; + static constexpr std::array component_sizes = {32, 32, 32, 32}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::None}; +}; + +struct R16G16B16A16_UNORMTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {16, 16, 16, 16}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A}; +}; + +struct R16G16B16A16_SNORMTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::SNORM, ComponentType::SNORM, ComponentType::SNORM, ComponentType::SNORM}; + static constexpr std::array component_sizes = {16, 16, 16, 16}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A}; +}; + +struct R16G16B16A16_SINTTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::SINT, ComponentType::SINT, ComponentType::SINT, ComponentType::SINT}; + static constexpr std::array component_sizes = {16, 16, 16, 16}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A}; +}; + +struct R16G16B16A16_UINTTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UINT, ComponentType::UINT, ComponentType::UINT, ComponentType::UINT}; + static constexpr std::array component_sizes = {16, 16, 16, 16}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A}; +}; + +struct R16G16B16A16_FLOATTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT}; + static constexpr std::array component_sizes = {16, 16, 16, 16}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A}; +}; + +struct R32G32_FLOATTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::FLOAT, ComponentType::FLOAT}; + static constexpr std::array component_sizes = {32, 32}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct R32G32_SINTTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::SINT, ComponentType::SINT}; + static constexpr std::array component_sizes = {32, 32}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct R32G32_UINTTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::UINT, ComponentType::UINT}; + static constexpr std::array component_sizes = {32, 32}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct R16G16B16X16_FLOATTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT}; + static constexpr std::array component_sizes = {16, 16, 16, 16}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::None}; +}; + +struct A8R8G8B8_UNORMTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {8, 8, 8, 8}; + static constexpr std::array component_swizzle = { + Swizzle::A, Swizzle::R, Swizzle::G, Swizzle::B}; +}; + +struct A8R8G8B8_SRGBTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB}; + static constexpr std::array component_sizes = {8, 8, 8, 8}; + static constexpr std::array component_swizzle = { + Swizzle::A, Swizzle::R, Swizzle::G, Swizzle::B}; +}; + +struct A2B10G10R10_UNORMTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {2, 10, 10, 10}; + static constexpr std::array component_swizzle = { + Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R}; +}; + +struct A2B10G10R10_UINTTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UINT, ComponentType::UINT, ComponentType::UINT, ComponentType::UINT}; + static constexpr std::array component_sizes = {2, 10, 10, 10}; + static constexpr std::array component_swizzle = { + Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R}; +}; + +struct A2R10G10B10_UNORMTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {2, 10, 10, 10}; + static constexpr std::array component_swizzle = { + Swizzle::A, Swizzle::R, Swizzle::G, Swizzle::B}; +}; + +struct A8B8G8R8_UNORMTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {8, 8, 8, 8}; + static constexpr std::array component_swizzle = { + Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R}; +}; + +struct A8B8G8R8_SRGBTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB}; + static constexpr std::array component_sizes = {8, 8, 8, 8}; + static constexpr std::array component_swizzle = { + Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R}; +}; + +struct A8B8G8R8_SNORMTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::SNORM, ComponentType::SNORM, ComponentType::SNORM, ComponentType::SNORM}; + static constexpr std::array component_sizes = {8, 8, 8, 8}; + static constexpr std::array component_swizzle = { + Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R}; +}; + +struct A8B8G8R8_SINTTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::SINT, ComponentType::SINT, ComponentType::SINT, ComponentType::SINT}; + static constexpr std::array component_sizes = {8, 8, 8, 8}; + static constexpr std::array component_swizzle = { + Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R}; +}; + +struct A8B8G8R8_UINTTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UINT, ComponentType::UINT, ComponentType::UINT, ComponentType::UINT}; + static constexpr std::array component_sizes = {8, 8, 8, 8}; + static constexpr std::array component_swizzle = { + Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R}; +}; + +struct R16G16_UNORMTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {16, 16}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct R16G16_SNORMTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::SNORM, ComponentType::SNORM}; + static constexpr std::array component_sizes = {16, 16}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct R16G16_SINTTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::SINT, ComponentType::SINT}; + static constexpr std::array component_sizes = {16, 16}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct R16G16_UINTTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::UINT, ComponentType::UINT}; + static constexpr std::array component_sizes = {16, 16}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct R16G16_FLOATTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::FLOAT, ComponentType::FLOAT}; + static constexpr std::array component_sizes = {16, 16}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct B10G11R11_FLOATTraits { + static constexpr size_t num_components = 3; + static constexpr std::array component_types = { + ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT}; + static constexpr std::array component_sizes = {10, 11, 11}; + static constexpr std::array component_swizzle = { + Swizzle::B, Swizzle::G, Swizzle::R}; +}; + +struct R32_SINTTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::SINT}; + static constexpr std::array component_sizes = {32}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct R32_UINTTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::UINT}; + static constexpr std::array component_sizes = {32}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct R32_FLOATTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::FLOAT}; + static constexpr std::array component_sizes = {32}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct X8R8G8B8_UNORMTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {8, 8, 8, 8}; + static constexpr std::array component_swizzle = { + Swizzle::None, Swizzle::R, Swizzle::G, Swizzle::B}; +}; + +struct X8R8G8B8_SRGBTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB}; + static constexpr std::array component_sizes = {8, 8, 8, 8}; + static constexpr std::array component_swizzle = { + Swizzle::None, Swizzle::R, Swizzle::G, Swizzle::B}; +}; + +struct R5G6B5_UNORMTraits { + static constexpr size_t num_components = 3; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {5, 6, 5}; + static constexpr std::array component_swizzle = { + Swizzle::R, Swizzle::G, Swizzle::B}; +}; + +struct A1R5G5B5_UNORMTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {1, 5, 5, 5}; + static constexpr std::array component_swizzle = { + Swizzle::A, Swizzle::R, Swizzle::G, Swizzle::B}; +}; + +struct R8G8_UNORMTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {8, 8}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct R8G8_SNORMTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::SNORM, ComponentType::SNORM}; + static constexpr std::array component_sizes = {8, 8}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct R8G8_SINTTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::SINT, ComponentType::SINT}; + static constexpr std::array component_sizes = {8, 8}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct R8G8_UINTTraits { + static constexpr size_t num_components = 2; + static constexpr std::array component_types = { + ComponentType::UINT, ComponentType::UINT}; + static constexpr std::array component_sizes = {8, 8}; + static constexpr std::array component_swizzle = {Swizzle::R, + Swizzle::G}; +}; + +struct R16_UNORMTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::UNORM}; + static constexpr std::array component_sizes = {16}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct R16_SNORMTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::SNORM}; + static constexpr std::array component_sizes = {16}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct R16_SINTTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::SINT}; + static constexpr std::array component_sizes = {16}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct R16_UINTTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::UINT}; + static constexpr std::array component_sizes = {16}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct R16_FLOATTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::FLOAT}; + static constexpr std::array component_sizes = {16}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct R8_UNORMTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::UNORM}; + static constexpr std::array component_sizes = {8}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct R8_SNORMTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::SNORM}; + static constexpr std::array component_sizes = {8}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct R8_SINTTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::SINT}; + static constexpr std::array component_sizes = {8}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct R8_UINTTraits { + static constexpr size_t num_components = 1; + static constexpr std::array component_types = { + ComponentType::UINT}; + static constexpr std::array component_sizes = {8}; + static constexpr std::array component_swizzle = {Swizzle::R}; +}; + +struct X1R5G5B5_UNORMTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {1, 5, 5, 5}; + static constexpr std::array component_swizzle = { + Swizzle::None, Swizzle::R, Swizzle::G, Swizzle::B}; +}; + +struct X8B8G8R8_UNORMTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM}; + static constexpr std::array component_sizes = {8, 8, 8, 8}; + static constexpr std::array component_swizzle = { + Swizzle::None, Swizzle::B, Swizzle::G, Swizzle::R}; +}; + +struct X8B8G8R8_SRGBTraits { + static constexpr size_t num_components = 4; + static constexpr std::array component_types = { + ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB}; + static constexpr std::array component_sizes = {8, 8, 8, 8}; + static constexpr std::array component_swizzle = { + Swizzle::None, Swizzle::B, Swizzle::G, Swizzle::R}; +}; + +template +class ConverterImpl : public Converter { +private: + static constexpr size_t num_components = ConverterTraits::num_components; + static constexpr std::array component_types = + ConverterTraits::component_types; + static constexpr std::array component_sizes = + ConverterTraits::component_sizes; + static constexpr std::array component_swizzle = + ConverterTraits::component_swizzle; + + static constexpr size_t CalculateByteSize() { + size_t size = 0; + for (const size_t component_size : component_sizes) { + size += component_size; + } + const size_t power = (sizeof(size_t) * 8) - std::countl_zero(size) - 1ULL; + const size_t base_size = 1ULL << power; + const size_t mask = base_size - 1ULL; + return ((size & mask) != 0 ? base_size << 1ULL : base_size) / 8; + } + + static constexpr size_t total_bytes_per_pixel = CalculateByteSize(); + static constexpr size_t total_words_per_pixel = + (total_bytes_per_pixel + sizeof(u32) - 1U) / sizeof(u32); + static constexpr size_t components_per_ir_rep = 4; + + template + static constexpr std::array GetBoundWordsOffsets() { + std::array result; + result.fill(0); + constexpr size_t total_bits_per_word = sizeof(u32) * 8; + size_t accumulated_size = 0; + size_t count = 0; + for (size_t i = 0; i < num_components; i++) { + if constexpr (get_offsets) { + result[i] = accumulated_size; + } else { + result[i] = count; + } + accumulated_size += component_sizes[i]; + if (accumulated_size > total_bits_per_word) { + if constexpr (get_offsets) { + result[i] = 0; + } else { + result[i]++; + } + count++; + accumulated_size = component_sizes[i]; + } + } + return result; + } + + static constexpr std::array bound_words = GetBoundWordsOffsets(); + static constexpr std::array bound_offsets = + GetBoundWordsOffsets(); + + static constexpr std::array GetComponentsMask() { + std::array result; + for (size_t i = 0; i < num_components; i++) { + result[i] = (((u32)~0) >> (8 * sizeof(u32) - component_sizes[i])) << bound_offsets[i]; + } + return result; + } + + static constexpr std::array component_mask = GetComponentsMask(); + + // We are forcing inline so the compiler can SIMD the conversations, since it may do 4 function + // calls, it may fail to detect the benefit of inlining. + template + FORCE_INLINE void ConvertToComponent(u32 which_word, f32& out_component) { + const u32 value = (which_word >> bound_offsets[which_component]) & + static_cast((1ULL << component_sizes[which_component]) - 1ULL); + const auto sign_extend = [](u32 base_value, size_t bits) { + const size_t shift_amount = sizeof(u32) * 8 - bits; + s32 shifted_value = static_cast(base_value << shift_amount); + return shifted_value >> shift_amount; + }; + const auto force_to_fp16 = [](f32 base_value) { + u32 tmp = Common::BitCast(base_value); + constexpr size_t fp32_mantissa_bits = 23; + constexpr size_t fp16_mantissa_bits = 10; + constexpr size_t mantissa_mask = + ~((1ULL << (fp32_mantissa_bits - fp16_mantissa_bits)) - 1ULL); + tmp = tmp & static_cast(mantissa_mask); + // TODO: force the exponent within the range of half float. Not needed in UNORM / SNORM + return Common::BitCast(tmp); + }; + const auto from_fp_n = [&sign_extend](u32 base_value, size_t bits, size_t mantissa) { + constexpr size_t fp32_mantissa_bits = 23; + size_t shift_towards = fp32_mantissa_bits - mantissa; + const u32 new_value = + static_cast(sign_extend(base_value, bits) << shift_towards) & (~(1U << 31)); + return Common::BitCast(new_value); + }; + const auto calculate_snorm = [&]() { + return static_cast( + static_cast(sign_extend(value, component_sizes[which_component])) / + static_cast((1ULL << (component_sizes[which_component] - 1ULL)) - 1ULL)); + }; + const auto calculate_unorm = [&]() { + return static_cast( + static_cast(value) / + static_cast((1ULL << (component_sizes[which_component])) - 1ULL)); + }; + if constexpr (component_types[which_component] == ComponentType::SNORM) { + out_component = calculate_snorm(); + } else if constexpr (component_types[which_component] == ComponentType::UNORM) { + out_component = calculate_unorm(); + } else if constexpr (component_types[which_component] == ComponentType::SINT) { + out_component = static_cast( + static_cast(sign_extend(value, component_sizes[which_component]))); + } else if constexpr (component_types[which_component] == ComponentType::UINT) { + out_component = static_cast( + static_cast(sign_extend(value, component_sizes[which_component]))); + } else if constexpr (component_types[which_component] == ComponentType::SNORM_FORCE_FP16) { + out_component = calculate_snorm(); + out_component = force_to_fp16(out_component); + } else if constexpr (component_types[which_component] == ComponentType::UNORM_FORCE_FP16) { + out_component = calculate_unorm(); + out_component = force_to_fp16(out_component); + } else if constexpr (component_types[which_component] == ComponentType::FLOAT) { + if constexpr (component_sizes[which_component] == 32) { + out_component = Common::BitCast(value); + } else if constexpr (component_sizes[which_component] == 16) { + static constexpr u32 sign_mask = 0x8000; + static constexpr u32 mantissa_mask = 0x8000; + out_component = Common::BitCast(((value & sign_mask) << 16) | + (((value & 0x7c00) + 0x1C000) << 13) | + ((value & mantissa_mask) << 13)); + } else { + out_component = from_fp_n(value, component_sizes[which_component], + component_sizes[which_component] - 5); + } + } else if constexpr (component_types[which_component] == ComponentType::SRGB) { + if constexpr (component_swizzle[which_component] == Swizzle::A) { + out_component = calculate_unorm(); + } else if constexpr (component_sizes[which_component] == 8) { + out_component = SRGB_TO_RGB_LUT[value]; + } else { + out_component = calculate_unorm(); + UNIMPLEMENTED_MSG("SRGB Conversion with component sizes of {} is unimplemented", + component_sizes[which_component]); + } + } + } + + // We are forcing inline so the compiler can SIMD the conversations, since it may do 4 function + // calls, it may fail to detect the benefit of inlining. + template + FORCE_INLINE void ConvertFromComponent(u32& which_word, f32 in_component) { + const auto insert_to_word = [&](T new_word) { + which_word |= (static_cast(new_word) << bound_offsets[which_component]) & + component_mask[which_component]; + }; + const auto to_fp_n = [](f32 base_value, size_t bits, size_t mantissa) { + constexpr size_t fp32_mantissa_bits = 23; + u32 tmp_value = Common::BitCast(std::max(base_value, 0.0f)); + size_t shift_towards = fp32_mantissa_bits - mantissa; + return tmp_value >> shift_towards; + }; + const auto calculate_unorm = [&]() { + return static_cast( + static_cast(in_component) * + static_cast((1ULL << (component_sizes[which_component])) - 1ULL)); + }; + if constexpr (component_types[which_component] == ComponentType::SNORM || + component_types[which_component] == ComponentType::SNORM_FORCE_FP16) { + s32 tmp_word = static_cast( + static_cast(in_component) * + static_cast((1ULL << (component_sizes[which_component] - 1ULL)) - 1ULL)); + insert_to_word(tmp_word); + + } else if constexpr (component_types[which_component] == ComponentType::UNORM || + component_types[which_component] == ComponentType::UNORM_FORCE_FP16) { + u32 tmp_word = calculate_unorm(); + insert_to_word(tmp_word); + } else if constexpr (component_types[which_component] == ComponentType::SINT) { + s32 tmp_word = static_cast(in_component); + insert_to_word(tmp_word); + } else if constexpr (component_types[which_component] == ComponentType::UINT) { + u32 tmp_word = static_cast(in_component); + insert_to_word(tmp_word); + } else if constexpr (component_types[which_component] == ComponentType::FLOAT) { + if constexpr (component_sizes[which_component] == 32) { + u32 tmp_word = Common::BitCast(in_component); + insert_to_word(tmp_word); + } else if constexpr (component_sizes[which_component] == 16) { + static constexpr u32 sign_mask = 0x8000; + static constexpr u32 mantissa_mask = 0x03ff; + static constexpr u32 exponent_mask = 0x7c00; + const u32 tmp_word = Common::BitCast(in_component); + const u32 half = ((tmp_word >> 16) & sign_mask) | + ((((tmp_word & 0x7f800000) - 0x38000000) >> 13) & exponent_mask) | + ((tmp_word >> 13) & mantissa_mask); + insert_to_word(half); + } else { + insert_to_word(to_fp_n(in_component, component_sizes[which_component], + component_sizes[which_component] - 5)); + } + } else if constexpr (component_types[which_component] == ComponentType::SRGB) { + if constexpr (component_swizzle[which_component] != Swizzle::A) { + if constexpr (component_sizes[which_component] == 8) { + const u32 index = calculate_unorm(); + in_component = RGB_TO_SRGB_LUT[index]; + } else { + UNIMPLEMENTED_MSG("SRGB Conversion with component sizes of {} is unimplemented", + component_sizes[which_component]); + } + } + const u32 tmp_word = calculate_unorm(); + insert_to_word(tmp_word); + } + } + +public: + void ConvertTo(std::span input, std::span output) override { + const size_t num_pixels = output.size() / components_per_ir_rep; + for (size_t pixel = 0; pixel < num_pixels; pixel++) { + std::array words{}; + + std::memcpy(words.data(), &input[pixel * total_bytes_per_pixel], total_bytes_per_pixel); + std::span new_components(&output[pixel * components_per_ir_rep], + components_per_ir_rep); + if constexpr (component_swizzle[0] != Swizzle::None) { + ConvertToComponent<0>(words[bound_words[0]], + new_components[static_cast(component_swizzle[0])]); + } else { + new_components[0] = 0.0f; + } + if constexpr (num_components >= 2) { + if constexpr (component_swizzle[1] != Swizzle::None) { + ConvertToComponent<1>( + words[bound_words[1]], + new_components[static_cast(component_swizzle[1])]); + } else { + new_components[1] = 0.0f; + } + } else { + new_components[1] = 0.0f; + } + if constexpr (num_components >= 3) { + if constexpr (component_swizzle[2] != Swizzle::None) { + ConvertToComponent<2>( + words[bound_words[2]], + new_components[static_cast(component_swizzle[2])]); + } else { + new_components[2] = 0.0f; + } + } else { + new_components[2] = 0.0f; + } + if constexpr (num_components >= 4) { + if constexpr (component_swizzle[3] != Swizzle::None) { + ConvertToComponent<3>( + words[bound_words[3]], + new_components[static_cast(component_swizzle[3])]); + } else { + new_components[3] = 0.0f; + } + } else { + new_components[3] = 0.0f; + } + } + } + + void ConvertFrom(std::span input, std::span output) override { + const size_t num_pixels = output.size() / total_bytes_per_pixel; + for (size_t pixel = 0; pixel < num_pixels; pixel++) { + std::span old_components(&input[pixel * components_per_ir_rep], + components_per_ir_rep); + std::array words{}; + if constexpr (component_swizzle[0] != Swizzle::None) { + ConvertFromComponent<0>(words[bound_words[0]], + old_components[static_cast(component_swizzle[0])]); + } + if constexpr (num_components >= 2) { + if constexpr (component_swizzle[1] != Swizzle::None) { + ConvertFromComponent<1>( + words[bound_words[1]], + old_components[static_cast(component_swizzle[1])]); + } + } + if constexpr (num_components >= 3) { + if constexpr (component_swizzle[2] != Swizzle::None) { + ConvertFromComponent<2>( + words[bound_words[2]], + old_components[static_cast(component_swizzle[2])]); + } + } + if constexpr (num_components >= 4) { + if constexpr (component_swizzle[3] != Swizzle::None) { + ConvertFromComponent<3>( + words[bound_words[3]], + old_components[static_cast(component_swizzle[3])]); + } + } + std::memcpy(&output[pixel * total_bytes_per_pixel], words.data(), + total_bytes_per_pixel); + } + } + + ConverterImpl() = default; + ~ConverterImpl() override = default; +}; + +struct ConverterFactory::ConverterFactoryImpl { + std::unordered_map> converters_cache; +}; + +ConverterFactory::ConverterFactory() { + impl = std::make_unique(); +} + +ConverterFactory::~ConverterFactory() = default; + +Converter* ConverterFactory::GetFormatConverter(RenderTargetFormat format) { + auto it = impl->converters_cache.find(format); + if (it == impl->converters_cache.end()) [[unlikely]] { + return BuildConverter(format); + } + return it->second.get(); +} + +class NullConverter : public Converter { +public: + void ConvertTo([[maybe_unused]] std::span input, std::span output) override { + std::fill(output.begin(), output.end(), 0.0f); + } + void ConvertFrom([[maybe_unused]] std::span input, std::span output) override { + const u8 fill_value = 0U; + std::fill(output.begin(), output.end(), fill_value); + } + NullConverter() = default; + ~NullConverter() = default; +}; + +Converter* ConverterFactory::BuildConverter(RenderTargetFormat format) { + switch (format) { + case RenderTargetFormat::R32G32B32A32_FLOAT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R32G32B32A32_SINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R32G32B32A32_UINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R32G32B32X32_FLOAT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R32G32B32X32_SINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R32G32B32X32_UINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16G16B16A16_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16G16B16A16_SNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16G16B16A16_SINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16G16B16A16_UINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16G16B16A16_FLOAT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R32G32_FLOAT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R32G32_SINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R32G32_UINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16G16B16X16_FLOAT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::A8R8G8B8_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::A8R8G8B8_SRGB: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::A2B10G10R10_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::A2B10G10R10_UINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::A2R10G10B10_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::A8B8G8R8_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::A8B8G8R8_SRGB: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::A8B8G8R8_SNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::A8B8G8R8_SINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::A8B8G8R8_UINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16G16_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16G16_SNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16G16_SINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16G16_UINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16G16_FLOAT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::B10G11R11_FLOAT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R32_SINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R32_UINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R32_FLOAT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::X8R8G8B8_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::X8R8G8B8_SRGB: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R5G6B5_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::A1R5G5B5_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R8G8_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R8G8_SNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R8G8_SINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R8G8_UINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16_SNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16_SINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16_UINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R16_FLOAT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R8_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R8_SNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R8_SINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::R8_UINT: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::X1R5G5B5_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::X8B8G8R8_UNORM: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + case RenderTargetFormat::X8B8G8R8_SRGB: + return impl->converters_cache + .emplace(format, std::make_unique>()) + .first->second.get(); + break; + default: { + UNIMPLEMENTED_MSG("This format {} converter is not implemented", format); + return impl->converters_cache.emplace(format, std::make_unique()) + .first->second.get(); + } + } +} + +} // namespace Tegra::Engines::Blitter diff --git a/src/video_core/engines/sw_blitter/converter.h b/src/video_core/engines/sw_blitter/converter.h new file mode 100644 index 0000000..f9bdc51 --- /dev/null +++ b/src/video_core/engines/sw_blitter/converter.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" + +#include "video_core/gpu.h" + +namespace Tegra::Engines::Blitter { + +class Converter { +public: + virtual void ConvertTo(std::span input, std::span output) = 0; + virtual void ConvertFrom(std::span input, std::span output) = 0; + virtual ~Converter() = default; +}; + +class ConverterFactory { +public: + ConverterFactory(); + ~ConverterFactory(); + + Converter* GetFormatConverter(RenderTargetFormat format); + +private: + Converter* BuildConverter(RenderTargetFormat format); + + struct ConverterFactoryImpl; + std::unique_ptr impl; +}; + +} // namespace Tegra::Engines::Blitter diff --git a/src/video_core/engines/sw_blitter/generate_converters.py b/src/video_core/engines/sw_blitter/generate_converters.py new file mode 100644 index 0000000..f641564 --- /dev/null +++ b/src/video_core/engines/sw_blitter/generate_converters.py @@ -0,0 +1,136 @@ +# SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +import re + +class Format: + def __init__(self, string_value): + self.name = string_value + tmp = string_value.split('_') + self.component_type = tmp[1] + component_data = re.findall(r"\w\d+", tmp[0]) + self.num_components = len(component_data) + sizes = [] + swizzle = [] + for data in component_data: + swizzle.append(data[0]) + sizes.append(int(data[1:])) + self.sizes = sizes + self.swizzle = swizzle + + def build_component_type_array(self): + result = "{ " + b = False + for i in range(0, self.num_components): + if b: + result += ", " + b = True + result += "ComponentType::" + self.component_type + result += " }" + return result + + def build_component_sizes_array(self): + result = "{ " + b = False + for i in range(0, self.num_components): + if b: + result += ", " + b = True + result += str(self.sizes[i]) + result += " }" + return result + + def build_component_swizzle_array(self): + result = "{ " + b = False + for i in range(0, self.num_components): + if b: + result += ", " + b = True + swizzle = self.swizzle[i] + if swizzle == "X": + swizzle = "None" + result += "Swizzle::" + swizzle + result += " }" + return result + + def print_declaration(self): + print("struct " + self.name + "Traits {") + print(" static constexpr size_t num_components = " + str(self.num_components) + ";") + print(" static constexpr std::array component_types = " + self.build_component_type_array() + ";") + print(" static constexpr std::array component_sizes = " + self.build_component_sizes_array() + ";") + print(" static constexpr std::array component_swizzle = " + self.build_component_swizzle_array() + ";") + print("};\n") + + def print_case(self): + print("case RenderTargetFormat::" + self.name + ":") + print(" return impl->converters_cache") + print(" .emplace(format, std::make_unique>())") + print(" .first->second.get();") + print(" break;") + +txt = """ +R32G32B32A32_FLOAT +R32G32B32A32_SINT +R32G32B32A32_UINT +R32G32B32X32_FLOAT +R32G32B32X32_SINT +R32G32B32X32_UINT +R16G16B16A16_UNORM +R16G16B16A16_SNORM +R16G16B16A16_SINT +R16G16B16A16_UINT +R16G16B16A16_FLOAT +R32G32_FLOAT +R32G32_SINT +R32G32_UINT +R16G16B16X16_FLOAT +A8R8G8B8_UNORM +A8R8G8B8_SRGB +A2B10G10R10_UNORM +A2B10G10R10_UINT +A2R10G10B10_UNORM +A8B8G8R8_UNORM +A8B8G8R8_SRGB +A8B8G8R8_SNORM +A8B8G8R8_SINT +A8B8G8R8_UINT +R16G16_UNORM +R16G16_SNORM +R16G16_SINT +R16G16_UINT +R16G16_FLOAT +B10G11R11_FLOAT +R32_SINT +R32_UINT +R32_FLOAT +X8R8G8B8_UNORM +X8R8G8B8_SRGB +R5G6B5_UNORM +A1R5G5B5_UNORM +R8G8_UNORM +R8G8_SNORM +R8G8_SINT +R8G8_UINT +R16_UNORM +R16_SNORM +R16_SINT +R16_UINT +R16_FLOAT +R8_UNORM +R8_SNORM +R8_SINT +R8_UINT +X1R5G5B5_UNORM +X8B8G8R8_UNORM +X8B8G8R8_SRGB +""" + +x = txt.split() +y = list(map(lambda a: Format(a), x)) +formats = list(y) +for format in formats: + format.print_declaration() + +for format in formats: + format.print_case() diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index 1e9832d..c390ac9 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -4,40 +4,24 @@ #pragma once #include +#include +#include +#include +#include #include #include "common/common_types.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/gpu.h" -#include "video_core/memory_manager.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/syncpoint_manager.h" #include "video_core/rasterizer_interface.h" namespace VideoCommon { class FenceBase { public: - explicit FenceBase(u32 payload_, bool is_stubbed_) - : address{}, payload{payload_}, is_semaphore{false}, is_stubbed{is_stubbed_} {} - - explicit FenceBase(GPUVAddr address_, u32 payload_, bool is_stubbed_) - : address{address_}, payload{payload_}, is_semaphore{true}, is_stubbed{is_stubbed_} {} - - GPUVAddr GetAddress() const { - return address; - } - - u32 GetPayload() const { - return payload; - } - - bool IsSemaphore() const { - return is_semaphore; - } - -private: - GPUVAddr address; - u32 payload; - bool is_semaphore; + explicit FenceBase(bool is_stubbed_) : is_stubbed{is_stubbed_} {} protected: bool is_stubbed; @@ -57,30 +41,28 @@ public: buffer_cache.AccumulateFlushes(); } - void SignalSemaphore(GPUVAddr addr, u32 value) { + void SyncOperation(std::function&& func) { + uncommitted_operations.emplace_back(std::move(func)); + } + + void SignalFence(std::function&& func) { TryReleasePendingFences(); const bool should_flush = ShouldFlush(); CommitAsyncFlushes(); - TFence new_fence = CreateFence(addr, value, !should_flush); + uncommitted_operations.emplace_back(std::move(func)); + CommitOperations(); + TFence new_fence = CreateFence(!should_flush); fences.push(new_fence); QueueFence(new_fence); if (should_flush) { rasterizer.FlushCommands(); } - rasterizer.SyncGuestHost(); } void SignalSyncPoint(u32 value) { - TryReleasePendingFences(); - const bool should_flush = ShouldFlush(); - CommitAsyncFlushes(); - TFence new_fence = CreateFence(value, !should_flush); - fences.push(new_fence); - QueueFence(new_fence); - if (should_flush) { - rasterizer.FlushCommands(); - } - rasterizer.SyncGuestHost(); + syncpoint_manager.IncrementGuest(value); + std::function func([this, value] { syncpoint_manager.IncrementHost(value); }); + SignalFence(std::move(func)); } void WaitPendingFences() { @@ -90,11 +72,10 @@ public: WaitFence(current_fence); } PopAsyncFlushes(); - if (current_fence->IsSemaphore()) { - gpu_memory.template Write(current_fence->GetAddress(), - current_fence->GetPayload()); - } else { - gpu.IncrementSyncPoint(current_fence->GetPayload()); + auto operations = std::move(pending_operations.front()); + pending_operations.pop_front(); + for (auto& operation : operations) { + operation(); } PopFence(); } @@ -104,16 +85,14 @@ protected: explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, TTextureCache& texture_cache_, TTBufferCache& buffer_cache_, TQueryCache& query_cache_) - : rasterizer{rasterizer_}, gpu{gpu_}, gpu_memory{gpu.MemoryManager()}, + : rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {} virtual ~FenceManager() = default; - /// Creates a Sync Point Fence Interface, does not create a backend fence if 'is_stubbed' is + /// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is /// true - virtual TFence CreateFence(u32 value, bool is_stubbed) = 0; - /// Creates a Semaphore Fence Interface, does not create a backend fence if 'is_stubbed' is true - virtual TFence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) = 0; + virtual TFence CreateFence(bool is_stubbed) = 0; /// Queues a fence into the backend if the fence isn't stubbed. virtual void QueueFence(TFence& fence) = 0; /// Notifies that the backend fence has been signaled/reached in host GPU. @@ -123,7 +102,7 @@ protected: VideoCore::RasterizerInterface& rasterizer; Tegra::GPU& gpu; - Tegra::MemoryManager& gpu_memory; + Tegra::Host1x::SyncpointManager& syncpoint_manager; TTextureCache& texture_cache; TTBufferCache& buffer_cache; TQueryCache& query_cache; @@ -136,11 +115,10 @@ private: return; } PopAsyncFlushes(); - if (current_fence->IsSemaphore()) { - gpu_memory.template Write(current_fence->GetAddress(), - current_fence->GetPayload()); - } else { - gpu.IncrementSyncPoint(current_fence->GetPayload()); + auto operations = std::move(pending_operations.front()); + pending_operations.pop_front(); + for (auto& operation : operations) { + operation(); } PopFence(); } @@ -159,16 +137,20 @@ private: } void PopAsyncFlushes() { - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; - texture_cache.PopAsyncFlushes(); - buffer_cache.PopAsyncFlushes(); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.PopAsyncFlushes(); + buffer_cache.PopAsyncFlushes(); + } query_cache.PopAsyncFlushes(); } void CommitAsyncFlushes() { - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; - texture_cache.CommitAsyncFlushes(); - buffer_cache.CommitAsyncFlushes(); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.CommitAsyncFlushes(); + buffer_cache.CommitAsyncFlushes(); + } query_cache.CommitAsyncFlushes(); } @@ -177,7 +159,13 @@ private: fences.pop(); } + void CommitOperations() { + pending_operations.emplace_back(std::move(uncommitted_operations)); + } + std::queue fences; + std::deque> uncommitted_operations; + std::deque>> pending_operations; DelayedDestructionRing delayed_destruction_ring; }; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 33431f2..c6d54be 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -14,10 +14,11 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" -#include "core/hardware_interrupt_manager.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/perf_stats.h" #include "video_core/cdma_pusher.h" +#include "video_core/control/channel_state.h" +#include "video_core/control/scheduler.h" #include "video_core/dma_pusher.h" #include "video_core/engines/fermi_2d.h" #include "video_core/engines/kepler_compute.h" @@ -26,75 +27,64 @@ #include "video_core/engines/maxwell_dma.h" #include "video_core/gpu.h" #include "video_core/gpu_thread.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/syncpoint_manager.h" #include "video_core/memory_manager.h" #include "video_core/renderer_base.h" #include "video_core/shader_notify.h" namespace Tegra { -MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); - struct GPU::Impl { explicit Impl(GPU& gpu_, Core::System& system_, bool is_async_, bool use_nvdec_) - : gpu{gpu_}, system{system_}, memory_manager{std::make_unique( - system)}, - dma_pusher{std::make_unique(system, gpu)}, use_nvdec{use_nvdec_}, - maxwell_3d{std::make_unique(system, *memory_manager)}, - fermi_2d{std::make_unique()}, - kepler_compute{std::make_unique(system, *memory_manager)}, - maxwell_dma{std::make_unique(system, *memory_manager)}, - kepler_memory{std::make_unique(system, *memory_manager)}, + : gpu{gpu_}, system{system_}, host1x{system.Host1x()}, use_nvdec{use_nvdec_}, shader_notify{std::make_unique()}, is_async{is_async_}, - gpu_thread{system_, is_async_} {} + gpu_thread{system_, is_async_}, scheduler{std::make_unique(gpu)} {} ~Impl() = default; + std::shared_ptr CreateChannel(s32 channel_id) { + auto channel_state = std::make_shared(channel_id); + channels.emplace(channel_id, channel_state); + scheduler->DeclareChannel(channel_state); + return channel_state; + } + + void BindChannel(s32 channel_id) { + if (bound_channel == channel_id) { + return; + } + auto it = channels.find(channel_id); + ASSERT(it != channels.end()); + bound_channel = channel_id; + current_channel = it->second.get(); + + rasterizer->BindChannel(*current_channel); + } + + std::shared_ptr AllocateChannel() { + return CreateChannel(new_channel_id++); + } + + void InitChannel(Control::ChannelState& to_init) { + to_init.Init(system, gpu); + to_init.BindRasterizer(rasterizer); + rasterizer->InitializeChannel(to_init); + } + + void InitAddressSpace(Tegra::MemoryManager& memory_manager) { + memory_manager.BindRasterizer(rasterizer); + } + + void ReleaseChannel(Control::ChannelState& to_release) { + UNIMPLEMENTED(); + } + /// Binds a renderer to the GPU. void BindRenderer(std::unique_ptr renderer_) { renderer = std::move(renderer_); rasterizer = renderer->ReadRasterizer(); - - memory_manager->BindRasterizer(rasterizer); - maxwell_3d->BindRasterizer(rasterizer); - fermi_2d->BindRasterizer(rasterizer); - kepler_compute->BindRasterizer(rasterizer); - kepler_memory->BindRasterizer(rasterizer); - maxwell_dma->BindRasterizer(rasterizer); - } - - /// Calls a GPU method. - void CallMethod(const GPU::MethodCall& method_call) { - LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, - method_call.subchannel); - - ASSERT(method_call.subchannel < bound_engines.size()); - - if (ExecuteMethodOnEngine(method_call.method)) { - CallEngineMethod(method_call); - } else { - CallPullerMethod(method_call); - } - } - - /// Calls a GPU multivalue method. - void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending) { - LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel); - - ASSERT(subchannel < bound_engines.size()); - - if (ExecuteMethodOnEngine(method)) { - CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending); - } else { - for (std::size_t i = 0; i < amount; i++) { - CallPullerMethod(GPU::MethodCall{ - method, - base_start[i], - subchannel, - methods_pending - static_cast(i), - }); - } - } + host1x.MemoryManager().BindRasterizer(rasterizer); } /// Flush all current written commands into the host GPU for execution. @@ -103,85 +93,82 @@ struct GPU::Impl { } /// Synchronizes CPU writes with Host GPU memory. - void SyncGuestHost() { - rasterizer->SyncGuestHost(); + void InvalidateGPUCache() { + rasterizer->InvalidateGPUCache(); } /// Signal the ending of command list. void OnCommandListEnd() { - if (is_async) { - // This command only applies to asynchronous GPU mode - gpu_thread.OnCommandListEnd(); - } + gpu_thread.OnCommandListEnd(); } /// Request a host GPU memory flush from the CPU. - [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size) { - std::unique_lock lck{flush_request_mutex}; - const u64 fence = ++last_flush_fence; - flush_requests.emplace_back(fence, addr, size); + template + [[nodiscard]] u64 RequestSyncOperation(Func&& action) { + std::unique_lock lck{sync_request_mutex}; + const u64 fence = ++last_sync_fence; + sync_requests.emplace_back(action); return fence; } /// Obtains current flush request fence id. - [[nodiscard]] u64 CurrentFlushRequestFence() const { - return current_flush_fence.load(std::memory_order_relaxed); + [[nodiscard]] u64 CurrentSyncRequestFence() const { + return current_sync_fence.load(std::memory_order_relaxed); + } + + void WaitForSyncOperation(const u64 fence) { + std::unique_lock lck{sync_request_mutex}; + sync_request_cv.wait(lck, [this, fence] { return CurrentSyncRequestFence() >= fence; }); } /// Tick pending requests within the GPU. void TickWork() { - std::unique_lock lck{flush_request_mutex}; - while (!flush_requests.empty()) { - auto& request = flush_requests.front(); - const u64 fence = request.fence; - const VAddr addr = request.addr; - const std::size_t size = request.size; - flush_requests.pop_front(); - flush_request_mutex.unlock(); - rasterizer->FlushRegion(addr, size); - current_flush_fence.store(fence); - flush_request_mutex.lock(); + std::unique_lock lck{sync_request_mutex}; + while (!sync_requests.empty()) { + auto request = std::move(sync_requests.front()); + sync_requests.pop_front(); + sync_request_mutex.unlock(); + request(); + current_sync_fence.fetch_add(1, std::memory_order_release); + sync_request_mutex.lock(); + sync_request_cv.notify_all(); } } /// Returns a reference to the Maxwell3D GPU engine. [[nodiscard]] Engines::Maxwell3D& Maxwell3D() { - return *maxwell_3d; + ASSERT(current_channel); + return *current_channel->maxwell_3d; } /// Returns a const reference to the Maxwell3D GPU engine. [[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const { - return *maxwell_3d; + ASSERT(current_channel); + return *current_channel->maxwell_3d; } /// Returns a reference to the KeplerCompute GPU engine. [[nodiscard]] Engines::KeplerCompute& KeplerCompute() { - return *kepler_compute; + ASSERT(current_channel); + return *current_channel->kepler_compute; } /// Returns a reference to the KeplerCompute GPU engine. [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const { - return *kepler_compute; - } - - /// Returns a reference to the GPU memory manager. - [[nodiscard]] Tegra::MemoryManager& MemoryManager() { - return *memory_manager; - } - - /// Returns a const reference to the GPU memory manager. - [[nodiscard]] const Tegra::MemoryManager& MemoryManager() const { - return *memory_manager; + ASSERT(current_channel); + return *current_channel->kepler_compute; } /// Returns a reference to the GPU DMA pusher. [[nodiscard]] Tegra::DmaPusher& DmaPusher() { - return *dma_pusher; + ASSERT(current_channel); + return *current_channel->dma_pusher; } /// Returns a const reference to the GPU DMA pusher. [[nodiscard]] const Tegra::DmaPusher& DmaPusher() const { - return *dma_pusher; + ASSERT(current_channel); + return *current_channel->dma_pusher; } /// Returns a reference to the underlying renderer. @@ -204,77 +191,6 @@ struct GPU::Impl { return *shader_notify; } - /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. - void WaitFence(u32 syncpoint_id, u32 value) { - // Synced GPU, is always in sync - if (!is_async) { - return; - } - if (syncpoint_id == UINT32_MAX) { - // TODO: Research what this does. - LOG_ERROR(HW_GPU, "Waiting for syncpoint -1 not implemented"); - return; - } - MICROPROFILE_SCOPE(GPU_wait); - std::unique_lock lock{sync_mutex}; - sync_cv.wait(lock, [=, this] { - if (shutting_down.load(std::memory_order_relaxed)) { - // We're shutting down, ensure no threads continue to wait for the next syncpoint - return true; - } - return syncpoints.at(syncpoint_id).load() >= value; - }); - } - - void IncrementSyncPoint(u32 syncpoint_id) { - auto& syncpoint = syncpoints.at(syncpoint_id); - syncpoint++; - std::scoped_lock lock{sync_mutex}; - sync_cv.notify_all(); - auto& interrupt = syncpt_interrupts.at(syncpoint_id); - if (!interrupt.empty()) { - u32 value = syncpoint.load(); - auto it = interrupt.begin(); - while (it != interrupt.end()) { - if (value >= *it) { - TriggerCpuInterrupt(syncpoint_id, *it); - it = interrupt.erase(it); - continue; - } - it++; - } - } - } - - [[nodiscard]] u32 GetSyncpointValue(u32 syncpoint_id) const { - return syncpoints.at(syncpoint_id).load(); - } - - void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { - std::scoped_lock lock{sync_mutex}; - auto& interrupt = syncpt_interrupts.at(syncpoint_id); - bool contains = std::any_of(interrupt.begin(), interrupt.end(), - [value](u32 in_value) { return in_value == value; }); - if (contains) { - return; - } - interrupt.emplace_back(value); - } - - [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) { - std::scoped_lock lock{sync_mutex}; - auto& interrupt = syncpt_interrupts.at(syncpoint_id); - const auto iter = - std::find_if(interrupt.begin(), interrupt.end(), - [value](u32 interrupt_value) { return value == interrupt_value; }); - - if (iter == interrupt.end()) { - return false; - } - interrupt.erase(iter); - return true; - } - [[nodiscard]] u64 GetTicks() const { // This values were reversed engineered by fincs from NVN // The gpu clock is reported in units of 385/625 nanoseconds @@ -306,9 +222,7 @@ struct GPU::Impl { /// This can be used to launch any necessary threads and register any necessary /// core timing events. void Start() { - gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher); - cpu_context = renderer->GetRenderWindow().CreateSharedContext(); - cpu_context->MakeCurrent(); + gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); } void NotifyShutdown() { @@ -319,6 +233,9 @@ struct GPU::Impl { /// Obtain the CPU Context void ObtainContext() { + if (!cpu_context) { + cpu_context = renderer->GetRenderWindow().CreateSharedContext(); + } cpu_context->MakeCurrent(); } @@ -328,8 +245,8 @@ struct GPU::Impl { } /// Push GPU command entries to be processed - void PushGPUEntries(Tegra::CommandList&& entries) { - gpu_thread.SubmitList(std::move(entries)); + void PushGPUEntries(s32 channel, Tegra::CommandList&& entries) { + gpu_thread.SubmitList(channel, std::move(entries)); } /// Push GPU command buffer entries to be processed @@ -339,7 +256,7 @@ struct GPU::Impl { } if (!cdma_pushers.contains(id)) { - cdma_pushers.insert_or_assign(id, std::make_unique(gpu)); + cdma_pushers.insert_or_assign(id, std::make_unique(host1x)); } // SubmitCommandBuffer would make the nvdec operations async, this is not currently working @@ -376,308 +293,55 @@ struct GPU::Impl { gpu_thread.FlushAndInvalidateRegion(addr, size); } - void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const { - auto& interrupt_manager = system.InterruptManager(); - interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); - } - - void ProcessBindMethod(const GPU::MethodCall& method_call) { - // Bind the current subchannel to the desired engine id. - LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, - method_call.argument); - const auto engine_id = static_cast(method_call.argument); - bound_engines[method_call.subchannel] = static_cast(engine_id); - switch (engine_id) { - case EngineID::FERMI_TWOD_A: - dma_pusher->BindSubchannel(fermi_2d.get(), method_call.subchannel); - break; - case EngineID::MAXWELL_B: - dma_pusher->BindSubchannel(maxwell_3d.get(), method_call.subchannel); - break; - case EngineID::KEPLER_COMPUTE_B: - dma_pusher->BindSubchannel(kepler_compute.get(), method_call.subchannel); - break; - case EngineID::MAXWELL_DMA_COPY_A: - dma_pusher->BindSubchannel(maxwell_dma.get(), method_call.subchannel); - break; - case EngineID::KEPLER_INLINE_TO_MEMORY_B: - dma_pusher->BindSubchannel(kepler_memory.get(), method_call.subchannel); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); - } - } - - void ProcessFenceActionMethod() { - switch (regs.fence_action.op) { - case GPU::FenceOperation::Acquire: - WaitFence(regs.fence_action.syncpoint_id, regs.fence_value); - break; - case GPU::FenceOperation::Increment: - IncrementSyncPoint(regs.fence_action.syncpoint_id); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); - } - } - - void ProcessWaitForInterruptMethod() { - // TODO(bunnei) ImplementMe - LOG_WARNING(HW_GPU, "(STUBBED) called"); - } - - void ProcessSemaphoreTriggerMethod() { - const auto semaphoreOperationMask = 0xF; - const auto op = - static_cast(regs.semaphore_trigger & semaphoreOperationMask); - if (op == GpuSemaphoreOperation::WriteLong) { - struct Block { - u32 sequence; - u32 zeros = 0; - u64 timestamp; - }; - - Block block{}; - block.sequence = regs.semaphore_sequence; - // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of - // CoreTiming - block.timestamp = GetTicks(); - memory_manager->WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block, - sizeof(block)); - } else { - const u32 word{memory_manager->Read(regs.semaphore_address.SemaphoreAddress())}; - if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || - (op == GpuSemaphoreOperation::AcquireGequal && - static_cast(word - regs.semaphore_sequence) > 0) || - (op == GpuSemaphoreOperation::AcquireMask && (word & regs.semaphore_sequence))) { - // Nothing to do in this case + void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, + std::array& fences, size_t num_fences) { + size_t current_request_counter{}; + { + std::unique_lock lk(request_swap_mutex); + if (free_swap_counters.empty()) { + current_request_counter = request_swap_counters.size(); + request_swap_counters.emplace_back(num_fences); } else { - regs.acquire_source = true; - regs.acquire_value = regs.semaphore_sequence; - if (op == GpuSemaphoreOperation::AcquireEqual) { - regs.acquire_active = true; - regs.acquire_mode = false; - } else if (op == GpuSemaphoreOperation::AcquireGequal) { - regs.acquire_active = true; - regs.acquire_mode = true; - } else if (op == GpuSemaphoreOperation::AcquireMask) { - // TODO(kemathe) The acquire mask operation waits for a value that, ANDed with - // semaphore_sequence, gives a non-0 result - LOG_ERROR(HW_GPU, "Invalid semaphore operation AcquireMask not implemented"); - } else { - LOG_ERROR(HW_GPU, "Invalid semaphore operation"); - } + current_request_counter = free_swap_counters.front(); + request_swap_counters[current_request_counter] = num_fences; + free_swap_counters.pop_front(); } } - } - - void ProcessSemaphoreRelease() { - memory_manager->Write(regs.semaphore_address.SemaphoreAddress(), - regs.semaphore_release); - } - - void ProcessSemaphoreAcquire() { - const u32 word = memory_manager->Read(regs.semaphore_address.SemaphoreAddress()); - const auto value = regs.semaphore_acquire; - if (word != value) { - regs.acquire_active = true; - regs.acquire_value = value; - // TODO(kemathe73) figure out how to do the acquire_timeout - regs.acquire_mode = false; - regs.acquire_source = false; - } - } - - /// Calls a GPU puller method. - void CallPullerMethod(const GPU::MethodCall& method_call) { - regs.reg_array[method_call.method] = method_call.argument; - const auto method = static_cast(method_call.method); - - switch (method) { - case BufferMethods::BindObject: { - ProcessBindMethod(method_call); - break; - } - case BufferMethods::Nop: - case BufferMethods::SemaphoreAddressHigh: - case BufferMethods::SemaphoreAddressLow: - case BufferMethods::SemaphoreSequence: - break; - case BufferMethods::UnkCacheFlush: - rasterizer->SyncGuestHost(); - break; - case BufferMethods::WrcacheFlush: - rasterizer->SignalReference(); - break; - case BufferMethods::FenceValue: - break; - case BufferMethods::RefCnt: - rasterizer->SignalReference(); - break; - case BufferMethods::FenceAction: - ProcessFenceActionMethod(); - break; - case BufferMethods::WaitForInterrupt: - rasterizer->WaitForIdle(); - break; - case BufferMethods::SemaphoreTrigger: { - ProcessSemaphoreTriggerMethod(); - break; - } - case BufferMethods::NotifyIntr: { - // TODO(Kmather73): Research and implement this method. - LOG_ERROR(HW_GPU, "Special puller engine method NotifyIntr not implemented"); - break; - } - case BufferMethods::Unk28: { - // TODO(Kmather73): Research and implement this method. - LOG_ERROR(HW_GPU, "Special puller engine method Unk28 not implemented"); - break; - } - case BufferMethods::SemaphoreAcquire: { - ProcessSemaphoreAcquire(); - break; - } - case BufferMethods::SemaphoreRelease: { - ProcessSemaphoreRelease(); - break; - } - case BufferMethods::Yield: { - // TODO(Kmather73): Research and implement this method. - LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented"); - break; - } - default: - LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented", method); - break; - } - } - - /// Calls a GPU engine method. - void CallEngineMethod(const GPU::MethodCall& method_call) { - const EngineID engine = bound_engines[method_call.subchannel]; - - switch (engine) { - case EngineID::FERMI_TWOD_A: - fermi_2d->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - case EngineID::MAXWELL_B: - maxwell_3d->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - case EngineID::KEPLER_COMPUTE_B: - kepler_compute->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - case EngineID::MAXWELL_DMA_COPY_A: - maxwell_dma->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - case EngineID::KEPLER_INLINE_TO_MEMORY_B: - kepler_memory->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented engine"); - } - } - - /// Calls a GPU engine multivalue method. - void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending) { - const EngineID engine = bound_engines[subchannel]; - - switch (engine) { - case EngineID::FERMI_TWOD_A: - fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::MAXWELL_B: - maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::KEPLER_COMPUTE_B: - kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::MAXWELL_DMA_COPY_A: - maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::KEPLER_INLINE_TO_MEMORY_B: - kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented engine"); - } - } - - /// Determines where the method should be executed. - [[nodiscard]] bool ExecuteMethodOnEngine(u32 method) { - const auto buffer_method = static_cast(method); - return buffer_method >= BufferMethods::NonPullerMethods; - } - - struct Regs { - static constexpr size_t NUM_REGS = 0x40; - - union { - struct { - INSERT_PADDING_WORDS_NOINIT(0x4); - struct { - u32 address_high; - u32 address_low; - - [[nodiscard]] GPUVAddr SemaphoreAddress() const { - return static_cast((static_cast(address_high) << 32) | - address_low); + const auto wait_fence = + RequestSyncOperation([this, current_request_counter, framebuffer, fences, num_fences] { + auto& syncpoint_manager = host1x.GetSyncpointManager(); + if (num_fences == 0) { + renderer->SwapBuffers(framebuffer); + } + const auto executer = [this, current_request_counter, + framebuffer_copy = *framebuffer]() { + { + std::unique_lock lk(request_swap_mutex); + if (--request_swap_counters[current_request_counter] != 0) { + return; + } + free_swap_counters.push_back(current_request_counter); } - } semaphore_address; - - u32 semaphore_sequence; - u32 semaphore_trigger; - INSERT_PADDING_WORDS_NOINIT(0xC); - - // The pusher and the puller share the reference counter, the pusher only has read - // access - u32 reference_count; - INSERT_PADDING_WORDS_NOINIT(0x5); - - u32 semaphore_acquire; - u32 semaphore_release; - u32 fence_value; - GPU::FenceAction fence_action; - INSERT_PADDING_WORDS_NOINIT(0xE2); - - // Puller state - u32 acquire_mode; - u32 acquire_source; - u32 acquire_active; - u32 acquire_timeout; - u32 acquire_value; - }; - std::array reg_array; - }; - } regs{}; + renderer->SwapBuffers(&framebuffer_copy); + }; + for (size_t i = 0; i < num_fences; i++) { + syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); + } + }); + gpu_thread.TickGPU(); + WaitForSyncOperation(wait_fence); + } GPU& gpu; Core::System& system; - std::unique_ptr memory_manager; - std::unique_ptr dma_pusher; + Host1x::Host1x& host1x; + std::map> cdma_pushers; std::unique_ptr renderer; VideoCore::RasterizerInterface* rasterizer = nullptr; const bool use_nvdec; - /// Mapping of command subchannels to their bound engine ids - std::array bound_engines{}; - /// 3D engine - std::unique_ptr maxwell_3d; - /// 2D engine - std::unique_ptr fermi_2d; - /// Compute engine - std::unique_ptr kepler_compute; - /// DMA engine - std::unique_ptr maxwell_dma; - /// Inline memory engine - std::unique_ptr kepler_memory; + s32 new_channel_id{1}; /// Shader build notifier std::unique_ptr shader_notify; /// When true, we are about to shut down emulation session, so terminate outstanding tasks @@ -692,51 +356,25 @@ struct GPU::Impl { std::condition_variable sync_cv; - struct FlushRequest { - explicit FlushRequest(u64 fence_, VAddr addr_, std::size_t size_) - : fence{fence_}, addr{addr_}, size{size_} {} - u64 fence; - VAddr addr; - std::size_t size; - }; - - std::list flush_requests; - std::atomic current_flush_fence{}; - u64 last_flush_fence{}; - std::mutex flush_request_mutex; + std::list> sync_requests; + std::atomic current_sync_fence{}; + u64 last_sync_fence{}; + std::mutex sync_request_mutex; + std::condition_variable sync_request_cv; const bool is_async; VideoCommon::GPUThread::ThreadManager gpu_thread; std::unique_ptr cpu_context; -#define ASSERT_REG_POSITION(field_name, position) \ - static_assert(offsetof(Regs, field_name) == position * 4, \ - "Field " #field_name " has invalid position") + std::unique_ptr scheduler; + std::unordered_map> channels; + Tegra::Control::ChannelState* current_channel; + s32 bound_channel{-1}; - ASSERT_REG_POSITION(semaphore_address, 0x4); - ASSERT_REG_POSITION(semaphore_sequence, 0x6); - ASSERT_REG_POSITION(semaphore_trigger, 0x7); - ASSERT_REG_POSITION(reference_count, 0x14); - ASSERT_REG_POSITION(semaphore_acquire, 0x1A); - ASSERT_REG_POSITION(semaphore_release, 0x1B); - ASSERT_REG_POSITION(fence_value, 0x1C); - ASSERT_REG_POSITION(fence_action, 0x1D); - - ASSERT_REG_POSITION(acquire_mode, 0x100); - ASSERT_REG_POSITION(acquire_source, 0x101); - ASSERT_REG_POSITION(acquire_active, 0x102); - ASSERT_REG_POSITION(acquire_timeout, 0x103); - ASSERT_REG_POSITION(acquire_value, 0x104); - -#undef ASSERT_REG_POSITION - - enum class GpuSemaphoreOperation { - AcquireEqual = 0x1, - WriteLong = 0x2, - AcquireGequal = 0x4, - AcquireMask = 0x8, - }; + std::deque free_swap_counters; + std::deque request_swap_counters; + std::mutex request_swap_mutex; }; GPU::GPU(Core::System& system, bool is_async, bool use_nvdec) @@ -744,25 +382,36 @@ GPU::GPU(Core::System& system, bool is_async, bool use_nvdec) GPU::~GPU() = default; +std::shared_ptr GPU::AllocateChannel() { + return impl->AllocateChannel(); +} + +void GPU::InitChannel(Control::ChannelState& to_init) { + impl->InitChannel(to_init); +} + +void GPU::BindChannel(s32 channel_id) { + impl->BindChannel(channel_id); +} + +void GPU::ReleaseChannel(Control::ChannelState& to_release) { + impl->ReleaseChannel(to_release); +} + +void GPU::InitAddressSpace(Tegra::MemoryManager& memory_manager) { + impl->InitAddressSpace(memory_manager); +} + void GPU::BindRenderer(std::unique_ptr renderer) { impl->BindRenderer(std::move(renderer)); } -void GPU::CallMethod(const MethodCall& method_call) { - impl->CallMethod(method_call); -} - -void GPU::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending) { - impl->CallMultiMethod(method, subchannel, base_start, amount, methods_pending); -} - void GPU::FlushCommands() { impl->FlushCommands(); } -void GPU::SyncGuestHost() { - impl->SyncGuestHost(); +void GPU::InvalidateGPUCache() { + impl->InvalidateGPUCache(); } void GPU::OnCommandListEnd() { @@ -770,17 +419,32 @@ void GPU::OnCommandListEnd() { } u64 GPU::RequestFlush(VAddr addr, std::size_t size) { - return impl->RequestFlush(addr, size); + return impl->RequestSyncOperation( + [this, addr, size]() { impl->rasterizer->FlushRegion(addr, size); }); } -u64 GPU::CurrentFlushRequestFence() const { - return impl->CurrentFlushRequestFence(); +u64 GPU::CurrentSyncRequestFence() const { + return impl->CurrentSyncRequestFence(); +} + +void GPU::WaitForSyncOperation(u64 fence) { + return impl->WaitForSyncOperation(fence); } void GPU::TickWork() { impl->TickWork(); } +/// Gets a mutable reference to the Host1x interface +Host1x::Host1x& GPU::Host1x() { + return impl->host1x; +} + +/// Gets an immutable reference to the Host1x interface. +const Host1x::Host1x& GPU::Host1x() const { + return impl->host1x; +} + Engines::Maxwell3D& GPU::Maxwell3D() { return impl->Maxwell3D(); } @@ -797,14 +461,6 @@ const Engines::KeplerCompute& GPU::KeplerCompute() const { return impl->KeplerCompute(); } -Tegra::MemoryManager& GPU::MemoryManager() { - return impl->MemoryManager(); -} - -const Tegra::MemoryManager& GPU::MemoryManager() const { - return impl->MemoryManager(); -} - Tegra::DmaPusher& GPU::DmaPusher() { return impl->DmaPusher(); } @@ -829,24 +485,9 @@ const VideoCore::ShaderNotify& GPU::ShaderNotify() const { return impl->ShaderNotify(); } -void GPU::WaitFence(u32 syncpoint_id, u32 value) { - impl->WaitFence(syncpoint_id, value); -} - -void GPU::IncrementSyncPoint(u32 syncpoint_id) { - impl->IncrementSyncPoint(syncpoint_id); -} - -u32 GPU::GetSyncpointValue(u32 syncpoint_id) const { - return impl->GetSyncpointValue(syncpoint_id); -} - -void GPU::RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { - impl->RegisterSyncptInterrupt(syncpoint_id, value); -} - -bool GPU::CancelSyncptInterrupt(u32 syncpoint_id, u32 value) { - return impl->CancelSyncptInterrupt(syncpoint_id, value); +void GPU::RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, + std::array& fences, size_t num_fences) { + impl->RequestSwapBuffers(framebuffer, fences, num_fences); } u64 GPU::GetTicks() const { @@ -881,8 +522,8 @@ void GPU::ReleaseContext() { impl->ReleaseContext(); } -void GPU::PushGPUEntries(Tegra::CommandList&& entries) { - impl->PushGPUEntries(std::move(entries)); +void GPU::PushGPUEntries(s32 channel, Tegra::CommandList&& entries) { + impl->PushGPUEntries(channel, std::move(entries)); } void GPU::PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) { diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index b939ba3..8a87159 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -24,11 +24,15 @@ namespace Tegra { class DmaPusher; struct CommandList; +// TODO: Implement the commented ones enum class RenderTargetFormat : u32 { NONE = 0x0, - R32B32G32A32_FLOAT = 0xC0, + R32G32B32A32_FLOAT = 0xC0, R32G32B32A32_SINT = 0xC1, R32G32B32A32_UINT = 0xC2, + R32G32B32X32_FLOAT = 0xC3, + R32G32B32X32_SINT = 0xC4, + R32G32B32X32_UINT = 0xC5, R16G16B16A16_UNORM = 0xC6, R16G16B16A16_SNORM = 0xC7, R16G16B16A16_SINT = 0xC8, @@ -38,8 +42,8 @@ enum class RenderTargetFormat : u32 { R32G32_SINT = 0xCC, R32G32_UINT = 0xCD, R16G16B16X16_FLOAT = 0xCE, - B8G8R8A8_UNORM = 0xCF, - B8G8R8A8_SRGB = 0xD0, + A8R8G8B8_UNORM = 0xCF, + A8R8G8B8_SRGB = 0xD0, A2B10G10R10_UNORM = 0xD1, A2B10G10R10_UINT = 0xD2, A8B8G8R8_UNORM = 0xD5, @@ -52,10 +56,13 @@ enum class RenderTargetFormat : u32 { R16G16_SINT = 0xDC, R16G16_UINT = 0xDD, R16G16_FLOAT = 0xDE, + A2R10G10B10_UNORM = 0xDF, B10G11R11_FLOAT = 0xE0, R32_SINT = 0xE3, R32_UINT = 0xE4, R32_FLOAT = 0xE5, + X8R8G8B8_UNORM = 0xE6, + X8R8G8B8_SRGB = 0xE7, R5G6B5_UNORM = 0xE8, A1R5G5B5_UNORM = 0xE9, R8G8_UNORM = 0xEA, @@ -71,17 +78,42 @@ enum class RenderTargetFormat : u32 { R8_SNORM = 0xF4, R8_SINT = 0xF5, R8_UINT = 0xF6, + + // A8_UNORM = 0xF7, + X1R5G5B5_UNORM = 0xF8, + X8B8G8R8_UNORM = 0xF9, + X8B8G8R8_SRGB = 0xFA, + /* + Z1R5G5B5_UNORM = 0xFB, + O1R5G5B5_UNORM = 0xFC, + Z8R8G8B8_UNORM = 0xFD, + O8R8G8B8_UNORM = 0xFE, + R32_UNORM = 0xFF, + A16_UNORM = 0x40, + A16_FLOAT = 0x41, + A32_FLOAT = 0x42, + A8R8_UNORM = 0x43, + R16A16_UNORM = 0x44, + R16A16_FLOAT = 0x45, + R32A32_FLOAT = 0x46, + B8G8R8A8_UNORM = 0x47, + */ }; enum class DepthFormat : u32 { - D32_FLOAT = 0xA, - D16_UNORM = 0x13, - S8_UINT_Z24_UNORM = 0x14, - D24X8_UNORM = 0x15, - D24S8_UNORM = 0x16, + Z32_FLOAT = 0xA, + Z16_UNORM = 0x13, + Z24_UNORM_S8_UINT = 0x14, + X8Z24_UNORM = 0x15, + S8Z24_UNORM = 0x16, S8_UINT = 0x17, - D24C8_UNORM = 0x18, - D32_FLOAT_S8X24_UINT = 0x19, + V8Z24_UNORM = 0x18, + Z32_FLOAT_X24S8_UINT = 0x19, + /* + X8Z24_UNORM_X16V8S8_UINT = 0x1D, + Z32_FLOAT_X16V8X8_UINT = 0x1E, + Z32_FLOAT_X16V8S8_UINT = 0x1F, + */ }; namespace Engines { @@ -89,73 +121,58 @@ class Maxwell3D; class KeplerCompute; } // namespace Engines -enum class EngineID { - FERMI_TWOD_A = 0x902D, // 2D Engine - MAXWELL_B = 0xB197, // 3D Engine - KEPLER_COMPUTE_B = 0xB1C0, - KEPLER_INLINE_TO_MEMORY_B = 0xA140, - MAXWELL_DMA_COPY_A = 0xB0B5, -}; +namespace Control { +struct ChannelState; +} + +namespace Host1x { +class Host1x; +} // namespace Host1x class MemoryManager; class GPU final { public: - struct MethodCall { - u32 method{}; - u32 argument{}; - u32 subchannel{}; - u32 method_count{}; - - explicit MethodCall(u32 method_, u32 argument_, u32 subchannel_ = 0, u32 method_count_ = 0) - : method(method_), argument(argument_), subchannel(subchannel_), - method_count(method_count_) {} - - [[nodiscard]] bool IsLastCall() const { - return method_count <= 1; - } - }; - - enum class FenceOperation : u32 { - Acquire = 0, - Increment = 1, - }; - - union FenceAction { - u32 raw; - BitField<0, 1, FenceOperation> op; - BitField<8, 24, u32> syncpoint_id; - }; - explicit GPU(Core::System& system, bool is_async, bool use_nvdec); ~GPU(); /// Binds a renderer to the GPU. void BindRenderer(std::unique_ptr renderer); - /// Calls a GPU method. - void CallMethod(const MethodCall& method_call); - - /// Calls a GPU multivalue method. - void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending); - /// Flush all current written commands into the host GPU for execution. void FlushCommands(); /// Synchronizes CPU writes with Host GPU memory. - void SyncGuestHost(); + void InvalidateGPUCache(); /// Signal the ending of command list. void OnCommandListEnd(); + std::shared_ptr AllocateChannel(); + + void InitChannel(Control::ChannelState& to_init); + + void BindChannel(s32 channel_id); + + void ReleaseChannel(Control::ChannelState& to_release); + + void InitAddressSpace(Tegra::MemoryManager& memory_manager); + /// Request a host GPU memory flush from the CPU. [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); /// Obtains current flush request fence id. - [[nodiscard]] u64 CurrentFlushRequestFence() const; + [[nodiscard]] u64 CurrentSyncRequestFence() const; + + void WaitForSyncOperation(u64 fence); /// Tick pending requests within the GPU. void TickWork(); + /// Gets a mutable reference to the Host1x interface + [[nodiscard]] Host1x::Host1x& Host1x(); + + /// Gets an immutable reference to the Host1x interface. + [[nodiscard]] const Host1x::Host1x& Host1x() const; + /// Returns a reference to the Maxwell3D GPU engine. [[nodiscard]] Engines::Maxwell3D& Maxwell3D(); @@ -168,12 +185,6 @@ public: /// Returns a reference to the KeplerCompute GPU engine. [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const; - /// Returns a reference to the GPU memory manager. - [[nodiscard]] Tegra::MemoryManager& MemoryManager(); - - /// Returns a const reference to the GPU memory manager. - [[nodiscard]] const Tegra::MemoryManager& MemoryManager() const; - /// Returns a reference to the GPU DMA pusher. [[nodiscard]] Tegra::DmaPusher& DmaPusher(); @@ -192,17 +203,6 @@ public: /// Returns a const reference to the shader notifier. [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const; - /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. - void WaitFence(u32 syncpoint_id, u32 value); - - void IncrementSyncPoint(u32 syncpoint_id); - - [[nodiscard]] u32 GetSyncpointValue(u32 syncpoint_id) const; - - void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value); - - [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value); - [[nodiscard]] u64 GetTicks() const; [[nodiscard]] bool IsAsync() const; @@ -211,6 +211,9 @@ public: void RendererFrameEndNotify(); + void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, + std::array& fences, size_t num_fences); + /// Performs any additional setup necessary in order to begin GPU emulation. /// This can be used to launch any necessary threads and register any necessary /// core timing events. @@ -226,7 +229,7 @@ public: void ReleaseContext(); /// Push GPU command entries to be processed - void PushGPUEntries(Tegra::CommandList&& entries); + void PushGPUEntries(s32 channel, Tegra::CommandList&& entries); /// Push GPU command buffer entries to be processed void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries); @@ -248,7 +251,7 @@ public: private: struct Impl; - std::unique_ptr impl; + mutable std::unique_ptr impl; }; } // namespace Tegra diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index d43f717..164a525 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -8,6 +8,7 @@ #include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" +#include "video_core/control/scheduler.h" #include "video_core/dma_pusher.h" #include "video_core/gpu.h" #include "video_core/gpu_thread.h" @@ -18,8 +19,8 @@ namespace VideoCommon::GPUThread { /// Runs the GPU thread static void RunThread(std::stop_token stop_token, Core::System& system, VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, - Tegra::DmaPusher& dma_pusher, SynchState& state) { - std::string name = "yuzu:GPU"; + Tegra::Control::Scheduler& scheduler, SynchState& state) { + std::string name = "GPU"; MicroProfileOnThreadCreate(name.c_str()); SCOPE_EXIT({ MicroProfileOnThreadExit(); }); @@ -36,8 +37,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system, break; } if (auto* submit_list = std::get_if(&next.data)) { - dma_pusher.Push(std::move(submit_list->entries)); - dma_pusher.DispatchCalls(); + scheduler.Push(submit_list->channel, std::move(submit_list->entries)); } else if (const auto* data = std::get_if(&next.data)) { renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); } else if (std::holds_alternative(next.data)) { @@ -68,14 +68,14 @@ ThreadManager::~ThreadManager() = default; void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, - Tegra::DmaPusher& dma_pusher) { + Tegra::Control::Scheduler& scheduler) { rasterizer = renderer.ReadRasterizer(); thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context), - std::ref(dma_pusher), std::ref(state)); + std::ref(scheduler), std::ref(state)); } -void ThreadManager::SubmitList(Tegra::CommandList&& entries) { - PushCommand(SubmitListCommand(std::move(entries))); +void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) { + PushCommand(SubmitListCommand(channel, std::move(entries))); } void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { @@ -93,8 +93,12 @@ void ThreadManager::FlushRegion(VAddr addr, u64 size) { } auto& gpu = system.GPU(); u64 fence = gpu.RequestFlush(addr, size); - PushCommand(GPUTickCommand(), true); - ASSERT(fence <= gpu.CurrentFlushRequestFence()); + TickGPU(); + gpu.WaitForSyncOperation(fence); +} + +void ThreadManager::TickGPU() { + PushCommand(GPUTickCommand()); } void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { @@ -121,7 +125,7 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { state.queue.Push(CommandDataContainer(std::move(command_data), fence, block)); if (block) { - state.cv.wait(lk, thread.get_stop_token(), [this, fence] { + Common::CondvarWait(state.cv, lk, thread.get_stop_token(), [this, fence] { return fence <= state.signaled_fence.load(std::memory_order_relaxed); }); } diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 2f8210c..c71a419 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -10,12 +10,15 @@ #include #include +#include "common/polyfill_thread.h" #include "common/threadsafe_queue.h" #include "video_core/framebuffer_config.h" namespace Tegra { struct FramebufferConfig; -class DmaPusher; +namespace Control { +class Scheduler; +} } // namespace Tegra namespace Core { @@ -34,8 +37,10 @@ namespace VideoCommon::GPUThread { /// Command to signal to the GPU thread that a command list is ready for processing struct SubmitListCommand final { - explicit SubmitListCommand(Tegra::CommandList&& entries_) : entries{std::move(entries_)} {} + explicit SubmitListCommand(s32 channel_, Tegra::CommandList&& entries_) + : channel{channel_}, entries{std::move(entries_)} {} + s32 channel; Tegra::CommandList entries; }; @@ -112,10 +117,10 @@ public: /// Creates and starts the GPU thread. void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, - Tegra::DmaPusher& dma_pusher); + Tegra::Control::Scheduler& scheduler); /// Push GPU command entries to be processed - void SubmitList(Tegra::CommandList&& entries); + void SubmitList(s32 channel, Tegra::CommandList&& entries); /// Swap buffers (render frame) void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); @@ -131,6 +136,8 @@ public: void OnCommandListEnd(); + void TickGPU(); + private: /// Pushes a command to be executed by the GPU thread u64 PushCommand(CommandData&& command_data, bool block = false); diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp similarity index 89% rename from src/video_core/command_classes/codecs/codec.cpp rename to src/video_core/host1x/codecs/codec.cpp index a5eb97b..42e7d6e 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/host1x/codecs/codec.cpp @@ -6,11 +6,11 @@ #include #include "common/assert.h" #include "common/settings.h" -#include "video_core/command_classes/codecs/codec.h" -#include "video_core/command_classes/codecs/h264.h" -#include "video_core/command_classes/codecs/vp8.h" -#include "video_core/command_classes/codecs/vp9.h" -#include "video_core/gpu.h" +#include "video_core/host1x/codecs/codec.h" +#include "video_core/host1x/codecs/h264.h" +#include "video_core/host1x/codecs/vp8.h" +#include "video_core/host1x/codecs/vp9.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" extern "C" { @@ -73,10 +73,10 @@ void AVFrameDeleter(AVFrame* ptr) { av_frame_free(&ptr); } -Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs) - : gpu(gpu_), state{regs}, h264_decoder(std::make_unique(gpu)), - vp8_decoder(std::make_unique(gpu)), - vp9_decoder(std::make_unique(gpu)) {} +Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs) + : host1x(host1x_), state{regs}, h264_decoder(std::make_unique(host1x)), + vp8_decoder(std::make_unique(host1x)), + vp9_decoder(std::make_unique(host1x)) {} Codec::~Codec() { if (!initialized) { @@ -168,11 +168,11 @@ void Codec::InitializeGpuDecoder() { void Codec::Initialize() { const AVCodecID codec = [&] { switch (current_codec) { - case NvdecCommon::VideoCodec::H264: + case Host1x::NvdecCommon::VideoCodec::H264: return AV_CODEC_ID_H264; - case NvdecCommon::VideoCodec::VP8: + case Host1x::NvdecCommon::VideoCodec::VP8: return AV_CODEC_ID_VP8; - case NvdecCommon::VideoCodec::VP9: + case Host1x::NvdecCommon::VideoCodec::VP9: return AV_CODEC_ID_VP9; default: UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); @@ -197,7 +197,7 @@ void Codec::Initialize() { initialized = true; } -void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) { +void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) { if (current_codec != codec) { current_codec = codec; LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", GetCurrentCodecName()); @@ -215,11 +215,11 @@ void Codec::Decode() { bool vp9_hidden_frame = false; const auto& frame_data = [&]() { switch (current_codec) { - case Tegra::NvdecCommon::VideoCodec::H264: + case Tegra::Host1x::NvdecCommon::VideoCodec::H264: return h264_decoder->ComposeFrame(state, is_first_frame); - case Tegra::NvdecCommon::VideoCodec::VP8: + case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: return vp8_decoder->ComposeFrame(state); - case Tegra::NvdecCommon::VideoCodec::VP9: + case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: vp9_decoder->ComposeFrame(state); vp9_hidden_frame = vp9_decoder->WasFrameHidden(); return vp9_decoder->GetFrameBytes(); @@ -287,21 +287,21 @@ AVFramePtr Codec::GetCurrentFrame() { return frame; } -NvdecCommon::VideoCodec Codec::GetCurrentCodec() const { +Host1x::NvdecCommon::VideoCodec Codec::GetCurrentCodec() const { return current_codec; } std::string_view Codec::GetCurrentCodecName() const { switch (current_codec) { - case NvdecCommon::VideoCodec::None: + case Host1x::NvdecCommon::VideoCodec::None: return "None"; - case NvdecCommon::VideoCodec::H264: + case Host1x::NvdecCommon::VideoCodec::H264: return "H264"; - case NvdecCommon::VideoCodec::VP8: + case Host1x::NvdecCommon::VideoCodec::VP8: return "VP8"; - case NvdecCommon::VideoCodec::H265: + case Host1x::NvdecCommon::VideoCodec::H265: return "H265"; - case NvdecCommon::VideoCodec::VP9: + case Host1x::NvdecCommon::VideoCodec::VP9: return "VP9"; default: return "Unknown"; diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/host1x/codecs/codec.h similarity index 74% rename from src/video_core/command_classes/codecs/codec.h rename to src/video_core/host1x/codecs/codec.h index 0c24054..0d45fb7 100644 --- a/src/video_core/command_classes/codecs/codec.h +++ b/src/video_core/host1x/codecs/codec.h @@ -6,8 +6,8 @@ #include #include #include - -#include "video_core/command_classes/nvdec_common.h" +#include "common/common_types.h" +#include "video_core/host1x/nvdec_common.h" extern "C" { #if defined(__GNUC__) || defined(__clang__) @@ -21,7 +21,6 @@ extern "C" { } namespace Tegra { -class GPU; void AVFrameDeleter(AVFrame* ptr); using AVFramePtr = std::unique_ptr; @@ -32,16 +31,20 @@ class VP8; class VP9; } // namespace Decoder +namespace Host1x { +class Host1x; +} // namespace Host1x + class Codec { public: - explicit Codec(GPU& gpu, const NvdecCommon::NvdecRegisters& regs); + explicit Codec(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs); ~Codec(); /// Initialize the codec, returning success or failure void Initialize(); /// Sets NVDEC video stream codec - void SetTargetCodec(NvdecCommon::VideoCodec codec); + void SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec); /// Call decoders to construct headers, decode AVFrame with ffmpeg void Decode(); @@ -50,7 +53,7 @@ public: [[nodiscard]] AVFramePtr GetCurrentFrame(); /// Returns the value of current_codec - [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const; + [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const; /// Return name of the current codec [[nodiscard]] std::string_view GetCurrentCodecName() const; @@ -63,14 +66,14 @@ private: bool CreateGpuAvDevice(); bool initialized{}; - NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; + Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None}; const AVCodec* av_codec{nullptr}; AVCodecContext* av_codec_ctx{nullptr}; AVBufferRef* av_gpu_decoder{nullptr}; - GPU& gpu; - const NvdecCommon::NvdecRegisters& state; + Host1x::Host1x& host1x; + const Host1x::NvdecCommon::NvdecRegisters& state; std::unique_ptr h264_decoder; std::unique_ptr vp8_decoder; std::unique_ptr vp9_decoder; diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp similarity index 92% rename from src/video_core/command_classes/codecs/h264.cpp rename to src/video_core/host1x/codecs/h264.cpp index e2acd54..e87bd65 100644 --- a/src/video_core/command_classes/codecs/h264.cpp +++ b/src/video_core/host1x/codecs/h264.cpp @@ -5,8 +5,8 @@ #include #include "common/settings.h" -#include "video_core/command_classes/codecs/h264.h" -#include "video_core/gpu.h" +#include "video_core/host1x/codecs/h264.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" namespace Tegra::Decoder { @@ -24,19 +24,20 @@ constexpr std::array zig_zag_scan{ }; } // Anonymous namespace -H264::H264(GPU& gpu_) : gpu(gpu_) {} +H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {} H264::~H264() = default; -const std::vector& H264::ComposeFrame(const NvdecCommon::NvdecRegisters& state, +const std::vector& H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, bool is_first_frame) { H264DecoderContext context; - gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext)); + host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, + sizeof(H264DecoderContext)); const s64 frame_number = context.h264_parameter_set.frame_number.Value(); if (!is_first_frame && frame_number != 0) { frame.resize(context.stream_len); - gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); + host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); return frame; } @@ -155,8 +156,8 @@ const std::vector& H264::ComposeFrame(const NvdecCommon::NvdecRegisters& sta frame.resize(encoded_header.size() + context.stream_len); std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); - gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, - frame.data() + encoded_header.size(), context.stream_len); + host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, + frame.data() + encoded_header.size(), context.stream_len); return frame; } diff --git a/src/video_core/command_classes/codecs/h264.h b/src/video_core/host1x/codecs/h264.h similarity index 95% rename from src/video_core/command_classes/codecs/h264.h rename to src/video_core/host1x/codecs/h264.h index 2615743..5cc8645 100644 --- a/src/video_core/command_classes/codecs/h264.h +++ b/src/video_core/host1x/codecs/h264.h @@ -8,10 +8,14 @@ #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" -#include "video_core/command_classes/nvdec_common.h" +#include "video_core/host1x/nvdec_common.h" namespace Tegra { -class GPU; + +namespace Host1x { +class Host1x; +} // namespace Host1x + namespace Decoder { class H264BitWriter { @@ -55,16 +59,16 @@ private: class H264 { public: - explicit H264(GPU& gpu); + explicit H264(Host1x::Host1x& host1x); ~H264(); /// Compose the H264 frame for FFmpeg decoding - [[nodiscard]] const std::vector& ComposeFrame(const NvdecCommon::NvdecRegisters& state, - bool is_first_frame = false); + [[nodiscard]] const std::vector& ComposeFrame( + const Host1x::NvdecCommon::NvdecRegisters& state, bool is_first_frame = false); private: std::vector frame; - GPU& gpu; + Host1x::Host1x& host1x; struct H264ParameterSet { s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00 diff --git a/src/video_core/command_classes/codecs/vp8.cpp b/src/video_core/host1x/codecs/vp8.cpp similarity index 80% rename from src/video_core/command_classes/codecs/vp8.cpp rename to src/video_core/host1x/codecs/vp8.cpp index c83b9bb..28fb12c 100644 --- a/src/video_core/command_classes/codecs/vp8.cpp +++ b/src/video_core/host1x/codecs/vp8.cpp @@ -3,18 +3,18 @@ #include -#include "video_core/command_classes/codecs/vp8.h" -#include "video_core/gpu.h" +#include "video_core/host1x/codecs/vp8.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" namespace Tegra::Decoder { -VP8::VP8(GPU& gpu_) : gpu(gpu_) {} +VP8::VP8(Host1x::Host1x& host1x_) : host1x{host1x_} {} VP8::~VP8() = default; -const std::vector& VP8::ComposeFrame(const NvdecCommon::NvdecRegisters& state) { +const std::vector& VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { VP8PictureInfo info; - gpu.MemoryManager().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo)); + host1x.MemoryManager().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo)); const bool is_key_frame = info.key_frame == 1u; const auto bitstream_size = static_cast(info.vld_buffer_size); @@ -45,7 +45,7 @@ const std::vector& VP8::ComposeFrame(const NvdecCommon::NvdecRegisters& stat frame[9] = static_cast(((info.frame_height >> 8) & 0x3f)); } const u64 bitstream_offset = state.frame_bitstream_offset; - gpu.MemoryManager().ReadBlock(bitstream_offset, frame.data() + header_size, bitstream_size); + host1x.MemoryManager().ReadBlock(bitstream_offset, frame.data() + header_size, bitstream_size); return frame; } diff --git a/src/video_core/command_classes/codecs/vp8.h b/src/video_core/host1x/codecs/vp8.h similarity index 88% rename from src/video_core/command_classes/codecs/vp8.h rename to src/video_core/host1x/codecs/vp8.h index 3357667..5bf07ec 100644 --- a/src/video_core/command_classes/codecs/vp8.h +++ b/src/video_core/host1x/codecs/vp8.h @@ -8,23 +8,28 @@ #include "common/common_funcs.h" #include "common/common_types.h" -#include "video_core/command_classes/nvdec_common.h" +#include "video_core/host1x/nvdec_common.h" namespace Tegra { -class GPU; + +namespace Host1x { +class Host1x; +} // namespace Host1x + namespace Decoder { class VP8 { public: - explicit VP8(GPU& gpu); + explicit VP8(Host1x::Host1x& host1x); ~VP8(); /// Compose the VP8 frame for FFmpeg decoding - [[nodiscard]] const std::vector& ComposeFrame(const NvdecCommon::NvdecRegisters& state); + [[nodiscard]] const std::vector& ComposeFrame( + const Host1x::NvdecCommon::NvdecRegisters& state); private: std::vector frame; - GPU& gpu; + Host1x::Host1x& host1x; struct VP8PictureInfo { INSERT_PADDING_WORDS_NOINIT(14); diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/host1x/codecs/vp9.cpp similarity index 98% rename from src/video_core/command_classes/codecs/vp9.cpp rename to src/video_core/host1x/codecs/vp9.cpp index c014314..cf40c90 100644 --- a/src/video_core/command_classes/codecs/vp9.cpp +++ b/src/video_core/host1x/codecs/vp9.cpp @@ -4,8 +4,8 @@ #include // for std::copy #include #include "common/assert.h" -#include "video_core/command_classes/codecs/vp9.h" -#include "video_core/gpu.h" +#include "video_core/host1x/codecs/vp9.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" namespace Tegra::Decoder { @@ -236,7 +236,7 @@ constexpr std::array map_lut{ } } // Anonymous namespace -VP9::VP9(GPU& gpu_) : gpu{gpu_} {} +VP9::VP9(Host1x::Host1x& host1x_) : host1x{host1x_} {} VP9::~VP9() = default; @@ -355,9 +355,9 @@ void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_ } } -Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) { +Vp9PictureInfo VP9::GetVp9PictureInfo(const Host1x::NvdecCommon::NvdecRegisters& state) { PictureInfo picture_info; - gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo)); + host1x.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo)); Vp9PictureInfo vp9_info = picture_info.Convert(); InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy); @@ -372,18 +372,19 @@ Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) { EntropyProbs entropy; - gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs)); + host1x.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs)); entropy.Convert(dst); } -Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state) { +Vp9FrameContainer VP9::GetCurrentFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { Vp9FrameContainer current_frame{}; { - gpu.SyncGuestHost(); + // gpu.SyncGuestHost(); epic, why? current_frame.info = GetVp9PictureInfo(state); current_frame.bit_stream.resize(current_frame.info.bitstream_size); - gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(), - current_frame.info.bitstream_size); + host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, + current_frame.bit_stream.data(), + current_frame.info.bitstream_size); } if (!next_frame.bit_stream.empty()) { Vp9FrameContainer temp{ @@ -769,7 +770,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() { return uncomp_writer; } -void VP9::ComposeFrame(const NvdecCommon::NvdecRegisters& state) { +void VP9::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { std::vector bitstream; { Vp9FrameContainer curr_frame = GetCurrentFrame(state); diff --git a/src/video_core/command_classes/codecs/vp9.h b/src/video_core/host1x/codecs/vp9.h similarity index 91% rename from src/video_core/command_classes/codecs/vp9.h rename to src/video_core/host1x/codecs/vp9.h index ecc40e8..d4083e8 100644 --- a/src/video_core/command_classes/codecs/vp9.h +++ b/src/video_core/host1x/codecs/vp9.h @@ -8,11 +8,15 @@ #include "common/common_types.h" #include "common/stream.h" -#include "video_core/command_classes/codecs/vp9_types.h" -#include "video_core/command_classes/nvdec_common.h" +#include "video_core/host1x/codecs/vp9_types.h" +#include "video_core/host1x/nvdec_common.h" namespace Tegra { -class GPU; + +namespace Host1x { +class Host1x; +} // namespace Host1x + namespace Decoder { /// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the @@ -106,7 +110,7 @@ private: class VP9 { public: - explicit VP9(GPU& gpu_); + explicit VP9(Host1x::Host1x& host1x); ~VP9(); VP9(const VP9&) = delete; @@ -117,7 +121,7 @@ public: /// Composes the VP9 frame from the GPU state information. /// Based on the official VP9 spec documentation - void ComposeFrame(const NvdecCommon::NvdecRegisters& state); + void ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state); /// Returns true if the most recent frame was a hidden frame. [[nodiscard]] bool WasFrameHidden() const { @@ -162,19 +166,21 @@ private: void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob); /// Returns VP9 information from NVDEC provided offset and size - [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state); + [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo( + const Host1x::NvdecCommon::NvdecRegisters& state); /// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct void InsertEntropy(u64 offset, Vp9EntropyProbs& dst); /// Returns frame to be decoded after buffering - [[nodiscard]] Vp9FrameContainer GetCurrentFrame(const NvdecCommon::NvdecRegisters& state); + [[nodiscard]] Vp9FrameContainer GetCurrentFrame( + const Host1x::NvdecCommon::NvdecRegisters& state); /// Use NVDEC providied information to compose the headers for the current frame [[nodiscard]] std::vector ComposeCompressedHeader(); [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader(); - GPU& gpu; + Host1x::Host1x& host1x; std::vector frame; std::array loop_filter_ref_deltas{}; diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/host1x/codecs/vp9_types.h similarity index 99% rename from src/video_core/command_classes/codecs/vp9_types.h rename to src/video_core/host1x/codecs/vp9_types.h index bb3d8df..adad8ed 100644 --- a/src/video_core/command_classes/codecs/vp9_types.h +++ b/src/video_core/host1x/codecs/vp9_types.h @@ -9,7 +9,6 @@ #include "common/common_types.h" namespace Tegra { -class GPU; namespace Decoder { struct Vp9FrameDimensions { diff --git a/src/video_core/host1x/control.cpp b/src/video_core/host1x/control.cpp new file mode 100644 index 0000000..dceefdb --- /dev/null +++ b/src/video_core/host1x/control.cpp @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/assert.h" +#include "video_core/host1x/control.h" +#include "video_core/host1x/host1x.h" + +namespace Tegra::Host1x { + +Control::Control(Host1x& host1x_) : host1x(host1x_) {} + +Control::~Control() = default; + +void Control::ProcessMethod(Method method, u32 argument) { + switch (method) { + case Method::LoadSyncptPayload32: + syncpoint_value = argument; + break; + case Method::WaitSyncpt: + case Method::WaitSyncpt32: + Execute(argument); + break; + default: + UNIMPLEMENTED_MSG("Control method 0x{:X}", static_cast(method)); + break; + } +} + +void Control::Execute(u32 data) { + host1x.GetSyncpointManager().WaitHost(data, syncpoint_value); +} + +} // namespace Tegra::Host1x diff --git a/src/video_core/command_classes/host1x.h b/src/video_core/host1x/control.h similarity index 62% rename from src/video_core/command_classes/host1x.h rename to src/video_core/host1x/control.h index bb48a43..e117888 100644 --- a/src/video_core/command_classes/host1x.h +++ b/src/video_core/host1x/control.h @@ -1,15 +1,19 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include "common/common_types.h" namespace Tegra { -class GPU; + +namespace Host1x { + +class Host1x; class Nvdec; -class Host1x { +class Control { public: enum class Method : u32 { WaitSyncpt = 0x8, @@ -17,8 +21,8 @@ public: WaitSyncpt32 = 0x50, }; - explicit Host1x(GPU& gpu); - ~Host1x(); + explicit Control(Host1x& host1x); + ~Control(); /// Writes the method into the state, Invoke Execute() if encountered void ProcessMethod(Method method, u32 argument); @@ -28,7 +32,9 @@ private: void Execute(u32 data); u32 syncpoint_value{}; - GPU& gpu; + Host1x& host1x; }; +} // namespace Host1x + } // namespace Tegra diff --git a/src/video_core/host1x/host1x.cpp b/src/video_core/host1x/host1x.cpp new file mode 100644 index 0000000..7c317a8 --- /dev/null +++ b/src/video_core/host1x/host1x.cpp @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/core.h" +#include "video_core/host1x/host1x.h" + +namespace Tegra { + +namespace Host1x { + +Host1x::Host1x(Core::System& system_) + : system{system_}, syncpoint_manager{}, memory_manager{system, 32, 12}, + allocator{std::make_unique>(1 << 12)} {} + +} // namespace Host1x + +} // namespace Tegra diff --git a/src/video_core/host1x/host1x.h b/src/video_core/host1x/host1x.h new file mode 100644 index 0000000..57082ae --- /dev/null +++ b/src/video_core/host1x/host1x.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" + +#include "common/address_space.h" +#include "video_core/host1x/syncpoint_manager.h" +#include "video_core/memory_manager.h" + +namespace Core { +class System; +} // namespace Core + +namespace Tegra { + +namespace Host1x { + +class Host1x { +public: + explicit Host1x(Core::System& system); + + SyncpointManager& GetSyncpointManager() { + return syncpoint_manager; + } + + const SyncpointManager& GetSyncpointManager() const { + return syncpoint_manager; + } + + Tegra::MemoryManager& MemoryManager() { + return memory_manager; + } + + const Tegra::MemoryManager& MemoryManager() const { + return memory_manager; + } + + Common::FlatAllocator& Allocator() { + return *allocator; + } + + const Common::FlatAllocator& Allocator() const { + return *allocator; + } + +private: + Core::System& system; + SyncpointManager syncpoint_manager; + Tegra::MemoryManager memory_manager; + std::unique_ptr> allocator; +}; + +} // namespace Host1x + +} // namespace Tegra diff --git a/src/video_core/command_classes/nvdec.cpp b/src/video_core/host1x/nvdec.cpp similarity index 81% rename from src/video_core/command_classes/nvdec.cpp rename to src/video_core/host1x/nvdec.cpp index 4fbbe3d..a4bd5b7 100644 --- a/src/video_core/command_classes/nvdec.cpp +++ b/src/video_core/host1x/nvdec.cpp @@ -2,15 +2,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" -#include "video_core/command_classes/nvdec.h" -#include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/nvdec.h" -namespace Tegra { +namespace Tegra::Host1x { #define NVDEC_REG_INDEX(field_name) \ (offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64)) -Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), state{}, codec(std::make_unique(gpu, state)) {} +Nvdec::Nvdec(Host1x& host1x_) + : host1x(host1x_), state{}, codec(std::make_unique(host1x, state)) {} Nvdec::~Nvdec() = default; @@ -44,4 +45,4 @@ void Nvdec::Execute() { } } -} // namespace Tegra +} // namespace Tegra::Host1x diff --git a/src/video_core/command_classes/nvdec.h b/src/video_core/host1x/nvdec.h similarity index 79% rename from src/video_core/command_classes/nvdec.h rename to src/video_core/host1x/nvdec.h index 488531f..3949d51 100644 --- a/src/video_core/command_classes/nvdec.h +++ b/src/video_core/host1x/nvdec.h @@ -6,14 +6,17 @@ #include #include #include "common/common_types.h" -#include "video_core/command_classes/codecs/codec.h" +#include "video_core/host1x/codecs/codec.h" namespace Tegra { -class GPU; + +namespace Host1x { + +class Host1x; class Nvdec { public: - explicit Nvdec(GPU& gpu); + explicit Nvdec(Host1x& host1x); ~Nvdec(); /// Writes the method into the state, Invoke Execute() if encountered @@ -26,8 +29,11 @@ private: /// Invoke codec to decode a frame void Execute(); - GPU& gpu; + Host1x& host1x; NvdecCommon::NvdecRegisters state; std::unique_ptr codec; }; + +} // namespace Host1x + } // namespace Tegra diff --git a/src/video_core/command_classes/nvdec_common.h b/src/video_core/host1x/nvdec_common.h similarity index 98% rename from src/video_core/command_classes/nvdec_common.h rename to src/video_core/host1x/nvdec_common.h index 521e5b5..49d67eb 100644 --- a/src/video_core/command_classes/nvdec_common.h +++ b/src/video_core/host1x/nvdec_common.h @@ -7,7 +7,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" -namespace Tegra::NvdecCommon { +namespace Tegra::Host1x::NvdecCommon { enum class VideoCodec : u64 { None = 0x0, @@ -94,4 +94,4 @@ ASSERT_REG_POSITION(vp9_curr_frame_mvs_offset, 0x176); #undef ASSERT_REG_POSITION -} // namespace Tegra::NvdecCommon +} // namespace Tegra::Host1x::NvdecCommon diff --git a/src/video_core/command_classes/sync_manager.cpp b/src/video_core/host1x/sync_manager.cpp similarity index 73% rename from src/video_core/command_classes/sync_manager.cpp rename to src/video_core/host1x/sync_manager.cpp index 67e5804..5ef9ea2 100644 --- a/src/video_core/command_classes/sync_manager.cpp +++ b/src/video_core/host1x/sync_manager.cpp @@ -3,10 +3,13 @@ #include #include "sync_manager.h" -#include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/syncpoint_manager.h" namespace Tegra { -SyncptIncrManager::SyncptIncrManager(GPU& gpu_) : gpu(gpu_) {} +namespace Host1x { + +SyncptIncrManager::SyncptIncrManager(Host1x& host1x_) : host1x(host1x_) {} SyncptIncrManager::~SyncptIncrManager() = default; void SyncptIncrManager::Increment(u32 id) { @@ -36,8 +39,12 @@ void SyncptIncrManager::IncrementAllDone() { if (!increments[done_count].complete) { break; } - gpu.IncrementSyncPoint(increments[done_count].syncpt_id); + auto& syncpoint_manager = host1x.GetSyncpointManager(); + syncpoint_manager.IncrementGuest(increments[done_count].syncpt_id); + syncpoint_manager.IncrementHost(increments[done_count].syncpt_id); } increments.erase(increments.begin(), increments.begin() + done_count); } + +} // namespace Host1x } // namespace Tegra diff --git a/src/video_core/command_classes/sync_manager.h b/src/video_core/host1x/sync_manager.h similarity index 88% rename from src/video_core/command_classes/sync_manager.h rename to src/video_core/host1x/sync_manager.h index 6dfaae0..7bb77fa 100644 --- a/src/video_core/command_classes/sync_manager.h +++ b/src/video_core/host1x/sync_manager.h @@ -8,7 +8,11 @@ #include "common/common_types.h" namespace Tegra { -class GPU; + +namespace Host1x { + +class Host1x; + struct SyncptIncr { u32 id; u32 class_id; @@ -21,7 +25,7 @@ struct SyncptIncr { class SyncptIncrManager { public: - explicit SyncptIncrManager(GPU& gpu); + explicit SyncptIncrManager(Host1x& host1x); ~SyncptIncrManager(); /// Add syncpoint id and increment all @@ -41,7 +45,9 @@ private: std::mutex increment_lock; u32 current_id{}; - GPU& gpu; + Host1x& host1x; }; +} // namespace Host1x + } // namespace Tegra diff --git a/src/video_core/host1x/syncpoint_manager.cpp b/src/video_core/host1x/syncpoint_manager.cpp new file mode 100644 index 0000000..8f23ce5 --- /dev/null +++ b/src/video_core/host1x/syncpoint_manager.cpp @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/microprofile.h" +#include "video_core/host1x/syncpoint_manager.h" + +namespace Tegra { + +namespace Host1x { + +MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); + +SyncpointManager::ActionHandle SyncpointManager::RegisterAction( + std::atomic& syncpoint, std::list& action_storage, u32 expected_value, + std::function&& action) { + if (syncpoint.load(std::memory_order_acquire) >= expected_value) { + action(); + return {}; + } + + std::unique_lock lk(guard); + if (syncpoint.load(std::memory_order_relaxed) >= expected_value) { + action(); + return {}; + } + auto it = action_storage.begin(); + while (it != action_storage.end()) { + if (it->expected_value >= expected_value) { + break; + } + ++it; + } + return action_storage.emplace(it, expected_value, std::move(action)); +} + +void SyncpointManager::DeregisterAction(std::list& action_storage, + const ActionHandle& handle) { + std::unique_lock lk(guard); + + // We want to ensure the iterator still exists prior to erasing it + // Otherwise, if an invalid iterator was passed in then it could lead to UB + // It is important to avoid UB in that case since the deregister isn't called from a locked + // context + for (auto it = action_storage.begin(); it != action_storage.end(); it++) { + if (it == handle) { + action_storage.erase(it); + return; + } + } +} + +void SyncpointManager::DeregisterGuestAction(u32 syncpoint_id, const ActionHandle& handle) { + DeregisterAction(guest_action_storage[syncpoint_id], handle); +} + +void SyncpointManager::DeregisterHostAction(u32 syncpoint_id, const ActionHandle& handle) { + DeregisterAction(host_action_storage[syncpoint_id], handle); +} + +void SyncpointManager::IncrementGuest(u32 syncpoint_id) { + Increment(syncpoints_guest[syncpoint_id], wait_guest_cv, guest_action_storage[syncpoint_id]); +} + +void SyncpointManager::IncrementHost(u32 syncpoint_id) { + Increment(syncpoints_host[syncpoint_id], wait_host_cv, host_action_storage[syncpoint_id]); +} + +void SyncpointManager::WaitGuest(u32 syncpoint_id, u32 expected_value) { + Wait(syncpoints_guest[syncpoint_id], wait_guest_cv, expected_value); +} + +void SyncpointManager::WaitHost(u32 syncpoint_id, u32 expected_value) { + MICROPROFILE_SCOPE(GPU_wait); + Wait(syncpoints_host[syncpoint_id], wait_host_cv, expected_value); +} + +void SyncpointManager::Increment(std::atomic& syncpoint, std::condition_variable& wait_cv, + std::list& action_storage) { + auto new_value{syncpoint.fetch_add(1, std::memory_order_acq_rel) + 1}; + + std::unique_lock lk(guard); + auto it = action_storage.begin(); + while (it != action_storage.end()) { + if (it->expected_value > new_value) { + break; + } + it->action(); + it = action_storage.erase(it); + } + wait_cv.notify_all(); +} + +void SyncpointManager::Wait(std::atomic& syncpoint, std::condition_variable& wait_cv, + u32 expected_value) { + const auto pred = [&]() { return syncpoint.load(std::memory_order_acquire) >= expected_value; }; + if (pred()) { + return; + } + + std::unique_lock lk(guard); + wait_cv.wait(lk, pred); +} + +} // namespace Host1x + +} // namespace Tegra diff --git a/src/video_core/host1x/syncpoint_manager.h b/src/video_core/host1x/syncpoint_manager.h new file mode 100644 index 0000000..847ed20 --- /dev/null +++ b/src/video_core/host1x/syncpoint_manager.h @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "common/common_types.h" + +namespace Tegra { + +namespace Host1x { + +class SyncpointManager { +public: + u32 GetGuestSyncpointValue(u32 id) const { + return syncpoints_guest[id].load(std::memory_order_acquire); + } + + u32 GetHostSyncpointValue(u32 id) const { + return syncpoints_host[id].load(std::memory_order_acquire); + } + + struct RegisteredAction { + explicit RegisteredAction(u32 expected_value_, std::function&& action_) + : expected_value{expected_value_}, action{std::move(action_)} {} + u32 expected_value; + std::function action; + }; + using ActionHandle = std::list::iterator; + + template + ActionHandle RegisterGuestAction(u32 syncpoint_id, u32 expected_value, Func&& action) { + return RegisterAction(syncpoints_guest[syncpoint_id], guest_action_storage[syncpoint_id], + expected_value, std::move(action)); + } + + template + ActionHandle RegisterHostAction(u32 syncpoint_id, u32 expected_value, Func&& action) { + return RegisterAction(syncpoints_host[syncpoint_id], host_action_storage[syncpoint_id], + expected_value, std::move(action)); + } + + void DeregisterGuestAction(u32 syncpoint_id, const ActionHandle& handle); + + void DeregisterHostAction(u32 syncpoint_id, const ActionHandle& handle); + + void IncrementGuest(u32 syncpoint_id); + + void IncrementHost(u32 syncpoint_id); + + void WaitGuest(u32 syncpoint_id, u32 expected_value); + + void WaitHost(u32 syncpoint_id, u32 expected_value); + + bool IsReadyGuest(u32 syncpoint_id, u32 expected_value) const { + return syncpoints_guest[syncpoint_id].load(std::memory_order_acquire) >= expected_value; + } + + bool IsReadyHost(u32 syncpoint_id, u32 expected_value) const { + return syncpoints_host[syncpoint_id].load(std::memory_order_acquire) >= expected_value; + } + +private: + void Increment(std::atomic& syncpoint, std::condition_variable& wait_cv, + std::list& action_storage); + + ActionHandle RegisterAction(std::atomic& syncpoint, + std::list& action_storage, u32 expected_value, + std::function&& action); + + void DeregisterAction(std::list& action_storage, const ActionHandle& handle); + + void Wait(std::atomic& syncpoint, std::condition_variable& wait_cv, u32 expected_value); + + static constexpr size_t NUM_MAX_SYNCPOINTS = 192; + + std::array, NUM_MAX_SYNCPOINTS> syncpoints_guest{}; + std::array, NUM_MAX_SYNCPOINTS> syncpoints_host{}; + + std::array, NUM_MAX_SYNCPOINTS> guest_action_storage; + std::array, NUM_MAX_SYNCPOINTS> host_action_storage; + + std::mutex guard; + std::condition_variable wait_guest_cv; + std::condition_variable wait_host_cv; +}; + +} // namespace Host1x + +} // namespace Tegra diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/host1x/vic.cpp similarity index 87% rename from src/video_core/command_classes/vic.cpp rename to src/video_core/host1x/vic.cpp index 7c17df3..ac0b7d2 100644 --- a/src/video_core/command_classes/vic.cpp +++ b/src/video_core/host1x/vic.cpp @@ -18,14 +18,17 @@ extern "C" { #include "common/bit_field.h" #include "common/logging/log.h" -#include "video_core/command_classes/nvdec.h" -#include "video_core/command_classes/vic.h" #include "video_core/engines/maxwell_3d.h" -#include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/nvdec.h" +#include "video_core/host1x/vic.h" #include "video_core/memory_manager.h" #include "video_core/textures/decoders.h" namespace Tegra { + +namespace Host1x { + namespace { enum class VideoPixelFormat : u64_le { RGBA8 = 0x1f, @@ -46,8 +49,8 @@ union VicConfig { BitField<46, 14, u64_le> surface_height_minus1; }; -Vic::Vic(GPU& gpu_, std::shared_ptr nvdec_processor_) - : gpu(gpu_), +Vic::Vic(Host1x& host1x_, std::shared_ptr nvdec_processor_) + : host1x(host1x_), nvdec_processor(std::move(nvdec_processor_)), converted_frame_buffer{nullptr, av_free} {} Vic::~Vic() = default; @@ -78,7 +81,7 @@ void Vic::Execute() { LOG_ERROR(Service_NVDRV, "VIC Luma address not set."); return; } - const VicConfig config{gpu.MemoryManager().Read(config_struct_address + 0x20)}; + const VicConfig config{host1x.MemoryManager().Read(config_struct_address + 0x20)}; const AVFramePtr frame_ptr = nvdec_processor->GetFrame(); const auto* frame = frame_ptr.get(); if (!frame) { @@ -153,15 +156,16 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { const u32 block_height = static_cast(config.block_linear_height_log2); const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0); luma_buffer.resize(size); - Texture::SwizzleSubrect(width, height, width * 4, width, 4, luma_buffer.data(), - converted_frame_buf_addr, block_height, 0, 0); + std::span frame_buff(converted_frame_buf_addr, 4 * width * height); + Texture::SwizzleSubrect(luma_buffer, frame_buff, 4, width, height, 1, 0, 0, width, height, + block_height, 0, width * 4); - gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); + host1x.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); } else { // send pitch linear frame const size_t linear_size = width * height * 4; - gpu.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr, - linear_size); + host1x.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr, + linear_size); } } @@ -189,8 +193,8 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { luma_buffer[dst + x] = luma_src[src + x]; } } - gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), - luma_buffer.size()); + host1x.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), + luma_buffer.size()); // Chroma const std::size_t half_height = frame_height / 2; @@ -231,8 +235,10 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { ASSERT(false); break; } - gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(), - chroma_buffer.size()); + host1x.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(), + chroma_buffer.size()); } +} // namespace Host1x + } // namespace Tegra diff --git a/src/video_core/command_classes/vic.h b/src/video_core/host1x/vic.h similarity index 86% rename from src/video_core/command_classes/vic.h rename to src/video_core/host1x/vic.h index 010daa6..2b78786 100644 --- a/src/video_core/command_classes/vic.h +++ b/src/video_core/host1x/vic.h @@ -10,7 +10,10 @@ struct SwsContext; namespace Tegra { -class GPU; + +namespace Host1x { + +class Host1x; class Nvdec; union VicConfig; @@ -25,7 +28,7 @@ public: SetOutputSurfaceChromaUnusedOffset = 0x1ca }; - explicit Vic(GPU& gpu, std::shared_ptr nvdec_processor); + explicit Vic(Host1x& host1x, std::shared_ptr nvdec_processor); ~Vic(); @@ -39,8 +42,8 @@ private: void WriteYUVFrame(const AVFrame* frame, const VicConfig& config); - GPU& gpu; - std::shared_ptr nvdec_processor; + Host1x& host1x; + std::shared_ptr nvdec_processor; /// Avoid reallocation of the following buffers every frame, as their /// size does not change during a stream @@ -58,4 +61,6 @@ private: s32 scaler_height{}; }; +} // namespace Host1x + } // namespace Tegra diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 2149ab9..e6dc24f 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -26,9 +26,16 @@ set(SHADER_FILES opengl_present.frag opengl_present.vert opengl_present_scaleforce.frag + opengl_smaa.glsl pitch_unswizzle.comp present_bicubic.frag present_gaussian.frag + smaa_edge_detection.vert + smaa_edge_detection.frag + smaa_blending_weight_calculation.vert + smaa_blending_weight_calculation.frag + smaa_neighborhood_blending.vert + smaa_neighborhood_blending.frag vulkan_blit_color_float.frag vulkan_blit_depth_stencil.frag vulkan_fidelityfx_fsr_easu_fp16.comp diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake index 9f75255..4f5cd25 100644 --- a/src/video_core/host_shaders/StringShaderHeader.cmake +++ b/src/video_core/host_shaders/StringShaderHeader.cmake @@ -11,10 +11,6 @@ string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME) FILE(READ ${SOURCE_FILE} line_contents) -# Replace double quotes with single quotes, -# as double quotes will be used to wrap the lines -STRING(REGEX REPLACE "\"" "'" line_contents "${line_contents}") - # CMake separates list elements with semicolons, but semicolons # are used extensively in the shader code. # Replace with a temporary marker, to be reverted later. @@ -25,7 +21,7 @@ STRING(REGEX REPLACE "\n" ";" line_contents "${line_contents}") # Build the shader string, wrapping each line in double quotes. foreach(line IN LISTS line_contents) - string(CONCAT CONTENTS "${CONTENTS}" \"${line}\\n\"\n) + string(CONCAT CONTENTS "${CONTENTS}" "R\"(${line}\n)\" ") endforeach() # Revert the original semicolons in the source. diff --git a/src/video_core/host_shaders/astc_decoder.comp b/src/video_core/host_shaders/astc_decoder.comp index 3441a5f..d608678 100644 --- a/src/video_core/host_shaders/astc_decoder.comp +++ b/src/video_core/host_shaders/astc_decoder.comp @@ -1065,7 +1065,7 @@ TexelWeightParams DecodeBlockInfo() { void FillError(ivec3 coord) { for (uint j = 0; j < block_dims.y; j++) { for (uint i = 0; i < block_dims.x; i++) { - imageStore(dest_image, coord + ivec3(i, j, 0), vec4(1.0, 1.0, 0.0, 1.0)); + imageStore(dest_image, coord + ivec3(i, j, 0), vec4(0.0, 0.0, 0.0, 0.0)); } } } diff --git a/src/video_core/host_shaders/opengl_smaa.glsl b/src/video_core/host_shaders/opengl_smaa.glsl new file mode 100644 index 0000000..3cbe87b --- /dev/null +++ b/src/video_core/host_shaders/opengl_smaa.glsl @@ -0,0 +1,1339 @@ +// SPDX-FileCopyrightText: 2013 Jorge Jimenez (jorge@iryoku.com) +// SPDX-FileCopyrightText: 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) +// SPDX-FileCopyrightText: 2013 Belen Masia (bmasia@unizar.es) +// SPDX-FileCopyrightText: 2013 Fernando Navarro (fernandn@microsoft.com) +// SPDX-FileCopyrightText: 2013 Diego Gutierrez (diegog@unizar.es) +// SPDX-License-Identifier: MIT + +/** + * _______ ___ ___ ___ ___ + * / || \/ | / \ / \ + * | (---- | \ / | / ^ \ / ^ \ + * \ \ | |\/| | / /_\ \ / /_\ \ + * ----) | | | | | / _____ \ / _____ \ + * |_______/ |__| |__| /__/ \__\ /__/ \__\ + * + * E N H A N C E D + * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G + * + * http://www.iryoku.com/smaa/ + * + * Hi, welcome aboard! + * + * Here you'll find instructions to get the shader up and running as fast as + * possible. + * + * IMPORTANTE NOTICE: when updating, remember to update both this file and the + * precomputed textures! They may change from version to version. + * + * The shader has three passes, chained together as follows: + * + * |input|------------------+ + * v | + * [ SMAA*EdgeDetection ] | + * v | + * |edgesTex| | + * v | + * [ SMAABlendingWeightCalculation ] | + * v | + * |blendTex| | + * v | + * [ SMAANeighborhoodBlending ] <------+ + * v + * |output| + * + * Note that each [pass] has its own vertex and pixel shader. Remember to use + * oversized triangles instead of quads to avoid overshading along the + * diagonal. + * + * You've three edge detection methods to choose from: luma, color or depth. + * They represent different quality/performance and anti-aliasing/sharpness + * tradeoffs, so our recommendation is for you to choose the one that best + * suits your particular scenario: + * + * - Depth edge detection is usually the fastest but it may miss some edges. + * + * - Luma edge detection is usually more expensive than depth edge detection, + * but catches visible edges that depth edge detection can miss. + * + * - Color edge detection is usually the most expensive one but catches + * chroma-only edges. + * + * For quickstarters: just use luma edge detection. + * + * The general advice is to not rush the integration process and ensure each + * step is done correctly (don't try to integrate SMAA T2x with predicated edge + * detection from the start!). Ok then, let's go! + * + * 1. The first step is to create two RGBA temporal render targets for holding + * |edgesTex| and |blendTex|. + * + * In DX10 or DX11, you can use a RG render target for the edges texture. + * In the case of NVIDIA GPUs, using RG render targets seems to actually be + * slower. + * + * On the Xbox 360, you can use the same render target for resolving both + * |edgesTex| and |blendTex|, as they aren't needed simultaneously. + * + * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared + * each frame. Do not forget to clear the alpha channel! + * + * 3. The next step is loading the two supporting precalculated textures, + * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as + * C++ headers, and also as regular DDS files. They'll be needed for the + * 'SMAABlendingWeightCalculation' pass. + * + * If you use the C++ headers, be sure to load them in the format specified + * inside of them. + * + * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 + * respectively, if you have that option in your content processor pipeline. + * When compressing then, you get a non-perceptible quality decrease, and a + * marginal performance increase. + * + * 4. All samplers must be set to linear filtering and clamp. + * + * After you get the technique working, remember that 64-bit inputs have + * half-rate linear filtering on GCN. + * + * If SMAA is applied to 64-bit color buffers, switching to point filtering + * when accesing them will increase the performance. Search for + * 'SMAASamplePoint' to see which textures may benefit from point + * filtering, and where (which is basically the color input in the edge + * detection and resolve passes). + * + * 5. All texture reads and buffer writes must be non-sRGB, with the exception + * of the input read and the output write in + * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in + * this last pass are not possible, the technique will work anyway, but + * will perform antialiasing in gamma space. + * + * IMPORTANT: for best results the input read for the color/luma edge + * detection should *NOT* be sRGB. + * + * 6. Before including SMAA.h you'll have to setup the render target metrics, + * the target and any optional configuration defines. Optionally you can + * use a preset. + * + * You have the following targets available: + * SMAA_HLSL_3 + * SMAA_HLSL_4 + * SMAA_HLSL_4_1 + * SMAA_GLSL_3 * + * SMAA_GLSL_4 * + * + * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). + * + * And four presets: + * SMAA_PRESET_LOW (%60 of the quality) + * SMAA_PRESET_MEDIUM (%80 of the quality) + * SMAA_PRESET_HIGH (%95 of the quality) + * SMAA_PRESET_ULTRA (%99 of the quality) + * + * For example: + * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) + * #define SMAA_HLSL_4 + * #define SMAA_PRESET_HIGH + * #include "SMAA.h" + * + * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a + * uniform variable. The code is designed to minimize the impact of not + * using a constant value, but it is still better to hardcode it. + * + * Depending on how you encoded 'areaTex' and 'searchTex', you may have to + * add (and customize) the following defines before including SMAA.h: + * #define SMAA_AREATEX_SELECT(sample) sample.rg + * #define SMAA_SEARCHTEX_SELECT(sample) sample.r + * + * If your engine is already using porting macros, you can define + * SMAA_CUSTOM_SL, and define the porting functions by yourself. + * + * 7. Then, you'll have to setup the passes as indicated in the scheme above. + * You can take a look into SMAA.fx, to see how we did it for our demo. + * Checkout the function wrappers, you may want to copy-paste them! + * + * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. + * You can use a screenshot from your engine to compare the |edgesTex| + * and |blendTex| produced inside of the engine with the results obtained + * with the reference demo. + * + * 9. After you get the last pass to work, it's time to optimize. You'll have + * to initialize a stencil buffer in the first pass (discard is already in + * the code), then mask execution by using it the second pass. The last + * pass should be executed in all pixels. + * + * + * After this point you can choose to enable predicated thresholding, + * temporal supersampling and motion blur integration: + * + * a) If you want to use predicated thresholding, take a look into + * SMAA_PREDICATION; you'll need to pass an extra texture in the edge + * detection pass. + * + * b) If you want to enable temporal supersampling (SMAA T2x): + * + * 1. The first step is to render using subpixel jitters. I won't go into + * detail, but it's as simple as moving each vertex position in the + * vertex shader, you can check how we do it in our DX10 demo. + * + * 2. Then, you must setup the temporal resolve. You may want to take a look + * into SMAAResolve for resolving 2x modes. After you get it working, you'll + * probably see ghosting everywhere. But fear not, you can enable the + * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. + * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. + * + * 3. The next step is to apply SMAA to each subpixel jittered frame, just as + * done for 1x. + * + * 4. At this point you should already have something usable, but for best + * results the proper area textures must be set depending on current jitter. + * For this, the parameter 'subsampleIndices' of + * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x + * mode: + * + * @SUBSAMPLE_INDICES + * + * | S# | Camera Jitter | subsampleIndices | + * +----+------------------+---------------------+ + * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | + * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | + * + * These jitter positions assume a bottom-to-top y axis. S# stands for the + * sample number. + * + * More information about temporal supersampling here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * c) If you want to enable spatial multisampling (SMAA S2x): + * + * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be + * created with: + * - DX10: see below (*) + * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or + * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN + * + * This allows to ensure that the subsample order matches the table in + * @SUBSAMPLE_INDICES. + * + * (*) In the case of DX10, we refer the reader to: + * - SMAA::detectMSAAOrder and + * - SMAA::msaaReorder + * + * These functions allow to match the standard multisample patterns by + * detecting the subsample order for a specific GPU, and reordering + * them appropriately. + * + * 2. A shader must be run to output each subsample into a separate buffer + * (DX10 is required). You can use SMAASeparate for this purpose, or just do + * it in an existing pass (for example, in the tone mapping pass, which has + * the advantage of feeding tone mapped subsamples to SMAA, which will yield + * better results). + * + * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing + * the results in the final buffer. The second run should alpha blend with + * the existing final buffer using a blending factor of 0.5. + * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point + * b). + * + * d) If you want to enable temporal supersampling on top of SMAA S2x + * (which actually is SMAA 4x): + * + * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is + * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' + * must be set as follows: + * + * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | + * +----+----+--------------------+-------------------+----------------------+ + * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | + * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | + * +----+----+--------------------+-------------------+----------------------+ + * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | + * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | + * + * These jitter positions assume a bottom-to-top y axis. F# stands for the + * frame number. S# stands for the sample number. + * + * 2. After calculating SMAA S2x for current frame (with the new subsample + * indices), previous frame must be reprojected as in SMAA T2x mode (see + * point b). + * + * e) If motion blur is used, you may want to do the edge detection pass + * together with motion blur. This has two advantages: + * + * 1. Pixels under heavy motion can be omitted from the edge detection process. + * For these pixels we can just store "no edge", as motion blur will take + * care of them. + * 2. The center pixel tap is reused. + * + * Note that in this case depth testing should be used instead of stenciling, + * as we have to write all the pixels in the motion blur pass. + * + * That's it! + */ + +//----------------------------------------------------------------------------- +// SMAA Presets + +/** + * Note that if you use one of these presets, the following configuration + * macros will be ignored if set in the "Configurable Defines" section. + */ + +#if defined(SMAA_PRESET_LOW) +#define SMAA_THRESHOLD 0.15 +#define SMAA_MAX_SEARCH_STEPS 4 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_MEDIUM) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 8 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_HIGH) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 16 +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#define SMAA_CORNER_ROUNDING 25 +#elif defined(SMAA_PRESET_ULTRA) +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 +#endif + +//----------------------------------------------------------------------------- +// Configurable Defines + +/** + * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. + * Lowering this value you will be able to detect more edges at the expense of + * performance. + * + * Range: [0, 0.5] + * 0.1 is a reasonable value, and allows to catch most visible edges. + * 0.05 is a rather overkill value, that allows to catch 'em all. + * + * If temporal supersampling is used, 0.2 could be a reasonable value, as low + * contrast edges are properly filtered by just 2x. + */ +#ifndef SMAA_THRESHOLD +#define SMAA_THRESHOLD 0.1 +#endif + +/** + * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. + * + * Range: depends on the depth range of the scene. + */ +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + +/** + * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the + * horizontal/vertical pattern searches, at each side of the pixel. + * + * In number of pixels, it's actually the double. So the maximum line length + * perfectly handled by, for example 16, is 64 (by perfectly, we meant that + * longer lines won't look as good, but still antialiased). + * + * Range: [0, 112] + */ +#ifndef SMAA_MAX_SEARCH_STEPS +#define SMAA_MAX_SEARCH_STEPS 16 +#endif + +/** + * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the + * diagonal pattern searches, at each side of the pixel. In this case we jump + * one pixel at time, instead of two. + * + * Range: [0, 20] + * + * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 + * steps), but it can have a significant impact on older machines. + * + * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. + */ +#ifndef SMAA_MAX_SEARCH_STEPS_DIAG +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#endif + +/** + * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. + * + * Range: [0, 100] + * + * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. + */ +#ifndef SMAA_CORNER_ROUNDING +#define SMAA_CORNER_ROUNDING 25 +#endif + +/** + * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times + * bigger contrast than current edge, current edge will be discarded. + * + * This allows to eliminate spurious crossing edges, and is based on the fact + * that, if there is too much contrast in a direction, that will hide + * perceptually contrast in the other neighbors. + */ +#ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR +#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 +#endif + +/** + * Predicated thresholding allows to better preserve texture details and to + * improve performance, by decreasing the number of detected edges using an + * additional buffer like the light accumulation buffer, object ids or even the + * depth buffer (the depth buffer usage may be limited to indoor or short range + * scenes). + * + * It locally decreases the luma or color threshold if an edge is found in an + * additional buffer (so the global threshold can be higher). + * + * This method was developed by Playstation EDGE MLAA team, and used in + * Killzone 3, by using the light accumulation buffer. More information here: + * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx + */ +#ifndef SMAA_PREDICATION +#define SMAA_PREDICATION 0 +#endif + +/** + * Threshold to be used in the additional predication buffer. + * + * Range: depends on the input, so you'll have to find the magic number that + * works for you. + */ +#ifndef SMAA_PREDICATION_THRESHOLD +#define SMAA_PREDICATION_THRESHOLD 0.01 +#endif + +/** + * How much to scale the global threshold used for luma or color edge + * detection when using predication. + * + * Range: [1, 5] + */ +#ifndef SMAA_PREDICATION_SCALE +#define SMAA_PREDICATION_SCALE 2.0 +#endif + +/** + * How much to locally decrease the threshold. + * + * Range: [0, 1] + */ +#ifndef SMAA_PREDICATION_STRENGTH +#define SMAA_PREDICATION_STRENGTH 0.4 +#endif + +/** + * Temporal reprojection allows to remove ghosting artifacts when using + * temporal supersampling. We use the CryEngine 3 method which also introduces + * velocity weighting. This feature is of extreme importance for totally + * removing ghosting. More information here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * Note that you'll need to setup a velocity buffer for enabling reprojection. + * For static geometry, saving the previous depth buffer is a viable + * alternative. + */ +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +/** + * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to + * remove ghosting trails behind the moving object, which are not removed by + * just using reprojection. Using low values will exhibit ghosting, while using + * high values will disable temporal supersampling under motion. + * + * Behind the scenes, velocity weighting removes temporal supersampling when + * the velocity of the subsamples differs (meaning they are different objects). + * + * Range: [0, 80] + */ +#ifndef SMAA_REPROJECTION_WEIGHT_SCALE +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 +#endif + +/** + * On some compilers, discard cannot be used in vertex shaders. Thus, they need + * to be compiled separately. + */ +#ifndef SMAA_INCLUDE_VS +#define SMAA_INCLUDE_VS 1 +#endif +#ifndef SMAA_INCLUDE_PS +#define SMAA_INCLUDE_PS 1 +#endif + +//----------------------------------------------------------------------------- +// Texture Access Defines + +#ifndef SMAA_AREATEX_SELECT +#if defined(SMAA_HLSL_3) +#define SMAA_AREATEX_SELECT(sample) sample.ra +#else +#define SMAA_AREATEX_SELECT(sample) sample.rg +#endif +#endif + +#ifndef SMAA_SEARCHTEX_SELECT +#define SMAA_SEARCHTEX_SELECT(sample) sample.r +#endif + +#ifndef SMAA_DECODE_VELOCITY +#define SMAA_DECODE_VELOCITY(sample) sample.rg +#endif + +//----------------------------------------------------------------------------- +// Non-Configurable Defines + +#define SMAA_AREATEX_MAX_DISTANCE 16 +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / float2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) +#define SMAA_SEARCHTEX_SIZE float2(66.0, 33.0) +#define SMAA_SEARCHTEX_PACKED_SIZE float2(64.0, 16.0) +#define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0) + +//----------------------------------------------------------------------------- +// Porting Functions + +#if defined(SMAA_HLSL_3) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) +#define SMAASample(tex, coord) tex2D(tex, coord) +#define SMAASamplePoint(tex, coord) tex2D(tex, coord) +#define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#endif +#if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) +SamplerState LinearSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +SamplerState PointSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +#define SMAATexture2D(tex) Texture2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex.SampleLevel(LinearSampler, coord, 0) +#define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(PointSampler, coord, 0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(LinearSampler, coord, 0, offset) +#define SMAASample(tex, coord) tex.Sample(LinearSampler, coord) +#define SMAASamplePoint(tex, coord) tex.Sample(PointSampler, coord) +#define SMAASampleOffset(tex, coord, offset) tex.Sample(LinearSampler, coord, offset) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#define SMAATexture2DMS2(tex) Texture2DMS tex +#define SMAALoad(tex, pos, sample) tex.Load(pos, sample) +#if defined(SMAA_HLSL_4_1) +#define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0) +#endif +#endif +#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroPoint(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) textureLodOffset(tex, coord, 0.0, offset) +#define SMAASample(tex, coord) texture(tex, coord) +#define SMAASamplePoint(tex, coord) texture(tex, coord) +#define SMAASampleOffset(tex, coord, offset) texture(tex, coord, offset) +#define SMAA_FLATTEN +#define SMAA_BRANCH +#define lerp(a, b, t) mix(a, b, t) +#define saturate(a) clamp(a, 0.0, 1.0) +#if defined(SMAA_GLSL_4) +#define mad(a, b, c) fma(a, b, c) +#define SMAAGather(tex, coord) textureGather(tex, coord) +#else +#define mad(a, b, c) (a * b + c) +#endif +#define float2 vec2 +#define float3 vec3 +#define float4 vec4 +#define int2 ivec2 +#define int3 ivec3 +#define int4 ivec4 +#define bool2 bvec2 +#define bool3 bvec3 +#define bool4 bvec4 +#endif + +#if !defined(SMAA_HLSL_3) && !defined(SMAA_HLSL_4) && !defined(SMAA_HLSL_4_1) && !defined(SMAA_GLSL_3) && !defined(SMAA_GLSL_4) && !defined(SMAA_CUSTOM_SL) +#error you must define the shading language: SMAA_HLSL_*, SMAA_GLSL_* or SMAA_CUSTOM_SL +#endif + +//----------------------------------------------------------------------------- +// Misc functions + +/** + * Gathers current pixel, and the top-left neighbors. + */ +float3 SMAAGatherNeighbours(float2 texcoord, + float4 offset[3], + SMAATexture2D(tex)) { + #ifdef SMAAGather + return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, -0.5)).grb; + #else + float P = SMAASamplePoint(tex, texcoord).r; + float Pleft = SMAASamplePoint(tex, offset[0].xy).r; + float Ptop = SMAASamplePoint(tex, offset[0].zw).r; + return float3(P, Pleft, Ptop); + #endif +} + +/** + * Adjusts the threshold by means of predication. + */ +float2 SMAACalculatePredicatedThreshold(float2 texcoord, + float4 offset[3], + SMAATexture2D(predicationTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(predicationTex)); + float2 delta = abs(neighbours.xx - neighbours.yz); + float2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); + return SMAA_PREDICATION_SCALE * SMAA_THRESHOLD * (1.0 - SMAA_PREDICATION_STRENGTH * edges); +} + +/** + * Conditional move: + */ +void SMAAMovc(bool2 cond, inout float2 variable, float2 value) { + SMAA_FLATTEN if (cond.x) variable.x = value.x; + SMAA_FLATTEN if (cond.y) variable.y = value.y; +} + +void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { + SMAAMovc(cond.xy, variable.xy, value.xy); + SMAAMovc(cond.zw, variable.zw, value.zw); +} + + +#if SMAA_INCLUDE_VS +//----------------------------------------------------------------------------- +// Vertex Shaders + +/** + * Edge Detection Vertex Shader + */ +void SMAAEdgeDetectionVS(float2 texcoord, + out float4 offset[3]) { + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); + offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); +} + +/** + * Blend Weight Calculation Vertex Shader + */ +void SMAABlendingWeightCalculationVS(float2 texcoord, + out float2 pixcoord, + out float4 offset[3]) { + pixcoord = texcoord * SMAA_RT_METRICS.zw; + + // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); + + // And these for the searches, they indicate the ends of the loops: + offset[2] = mad(SMAA_RT_METRICS.xxyy, + float4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), + float4(offset[0].xz, offset[1].yw)); +} + +/** + * Neighborhood Blending Vertex Shader + */ +void SMAANeighborhoodBlendingVS(float2 texcoord, + out float4 offset) { + offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); +} +#endif // SMAA_INCLUDE_VS + +#if SMAA_INCLUDE_PS +//----------------------------------------------------------------------------- +// Edge Detection Pixel Shaders (First Pass) + +/** + * Luma Edge Detection + * + * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAALumaEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate lumas: + float3 weights = float3(0.2126, 0.7152, 0.0722); + float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); + + float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); + float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); + + // We do the usual threshold: + float4 delta; + delta.xy = abs(L - float2(Lleft, Ltop)); + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); + float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); + delta.zw = abs(L - float2(Lright, Lbottom)); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); + float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); + delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Color Edge Detection + * + * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAAColorEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate color deltas: + float4 delta; + float3 C = SMAASamplePoint(colorTex, texcoord).rgb; + + float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; + float3 t = abs(C - Cleft); + delta.x = max(max(t.r, t.g), t.b); + + float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; + t = abs(C - Ctop); + delta.y = max(max(t.r, t.g), t.b); + + // We do the usual threshold: + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; + t = abs(C - Cright); + delta.z = max(max(t.r, t.g), t.b); + + float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; + t = abs(C - Cbottom); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; + t = abs(C - Cleftleft); + delta.z = max(max(t.r, t.g), t.b); + + float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; + t = abs(C - Ctoptop); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Depth Edge Detection + */ +float2 SMAADepthEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(depthTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); + float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); + float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); + + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + return edges; +} + +//----------------------------------------------------------------------------- +// Diagonal Search Functions + +#if !defined(SMAA_DISABLE_DIAG_DETECTION) + +/** + * Allows to decode two binary values from a bilinear-filtered access. + */ +float2 SMAADecodeDiagBilinearAccess(float2 e) { + // Bilinear access for fetching 'e' have a 0.25 offset, and we are + // interested in the R and G edges: + // + // +---G---+-------+ + // | x o R x | + // +-------+-------+ + // + // Then, if one of these edge is enabled: + // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 + // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 + // + // This function will unpack the values (mad + mul + round): + // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 + e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); + return round(e); +} + +float4 SMAADecodeDiagBilinearAccess(float4 e) { + e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); + return round(e); +} + +/** + * These functions allows to perform diagonal pattern searches. + */ +float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + + // @SearchDiag2Optimization + // Fetch both edges at once using bilinear filtering: + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + e = SMAADecodeDiagBilinearAccess(e); + + // Non-optimized version: + // e.g = SMAASampleLevelZero(edgesTex, coord.xy).g; + // e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0)).r; + + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +/** + * Similar to SMAAArea, this calculates the area corresponding to a certain + * diagonal distance and crossing edges 'e'. + */ +float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Diagonal areas are on the second half of the texture: + texcoord.x += 0.5; + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +/** + * This searches for diagonal patterns and returns the corresponding weights. + */ +float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { + float2 weights = float2(0.0, 0.0); + + // Search for the line ends: + float4 d; + float2 end; + if (e.r > 0.0) { + d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); + d.x += float(end.y > 0.9); + } else + d.xz = float2(0.0, 0.0); + d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; + c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); + + // Non-optimized version: + // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + // float4 c; + // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; + // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; + // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; + + // Merge crossing edges at each side into a single value: + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); + } + + // Search for the line ends: + d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); + if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { + d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); + d.y += float(end.y > 0.9); + } else + d.yw = float2(0.0, 0.0); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, -1)).r; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; + } + + return weights; +} +#endif + +//----------------------------------------------------------------------------- +// Horizontal/Vertical Search Functions + +/** + * This allows to determine how much length should we add in the last step + * of the searches. It takes the bilinearly interpolated edge (see + * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and + * crossing edges are active. + */ +float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { + // The texture is flipped vertically, with left and right cases taking half + // of the space horizontally: + float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); + float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); + + // Scale and bias to access texel centers: + scale += float2(-1.0, 1.0); + bias += float2( 0.5, -0.5); + + // Convert from pixel coordinates to texcoords: + // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) + scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + + // Lookup the search texture: + return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias))); +} + +/** + * Horizontal/vertical search functions for the 2nd pass. + */ +float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + /** + * @PSEUDO_GATHER4 + * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to + * sample between edge, thus fetching four edges in a row. + * Sampling with different offsets in each direction allows to disambiguate + * which edges are active from the four fetched ones. + */ + float2 e = float2(0.0, 1.0); + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0), 3.25); + return mad(SMAA_RT_METRICS.x, offset, texcoord.x); + + // Non-optimized version: + // We correct the previous (-0.25, -0.125) offset we applied: + // texcoord.x += 0.25 * SMAA_RT_METRICS.x; + + // The searches are bias by 1, so adjust the coords accordingly: + // texcoord.x += SMAA_RT_METRICS.x; + + // Disambiguate the length added by the last step: + // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step + // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); + // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(0.0, 1.0); + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y > end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.0), 3.25); + return mad(SMAA_RT_METRICS.y, offset, texcoord.y); +} + +float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y < end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.y, offset, texcoord.y); +} + +/** + * Ok, we have the distance and both crossing edges. So, what are the areas + * at each side of current edge? + */ +float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { + // Rounding prevents precision errors of bilinear filtering: + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Move to proper place, according to the subpixel offset: + texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +//----------------------------------------------------------------------------- +// Corner Detection Functions + +void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, -2)).r; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, -2)).r; + + weights *= saturate(factor); + #endif +} + +void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, 1)).g; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, 1)).g; + + weights *= saturate(factor); + #endif +} + +//----------------------------------------------------------------------------- +// Blending Weight Calculation Pixel Shader (Second Pass) + +float4 SMAABlendingWeightCalculationPS(float2 texcoord, + float2 pixcoord, + float4 offset[3], + SMAATexture2D(edgesTex), + SMAATexture2D(areaTex), + SMAATexture2D(searchTex), + float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + float4 weights = float4(0.0, 0.0, 0.0, 0.0); + + float2 e = SMAASample(edgesTex, texcoord).rg; + + SMAA_BRANCH + if (e.g > 0.0) { // Edge at north + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + // Diagonals have both north and west edges, so searching for them in + // one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + SMAA_BRANCH + if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 + #endif + + float2 d; + + // Find the distance to the left: + float3 coords; + coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; + + // Find the distance to the right: + coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); + d.y = coords.z; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the right crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + + // Fix corners: + coords.y = texcoord.y; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + } else + e.r = 0.0; // Skip vertical processing. + #endif + } + + SMAA_BRANCH + if (e.r > 0.0) { // Edge at west + float2 d; + + // Find the distance to the top: + float3 coords; + coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; + d.x = coords.y; + + // Fetch the top crossing edges: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; + + // Find the distance to the bottom: + coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); + d.y = coords.z; + + // We want the distances to be in pixel units: + d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the bottom crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; + + // Get the area for this direction: + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + + // Fix corners: + coords.x = texcoord.x; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + } + + return weights; +} + +//----------------------------------------------------------------------------- +// Neighborhood Blending Pixel Shader (Third Pass) + +float4 SMAANeighborhoodBlendingPS(float2 texcoord, + float4 offset, + SMAATexture2D(colorTex), + SMAATexture2D(blendTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + // Fetch the blending weights for current pixel: + float4 a; + a.x = SMAASample(blendTex, offset.xy).a; // Right + a.y = SMAASample(blendTex, offset.zw).g; // Top + a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left + + // Is there any blending weight with a value greater than 0.0? + SMAA_BRANCH + if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { + float4 color = SMAASampleLevelZero(colorTex, texcoord); + + #if SMAA_REPROJECTION + float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } else { + bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) + + // Calculate the blending offsets: + float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); + float2 blendingWeight = a.yw; + SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); + SMAAMovc(bool2(h, h), blendingWeight, a.xz); + blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); + + // Calculate the texture coordinates: + float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); + + // We exploit bilinear filtering to mix current pixel with the chosen + // neighbor: + float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); + color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); + + #if SMAA_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); + velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } +} + +//----------------------------------------------------------------------------- +// Temporal Resolve Pixel Shader (Optional Pass) + +float4 SMAAResolvePS(float2 texcoord, + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + #if SMAA_REPROJECTION + // Velocity is assumed to be calculated for motion blur, so we need to + // inverse it for reprojection: + float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); + + // Fetch current pixel: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + + // Reproject current coordinates and fetch previous pixel: + float4 previous = SMAASamplePoint(previousColorTex, texcoord + velocity); + + // Attenuate the previous pixel if the velocity is different: + float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; + float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); + + // Blend the pixels according to the calculated weight: + return lerp(current, previous, weight); + #else + // Just blend the pixels: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + float4 previous = SMAASamplePoint(previousColorTex, texcoord); + return lerp(current, previous, 0.5); + #endif +} + +//----------------------------------------------------------------------------- +// Separate Multisamples Pixel Shader (Optional Pass) + +#ifdef SMAALoad +void SMAASeparatePS(float4 position, + float2 texcoord, + out float4 target0, + out float4 target1, + SMAATexture2DMS2(colorTexMS)) { + int2 pos = int2(position.xy); + target0 = SMAALoad(colorTexMS, pos, 0); + target1 = SMAALoad(colorTexMS, pos, 1); +} +#endif + +//----------------------------------------------------------------------------- +#endif // SMAA_INCLUDE_PS diff --git a/src/video_core/host_shaders/smaa_blending_weight_calculation.frag b/src/video_core/host_shaders/smaa_blending_weight_calculation.frag new file mode 100644 index 0000000..c2385a0 --- /dev/null +++ b/src/video_core/host_shaders/smaa_blending_weight_calculation.frag @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 + +#extension GL_GOOGLE_include_directive : enable + +layout (binding = 0) uniform sampler2D edges_tex; +layout (binding = 1) uniform sampler2D area_tex; +layout (binding = 2) uniform sampler2D search_tex; + +layout (location = 0) in vec2 tex_coord; +layout (location = 1) in vec2 pix_coord; +layout (location = 2) in vec4 offset[3]; + +layout (location = 0) out vec4 frag_color; + +vec4 metrics = vec4(1.0 / textureSize(edges_tex, 0), textureSize(edges_tex, 0)); +#define SMAA_RT_METRICS metrics +#define SMAA_GLSL_4 +#define SMAA_PRESET_ULTRA +#define SMAA_INCLUDE_VS 0 +#define SMAA_INCLUDE_PS 1 + +#include "opengl_smaa.glsl" + +void main() { + frag_color = SMAABlendingWeightCalculationPS(tex_coord, + pix_coord, + offset, + edges_tex, + area_tex, + search_tex, + vec4(0) + ); +} diff --git a/src/video_core/host_shaders/smaa_blending_weight_calculation.vert b/src/video_core/host_shaders/smaa_blending_weight_calculation.vert new file mode 100644 index 0000000..25d8d93 --- /dev/null +++ b/src/video_core/host_shaders/smaa_blending_weight_calculation.vert @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 + +#extension GL_GOOGLE_include_directive : enable + +#ifdef VULKAN +#define VERTEX_ID gl_VertexIndex +#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv +#define VERTEX_ID gl_VertexID +#endif + +out gl_PerVertex { + vec4 gl_Position; +}; + +const vec2 vertices[3] = + vec2[3](vec2(-1,-1), vec2(3,-1), vec2(-1, 3)); + +layout (binding = 0) uniform sampler2D edges_tex; +layout (binding = 1) uniform sampler2D area_tex; +layout (binding = 2) uniform sampler2D search_tex; + +layout (location = 0) out vec2 tex_coord; +layout (location = 1) out vec2 pix_coord; +layout (location = 2) out vec4 offset[3]; + +vec4 metrics = vec4(1.0 / textureSize(edges_tex, 0), textureSize(edges_tex, 0)); +#define SMAA_RT_METRICS metrics +#define SMAA_GLSL_4 +#define SMAA_PRESET_ULTRA +#define SMAA_INCLUDE_VS 1 +#define SMAA_INCLUDE_PS 0 + +#include "opengl_smaa.glsl" + +void main() { + vec2 vertex = vertices[VERTEX_ID]; + gl_Position = vec4(vertex, 0.0, 1.0); + tex_coord = (vertex + 1.0) / 2.0; + SMAABlendingWeightCalculationVS(tex_coord, pix_coord, offset); +} diff --git a/src/video_core/host_shaders/smaa_edge_detection.frag b/src/video_core/host_shaders/smaa_edge_detection.frag new file mode 100644 index 0000000..0c768d2 --- /dev/null +++ b/src/video_core/host_shaders/smaa_edge_detection.frag @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 + +#extension GL_GOOGLE_include_directive : enable + +layout (binding = 0) uniform sampler2D input_tex; + +layout (location = 0) in vec2 tex_coord; +layout (location = 1) in vec4 offset[3]; + +layout (location = 0) out vec2 frag_color; + +vec4 metrics = vec4(1.0 / textureSize(input_tex, 0), textureSize(input_tex, 0)); +#define SMAA_RT_METRICS metrics +#define SMAA_GLSL_4 +#define SMAA_PRESET_ULTRA +#define SMAA_INCLUDE_VS 0 +#define SMAA_INCLUDE_PS 1 + +#include "opengl_smaa.glsl" + +void main() { + frag_color = SMAAColorEdgeDetectionPS(tex_coord, offset, input_tex); +} diff --git a/src/video_core/host_shaders/smaa_edge_detection.vert b/src/video_core/host_shaders/smaa_edge_detection.vert new file mode 100644 index 0000000..634a7bb --- /dev/null +++ b/src/video_core/host_shaders/smaa_edge_detection.vert @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 + +#extension GL_GOOGLE_include_directive : enable + +#ifdef VULKAN +#define VERTEX_ID gl_VertexIndex +#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv +#define VERTEX_ID gl_VertexID +#endif + +out gl_PerVertex { + vec4 gl_Position; +}; + +const vec2 vertices[3] = + vec2[3](vec2(-1,-1), vec2(3,-1), vec2(-1, 3)); + +layout (binding = 0) uniform sampler2D input_tex; + +layout (location = 0) out vec2 tex_coord; +layout (location = 1) out vec4 offset[3]; + +vec4 metrics = vec4(1.0 / textureSize(input_tex, 0), textureSize(input_tex, 0)); +#define SMAA_RT_METRICS metrics +#define SMAA_GLSL_4 +#define SMAA_PRESET_ULTRA +#define SMAA_INCLUDE_VS 1 +#define SMAA_INCLUDE_PS 0 + +#include "opengl_smaa.glsl" + +void main() { + vec2 vertex = vertices[VERTEX_ID]; + gl_Position = vec4(vertex, 0.0, 1.0); + tex_coord = (vertex + 1.0) / 2.0; + SMAAEdgeDetectionVS(tex_coord, offset); +} diff --git a/src/video_core/host_shaders/smaa_neighborhood_blending.frag b/src/video_core/host_shaders/smaa_neighborhood_blending.frag new file mode 100644 index 0000000..e3de6e2 --- /dev/null +++ b/src/video_core/host_shaders/smaa_neighborhood_blending.frag @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 + +#extension GL_GOOGLE_include_directive : enable + +layout (binding = 0) uniform sampler2D input_tex; +layout (binding = 1) uniform sampler2D blend_tex; + +layout (location = 0) in vec2 tex_coord; +layout (location = 1) in vec4 offset; + +layout (location = 0) out vec4 frag_color; + +vec4 metrics = vec4(1.0 / textureSize(input_tex, 0), textureSize(input_tex, 0)); +#define SMAA_RT_METRICS metrics +#define SMAA_GLSL_4 +#define SMAA_PRESET_ULTRA +#define SMAA_INCLUDE_VS 0 +#define SMAA_INCLUDE_PS 1 + +#include "opengl_smaa.glsl" + +void main() { + frag_color = SMAANeighborhoodBlendingPS(tex_coord, + offset, + input_tex, + blend_tex + ); +} diff --git a/src/video_core/host_shaders/smaa_neighborhood_blending.vert b/src/video_core/host_shaders/smaa_neighborhood_blending.vert new file mode 100644 index 0000000..007dcdd --- /dev/null +++ b/src/video_core/host_shaders/smaa_neighborhood_blending.vert @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 + +#extension GL_GOOGLE_include_directive : enable + +#ifdef VULKAN +#define VERTEX_ID gl_VertexIndex +#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv +#define VERTEX_ID gl_VertexID +#endif + +out gl_PerVertex { + vec4 gl_Position; +}; + +const vec2 vertices[3] = + vec2[3](vec2(-1,-1), vec2(3,-1), vec2(-1, 3)); + +layout (binding = 0) uniform sampler2D input_tex; +layout (binding = 1) uniform sampler2D blend_tex; + +layout (location = 0) out vec2 tex_coord; +layout (location = 1) out vec4 offset; + +vec4 metrics = vec4(1.0 / textureSize(input_tex, 0), textureSize(input_tex, 0)); +#define SMAA_RT_METRICS metrics +#define SMAA_GLSL_4 +#define SMAA_PRESET_ULTRA +#define SMAA_INCLUDE_VS 1 +#define SMAA_INCLUDE_PS 0 + +#include "opengl_smaa.glsl" + +void main() { + vec2 vertex = vertices[VERTEX_ID]; + gl_Position = vec4(vertex, 0.0, 1.0); + tex_coord = (vertex + 1.0) / 2.0; + SMAANeighborhoodBlendingVS(tex_coord, offset); +} diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index 43f8b59..505d81c 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp @@ -8,6 +8,7 @@ #include +#include #include "common/assert.h" #include "common/fs/fs.h" #include "common/fs/path_util.h" @@ -15,7 +16,10 @@ #include "video_core/macro/macro.h" #include "video_core/macro/macro_hle.h" #include "video_core/macro/macro_interpreter.h" + +#ifdef ARCHITECTURE_x86_64 #include "video_core/macro/macro_jit_x64.h" +#endif namespace Tegra { diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 17ecbca..8549db2 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp @@ -5,6 +5,7 @@ #include #include "common/scope_exit.h" #include "video_core/dirty_flags.h" +#include "video_core/engines/draw_manager.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/macro/macro.h" #include "video_core/macro/macro_hle.h" @@ -18,84 +19,46 @@ using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector& parameters) { const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B); - - maxwell3d.regs.draw.topology.Assign( - static_cast(parameters[0] & 0x3ffffff)); - maxwell3d.regs.vb_base_instance = parameters[5]; - maxwell3d.mme_draw.instance_count = instance_count; - maxwell3d.regs.vb_element_base = parameters[3]; - maxwell3d.regs.index_array.count = parameters[1]; - maxwell3d.regs.index_array.first = parameters[4]; - - if (maxwell3d.ShouldExecute()) { - maxwell3d.Rasterizer().Draw(true, true); - } - maxwell3d.regs.index_array.count = 0; - maxwell3d.mme_draw.instance_count = 0; - maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; + maxwell3d.draw_manager->DrawIndex( + static_cast(parameters[0] & 0x3ffffff), + parameters[4], parameters[1], parameters[3], parameters[5], instance_count); } void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector& parameters) { - const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); - - maxwell3d.regs.vertex_buffer.first = parameters[3]; - maxwell3d.regs.vertex_buffer.count = parameters[1]; - maxwell3d.regs.vb_base_instance = parameters[4]; - maxwell3d.regs.draw.topology.Assign( - static_cast(parameters[0])); - maxwell3d.mme_draw.instance_count = count; - - if (maxwell3d.ShouldExecute()) { - maxwell3d.Rasterizer().Draw(false, true); - } - maxwell3d.regs.vertex_buffer.count = 0; - maxwell3d.mme_draw.instance_count = 0; - maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; + const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); + maxwell3d.draw_manager->DrawArray( + static_cast(parameters[0]), + parameters[3], parameters[1], parameters[4], instance_count); } void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector& parameters) { const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); const u32 element_base = parameters[4]; const u32 base_instance = parameters[5]; - maxwell3d.regs.index_array.first = parameters[3]; - maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base? - maxwell3d.regs.index_array.count = parameters[1]; + maxwell3d.regs.vertex_id_base = element_base; maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; - maxwell3d.regs.vb_element_base = element_base; - maxwell3d.regs.vb_base_instance = base_instance; - maxwell3d.mme_draw.instance_count = instance_count; - maxwell3d.CallMethodFromMME(0x8e3, 0x640); - maxwell3d.CallMethodFromMME(0x8e4, element_base); - maxwell3d.CallMethodFromMME(0x8e5, base_instance); - maxwell3d.regs.draw.topology.Assign( - static_cast(parameters[0])); - if (maxwell3d.ShouldExecute()) { - maxwell3d.Rasterizer().Draw(true, true); - } - maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? - maxwell3d.regs.index_array.count = 0; - maxwell3d.regs.vb_element_base = 0x0; - maxwell3d.regs.vb_base_instance = 0x0; - maxwell3d.mme_draw.instance_count = 0; - maxwell3d.CallMethodFromMME(0x8e3, 0x640); - maxwell3d.CallMethodFromMME(0x8e4, 0x0); - maxwell3d.CallMethodFromMME(0x8e5, 0x0); - maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; + maxwell3d.CallMethod(0x8e3, 0x640, true); + maxwell3d.CallMethod(0x8e4, element_base, true); + maxwell3d.CallMethod(0x8e5, base_instance, true); + + maxwell3d.draw_manager->DrawIndex( + static_cast(parameters[0]), + parameters[3], parameters[1], element_base, base_instance, instance_count); + + maxwell3d.regs.vertex_id_base = 0x0; + maxwell3d.CallMethod(0x8e3, 0x640, true); + maxwell3d.CallMethod(0x8e4, 0x0, true); + maxwell3d.CallMethod(0x8e5, 0x0, true); } // Multidraw Indirect -void HLE_3f5e74b9c9a50164(Engines::Maxwell3D& maxwell3d, const std::vector& parameters) { +void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector& parameters) { SCOPE_EXIT({ // Clean everything. - maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? - maxwell3d.regs.index_array.count = 0; - maxwell3d.regs.vb_element_base = 0x0; - maxwell3d.regs.vb_base_instance = 0x0; - maxwell3d.mme_draw.instance_count = 0; - maxwell3d.CallMethodFromMME(0x8e3, 0x640); - maxwell3d.CallMethodFromMME(0x8e4, 0x0); - maxwell3d.CallMethodFromMME(0x8e5, 0x0); - maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; + maxwell3d.regs.vertex_id_base = 0x0; + maxwell3d.CallMethod(0x8e3, 0x640, true); + maxwell3d.CallMethod(0x8e4, 0x0, true); + maxwell3d.CallMethod(0x8e5, 0x0, true); maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; }); const u32 start_indirect = parameters[0]; @@ -104,9 +67,6 @@ void HLE_3f5e74b9c9a50164(Engines::Maxwell3D& maxwell3d, const std::vector& // Nothing to do. return; } - const auto topology = - static_cast(parameters[2]); - maxwell3d.regs.draw.topology.Assign(topology); const u32 padding = parameters[3]; const std::size_t max_draws = parameters[4]; @@ -117,33 +77,39 @@ void HLE_3f5e74b9c9a50164(Engines::Maxwell3D& maxwell3d, const std::vector& for (std::size_t index = first_draw; index < last_draw; index++) { const std::size_t base = index * indirect_words + 5; - const u32 num_vertices = parameters[base]; - const u32 instance_count = parameters[base + 1]; - const u32 first_index = parameters[base + 2]; const u32 base_vertex = parameters[base + 3]; const u32 base_instance = parameters[base + 4]; - maxwell3d.regs.index_array.first = first_index; - maxwell3d.regs.reg_array[0x446] = base_vertex; - maxwell3d.regs.index_array.count = num_vertices; - maxwell3d.regs.vb_element_base = base_vertex; - maxwell3d.regs.vb_base_instance = base_instance; - maxwell3d.mme_draw.instance_count = instance_count; - maxwell3d.CallMethodFromMME(0x8e3, 0x640); - maxwell3d.CallMethodFromMME(0x8e4, base_vertex); - maxwell3d.CallMethodFromMME(0x8e5, base_instance); + maxwell3d.regs.vertex_id_base = base_vertex; + maxwell3d.CallMethod(0x8e3, 0x640, true); + maxwell3d.CallMethod(0x8e4, base_vertex, true); + maxwell3d.CallMethod(0x8e5, base_instance, true); maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; - if (maxwell3d.ShouldExecute()) { - maxwell3d.Rasterizer().Draw(true, true); - } - maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; + maxwell3d.draw_manager->DrawIndex( + static_cast(parameters[2]), + parameters[base + 2], parameters[base], base_vertex, base_instance, + parameters[base + 1]); } } -constexpr std::array, 4> hle_funcs{{ +// Multi-layer Clear +void HLE_EAD26C3E2109B06B(Engines::Maxwell3D& maxwell3d, const std::vector& parameters) { + ASSERT(parameters.size() == 1); + + const Engines::Maxwell3D::Regs::ClearSurface clear_params{parameters[0]}; + const u32 rt_index = clear_params.RT; + const u32 num_layers = maxwell3d.regs.rt[rt_index].depth; + ASSERT(clear_params.layer == 0); + + maxwell3d.regs.clear_surface.raw = clear_params.raw; + maxwell3d.draw_manager->Clear(num_layers); +} + +constexpr std::array, 5> hle_funcs{{ {0x771BB18C62444DA0, &HLE_771BB18C62444DA0}, {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD}, {0x0217920100488FF7, &HLE_0217920100488FF7}, - {0x3f5e74b9c9a50164, &HLE_3f5e74b9c9a50164}, + {0x3F5E74B9C9A50164, &HLE_3F5E74B9C9A50164}, + {0xEAD26C3E2109B06B, &HLE_EAD26C3E2109B06B}, }}; class HLEMacroImpl final : public CachedMacro { diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp index f670b1b..0d63495 100644 --- a/src/video_core/macro/macro_interpreter.cpp +++ b/src/video_core/macro/macro_interpreter.cpp @@ -201,6 +201,7 @@ bool MacroInterpreterImpl::Step(bool is_delay_slot) { } default: UNIMPLEMENTED_MSG("Unimplemented macro operation {}", opcode.operation.Value()); + break; } // An instruction with the Exit flag will not actually @@ -297,6 +298,7 @@ void MacroInterpreterImpl::ProcessResult(Macro::ResultOperation operation, u32 r break; default: UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation); + break; } } @@ -335,7 +337,7 @@ void MacroInterpreterImpl::SetMethodAddress(u32 address) { } void MacroInterpreterImpl::Send(u32 value) { - maxwell3d.CallMethodFromMME(method_address.address, value); + maxwell3d.CallMethod(method_address.address, value, true); // Increment the method address by the method increment. method_address.address.Assign(method_address.address.Value() + method_address.increment.Value()); diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp index aca25d9..7347cbd 100644 --- a/src/video_core/macro/macro_jit_x64.cpp +++ b/src/video_core/macro/macro_jit_x64.cpp @@ -279,28 +279,13 @@ void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) { auto dst = Compile_GetRegister(opcode.src_a, RESULT); auto src = Compile_GetRegister(opcode.src_b, eax); - if (opcode.bf_src_bit != 0 && opcode.bf_src_bit != 31) { - shr(src, opcode.bf_src_bit); - } else if (opcode.bf_src_bit == 31) { - xor_(src, src); - } - // Don't bother masking the whole register since we're using a 32 bit register - if (opcode.bf_size != 31 && opcode.bf_size != 0) { - and_(src, opcode.GetBitfieldMask()); - } else if (opcode.bf_size == 0) { - xor_(src, src); - } - if (opcode.bf_dst_bit != 31 && opcode.bf_dst_bit != 0) { - shl(src, opcode.bf_dst_bit); - } else if (opcode.bf_dst_bit == 31) { - xor_(src, src); - } - const u32 mask = ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit); - if (mask != 0xffffffff) { - and_(dst, mask); - } + and_(dst, mask); + shr(src, opcode.bf_src_bit); + and_(src, opcode.GetBitfieldMask()); + shl(src, opcode.bf_dst_bit); or_(dst, src); + Compile_ProcessResult(opcode.result_operation, opcode.dst); } @@ -309,17 +294,9 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) { const auto src = Compile_GetRegister(opcode.src_b, RESULT); shr(src, dst.cvt8()); - if (opcode.bf_size != 0 && opcode.bf_size != 31) { - and_(src, opcode.GetBitfieldMask()); - } else if (opcode.bf_size == 0) { - xor_(src, src); - } + and_(src, opcode.GetBitfieldMask()); + shl(src, opcode.bf_dst_bit); - if (opcode.bf_dst_bit != 0 && opcode.bf_dst_bit != 31) { - shl(src, opcode.bf_dst_bit); - } else if (opcode.bf_dst_bit == 31) { - xor_(src, src); - } Compile_ProcessResult(opcode.result_operation, opcode.dst); } @@ -327,13 +304,8 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) { const auto dst = Compile_GetRegister(opcode.src_a, ecx); const auto src = Compile_GetRegister(opcode.src_b, RESULT); - if (opcode.bf_src_bit != 0) { - shr(src, opcode.bf_src_bit); - } - - if (opcode.bf_size != 31) { - and_(src, opcode.GetBitfieldMask()); - } + shr(src, opcode.bf_src_bit); + and_(src, opcode.GetBitfieldMask()); shl(src, dst.cvt8()); Compile_ProcessResult(opcode.result_operation, opcode.dst); @@ -374,7 +346,7 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) { } void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { - maxwell3d->CallMethodFromMME(method_address.address, value); + maxwell3d->CallMethod(method_address.address, value, true); } void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { @@ -429,17 +401,11 @@ void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) { Xbyak::Label handle_post_exit{}; Xbyak::Label skip{}; jmp(skip, T_NEAR); - if (opcode.is_exit) { - L(handle_post_exit); - // Execute 1 instruction - mov(BRANCH_HOLDER, end_of_code); - // Jump to next instruction to skip delay slot check - jmp(labels[jump_address], T_NEAR); - } else { - L(handle_post_exit); - xor_(BRANCH_HOLDER, BRANCH_HOLDER); - jmp(labels[jump_address], T_NEAR); - } + + L(handle_post_exit); + xor_(BRANCH_HOLDER, BRANCH_HOLDER); + jmp(labels[jump_address], T_NEAR); + L(skip); mov(BRANCH_HOLDER, handle_post_exit); jmp(delay_skip[pc], T_NEAR); @@ -686,6 +652,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3 break; default: UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation); + break; } } diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index bf9eb73..8c8dfcc 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/device_memory.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/memory.h" @@ -16,59 +17,208 @@ namespace Tegra { -MemoryManager::MemoryManager(Core::System& system_) - : system{system_}, page_table(page_table_size) {} +std::atomic MemoryManager::unique_identifier_generator{}; + +MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 big_page_bits_, + u64 page_bits_) + : system{system_}, memory{system.Memory()}, device_memory{system.DeviceMemory()}, + address_space_bits{address_space_bits_}, page_bits{page_bits_}, big_page_bits{big_page_bits_}, + entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38, + page_bits != big_page_bits ? page_bits : 0}, + unique_identifier{unique_identifier_generator.fetch_add(1, std::memory_order_acq_rel)} { + address_space_size = 1ULL << address_space_bits; + page_size = 1ULL << page_bits; + page_mask = page_size - 1ULL; + big_page_size = 1ULL << big_page_bits; + big_page_mask = big_page_size - 1ULL; + const u64 page_table_bits = address_space_bits - page_bits; + const u64 big_page_table_bits = address_space_bits - big_page_bits; + const u64 page_table_size = 1ULL << page_table_bits; + const u64 big_page_table_size = 1ULL << big_page_table_bits; + page_table_mask = page_table_size - 1; + big_page_table_mask = big_page_table_size - 1; + + big_entries.resize(big_page_table_size / 32, 0); + big_page_table_cpu.resize(big_page_table_size); + big_page_continous.resize(big_page_table_size / continous_bits, 0); + std::array kind_valus; + kind_valus.fill(PTEKind::INVALID); + big_kinds.resize(big_page_table_size / 32, kind_valus); + entries.resize(page_table_size / 32, 0); + kinds.resize(page_table_size / 32, kind_valus); +} MemoryManager::~MemoryManager() = default; -void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { - rasterizer = rasterizer_; +template +MemoryManager::EntryType MemoryManager::GetEntry(size_t position) const { + if constexpr (is_big_page) { + position = position >> big_page_bits; + const u64 entry_mask = big_entries[position / 32]; + const size_t sub_index = position % 32; + return static_cast((entry_mask >> (2 * sub_index)) & 0x03ULL); + } else { + position = position >> page_bits; + const u64 entry_mask = entries[position / 32]; + const size_t sub_index = position % 32; + return static_cast((entry_mask >> (2 * sub_index)) & 0x03ULL); + } } -GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) { - u64 remaining_size{size}; +template +void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) { + if constexpr (is_big_page) { + position = position >> big_page_bits; + const u64 entry_mask = big_entries[position / 32]; + const size_t sub_index = position % 32; + big_entries[position / 32] = + (~(3ULL << sub_index * 2) & entry_mask) | (static_cast(entry) << sub_index * 2); + } else { + position = position >> page_bits; + const u64 entry_mask = entries[position / 32]; + const size_t sub_index = position % 32; + entries[position / 32] = + (~(3ULL << sub_index * 2) & entry_mask) | (static_cast(entry) << sub_index * 2); + } +} + +PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const { + auto entry = GetEntry(gpu_addr); + if (entry == EntryType::Mapped || entry == EntryType::Reserved) [[likely]] { + return GetKind(gpu_addr); + } else { + return GetKind(gpu_addr); + } +} + +template +PTEKind MemoryManager::GetKind(size_t position) const { + if constexpr (is_big_page) { + position = position >> big_page_bits; + const size_t sub_index = position % 32; + return big_kinds[position / 32][sub_index]; + } else { + position = position >> page_bits; + const size_t sub_index = position % 32; + return kinds[position / 32][sub_index]; + } +} + +template +void MemoryManager::SetKind(size_t position, PTEKind kind) { + if constexpr (is_big_page) { + position = position >> big_page_bits; + const size_t sub_index = position % 32; + big_kinds[position / 32][sub_index] = kind; + } else { + position = position >> page_bits; + const size_t sub_index = position % 32; + kinds[position / 32][sub_index] = kind; + } +} + +inline bool MemoryManager::IsBigPageContinous(size_t big_page_index) const { + const u64 entry_mask = big_page_continous[big_page_index / continous_bits]; + const size_t sub_index = big_page_index % continous_bits; + return ((entry_mask >> sub_index) & 0x1ULL) != 0; +} + +inline void MemoryManager::SetBigPageContinous(size_t big_page_index, bool value) { + const u64 continous_mask = big_page_continous[big_page_index / continous_bits]; + const size_t sub_index = big_page_index % continous_bits; + big_page_continous[big_page_index / continous_bits] = + (~(1ULL << sub_index) & continous_mask) | (value ? 1ULL << sub_index : 0); +} + +template +GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size, + PTEKind kind) { + [[maybe_unused]] u64 remaining_size{size}; + if constexpr (entry_type == EntryType::Mapped) { + page_table.ReserveRange(gpu_addr, size); + } for (u64 offset{}; offset < size; offset += page_size) { - if (remaining_size < page_size) { - SetPageEntry(gpu_addr + offset, page_entry + offset, remaining_size); - } else { - SetPageEntry(gpu_addr + offset, page_entry + offset); + const GPUVAddr current_gpu_addr = gpu_addr + offset; + [[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr); + SetEntry(current_gpu_addr, entry_type); + SetKind(current_gpu_addr, kind); + if (current_entry_type != entry_type) { + rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, page_size); + } + if constexpr (entry_type == EntryType::Mapped) { + const VAddr current_cpu_addr = cpu_addr + offset; + const auto index = PageEntryIndex(current_gpu_addr); + const u32 sub_value = static_cast(current_cpu_addr >> cpu_page_bits); + page_table[index] = sub_value; } remaining_size -= page_size; } return gpu_addr; } -GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) { - const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first); - if (it != map_ranges.end() && it->first == gpu_addr) { - it->second = size; - } else { - map_ranges.insert(it, MapRange{gpu_addr, size}); +template +GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, + size_t size, PTEKind kind) { + [[maybe_unused]] u64 remaining_size{size}; + for (u64 offset{}; offset < size; offset += big_page_size) { + const GPUVAddr current_gpu_addr = gpu_addr + offset; + [[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr); + SetEntry(current_gpu_addr, entry_type); + SetKind(current_gpu_addr, kind); + if (current_entry_type != entry_type) { + rasterizer->ModifyGPUMemory(unique_identifier, gpu_addr, big_page_size); + } + if constexpr (entry_type == EntryType::Mapped) { + const VAddr current_cpu_addr = cpu_addr + offset; + const auto index = PageEntryIndex(current_gpu_addr); + const u32 sub_value = static_cast(current_cpu_addr >> cpu_page_bits); + big_page_table_cpu[index] = sub_value; + const bool is_continous = ([&] { + uintptr_t base_ptr{ + reinterpret_cast(memory.GetPointerSilent(current_cpu_addr))}; + if (base_ptr == 0) { + return false; + } + for (VAddr start_cpu = current_cpu_addr + page_size; + start_cpu < current_cpu_addr + big_page_size; start_cpu += page_size) { + base_ptr += page_size; + auto next_ptr = reinterpret_cast(memory.GetPointerSilent(start_cpu)); + if (next_ptr == 0 || base_ptr != next_ptr) { + return false; + } + } + return true; + })(); + SetBigPageContinous(index, is_continous); + } + remaining_size -= big_page_size; } - return UpdateRange(gpu_addr, cpu_addr, size); + return gpu_addr; } -GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) { - return Map(cpu_addr, *FindFreeRange(size, align), size); +void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { + rasterizer = rasterizer_; } -GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) { - const std::optional gpu_addr = FindFreeRange(size, 1, true); - ASSERT(gpu_addr); - return Map(cpu_addr, *gpu_addr, size); +GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, PTEKind kind, + bool is_big_pages) { + if (is_big_pages) [[likely]] { + return BigPageTableOp(gpu_addr, cpu_addr, size, kind); + } + return PageTableOp(gpu_addr, cpu_addr, size, kind); +} + +GPUVAddr MemoryManager::MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages) { + if (is_big_pages) [[likely]] { + return BigPageTableOp(gpu_addr, 0, size, PTEKind::INVALID); + } + return PageTableOp(gpu_addr, 0, size, PTEKind::INVALID); } void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { if (size == 0) { return; } - const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first); - if (it != map_ranges.end()) { - ASSERT(it->first == gpu_addr); - map_ranges.erase(it); - } else { - ASSERT_MSG(false, "Unmapping non-existent GPU address=0x{:x}", gpu_addr); - } const auto submapped_ranges = GetSubmappedRange(gpu_addr, size); for (const auto& [map_addr, map_size] : submapped_ranges) { @@ -79,109 +229,27 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { rasterizer->UnmapMemory(*cpu_addr, map_size); } - UpdateRange(gpu_addr, PageEntry::State::Unmapped, size); -} - -std::optional MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) { - for (u64 offset{}; offset < size; offset += page_size) { - if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) { - return std::nullopt; - } - } - - return UpdateRange(gpu_addr, PageEntry::State::Allocated, size); -} - -GPUVAddr MemoryManager::Allocate(std::size_t size, std::size_t align) { - return *AllocateFixed(*FindFreeRange(size, align), size); -} - -void MemoryManager::TryLockPage(PageEntry page_entry, std::size_t size) { - if (!page_entry.IsValid()) { - return; - } - - ASSERT(system.CurrentProcess() - ->PageTable() - .LockForDeviceAddressSpace(page_entry.ToAddress(), size) - .IsSuccess()); -} - -void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) { - if (!page_entry.IsValid()) { - return; - } - - ASSERT(system.CurrentProcess() - ->PageTable() - .UnlockForDeviceAddressSpace(page_entry.ToAddress(), size) - .IsSuccess()); -} - -PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const { - return page_table[PageEntryIndex(gpu_addr)]; -} - -void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) { - // TODO(bunnei): We should lock/unlock device regions. This currently causes issues due to - // improper tracking, but should be fixed in the future. - - //// Unlock the old page - // TryUnlockPage(page_table[PageEntryIndex(gpu_addr)], size); - - //// Lock the new page - // TryLockPage(page_entry, size); - auto& current_page = page_table[PageEntryIndex(gpu_addr)]; - - if ((!current_page.IsValid() && page_entry.IsValid()) || - current_page.ToAddress() != page_entry.ToAddress()) { - rasterizer->ModifyGPUMemory(gpu_addr, size); - } - - current_page = page_entry; -} - -std::optional MemoryManager::FindFreeRange(std::size_t size, std::size_t align, - bool start_32bit_address) const { - if (!align) { - align = page_size; - } else { - align = Common::AlignUp(align, page_size); - } - - u64 available_size{}; - GPUVAddr gpu_addr{start_32bit_address ? address_space_start_low : address_space_start}; - while (gpu_addr + available_size < address_space_size) { - if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) { - available_size += page_size; - - if (available_size >= size) { - return gpu_addr; - } - } else { - gpu_addr += available_size + page_size; - available_size = 0; - - const auto remainder{gpu_addr % align}; - if (remainder) { - gpu_addr = (gpu_addr - remainder) + align; - } - } - } - - return std::nullopt; + BigPageTableOp(gpu_addr, 0, size, PTEKind::INVALID); + PageTableOp(gpu_addr, 0, size, PTEKind::INVALID); } std::optional MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { - if (gpu_addr == 0) { + if (!IsWithinGPUAddressRange(gpu_addr)) [[unlikely]] { return std::nullopt; } - const auto page_entry{GetPageEntry(gpu_addr)}; - if (!page_entry.IsValid()) { - return std::nullopt; + if (GetEntry(gpu_addr) != EntryType::Mapped) [[unlikely]] { + if (GetEntry(gpu_addr) != EntryType::Mapped) { + return std::nullopt; + } + + const VAddr cpu_addr_base = static_cast(page_table[PageEntryIndex(gpu_addr)]) + << cpu_page_bits; + return cpu_addr_base + (gpu_addr & page_mask); } - return page_entry.ToAddress() + (gpu_addr & page_mask); + const VAddr cpu_addr_base = + static_cast(big_page_table_cpu[PageEntryIndex(gpu_addr)]) << cpu_page_bits; + return cpu_addr_base + (gpu_addr & big_page_mask); } std::optional MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t size) const { @@ -189,7 +257,7 @@ std::optional MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t s const size_t page_last{(addr + size + page_size - 1) >> page_bits}; while (page_index < page_last) { const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (page_addr && *page_addr != 0) { + if (page_addr) { return page_addr; } ++page_index; @@ -232,126 +300,298 @@ template void MemoryManager::Write(GPUVAddr addr, u32 data); template void MemoryManager::Write(GPUVAddr addr, u64 data); u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) { - if (!GetPageEntry(gpu_addr).IsValid()) { - return {}; - } - const auto address{GpuToCpuAddress(gpu_addr)}; if (!address) { return {}; } - return system.Memory().GetPointer(*address); + return memory.GetPointer(*address); } const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const { - if (!GetPageEntry(gpu_addr).IsValid()) { - return {}; - } - const auto address{GpuToCpuAddress(gpu_addr)}; if (!address) { return {}; } - return system.Memory().GetPointer(*address); + return memory.GetPointer(*address); } -size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept { - auto it = std::ranges::upper_bound(map_ranges, gpu_addr, {}, &MapRange::first); - --it; - return it->second - (gpu_addr - it->first); -} +#ifdef _MSC_VER // no need for gcc / clang but msvc's compiler is more conservative with inlining. +#pragma inline_recursion(on) +#endif -void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, - bool is_safe) const { +template +inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, + FuncMapped&& func_mapped, FuncReserved&& func_reserved, + FuncUnmapped&& func_unmapped) const { + static constexpr bool BOOL_BREAK_MAPPED = std::is_same_v; + static constexpr bool BOOL_BREAK_RESERVED = std::is_same_v; + static constexpr bool BOOL_BREAK_UNMAPPED = std::is_same_v; + u64 used_page_size; + u64 used_page_mask; + u64 used_page_bits; + if constexpr (is_big_pages) { + used_page_size = big_page_size; + used_page_mask = big_page_mask; + used_page_bits = big_page_bits; + } else { + used_page_size = page_size; + used_page_mask = page_mask; + used_page_bits = page_bits; + } std::size_t remaining_size{size}; - std::size_t page_index{gpu_src_addr >> page_bits}; - std::size_t page_offset{gpu_src_addr & page_mask}; + std::size_t page_index{gpu_src_addr >> used_page_bits}; + std::size_t page_offset{gpu_src_addr & used_page_mask}; + GPUVAddr current_address = gpu_src_addr; while (remaining_size > 0) { const std::size_t copy_amount{ - std::min(static_cast(page_size) - page_offset, remaining_size)}; - const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (page_addr && *page_addr != 0) { - const auto src_addr{*page_addr + page_offset}; - if (is_safe) { - // Flush must happen on the rasterizer interface, such that memory is always - // synchronous when it is read (even when in asynchronous GPU mode). - // Fixes Dead Cells title menu. - rasterizer->FlushRegion(src_addr, copy_amount); + std::min(static_cast(used_page_size) - page_offset, remaining_size)}; + auto entry = GetEntry(current_address); + if (entry == EntryType::Mapped) [[likely]] { + if constexpr (BOOL_BREAK_MAPPED) { + if (func_mapped(page_index, page_offset, copy_amount)) { + return; + } + } else { + func_mapped(page_index, page_offset, copy_amount); } - system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); - } else { - std::memset(dest_buffer, 0, copy_amount); - } + } else if (entry == EntryType::Reserved) { + if constexpr (BOOL_BREAK_RESERVED) { + if (func_reserved(page_index, page_offset, copy_amount)) { + return; + } + } else { + func_reserved(page_index, page_offset, copy_amount); + } + + } else [[unlikely]] { + if constexpr (BOOL_BREAK_UNMAPPED) { + if (func_unmapped(page_index, page_offset, copy_amount)) { + return; + } + } else { + func_unmapped(page_index, page_offset, copy_amount); + } + } page_index++; page_offset = 0; - dest_buffer = static_cast(dest_buffer) + copy_amount; remaining_size -= copy_amount; + current_address += copy_amount; } } +template +void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, + std::size_t size) const { + auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, std::size_t copy_amount) { + std::memset(dest_buffer, 0, copy_amount); + dest_buffer = static_cast(dest_buffer) + copy_amount; + }; + auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + if constexpr (is_safe) { + rasterizer->FlushRegion(cpu_addr_base, copy_amount); + } + u8* physical = memory.GetPointer(cpu_addr_base); + std::memcpy(dest_buffer, physical, copy_amount); + dest_buffer = static_cast(dest_buffer) + copy_amount; + }; + auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + if constexpr (is_safe) { + rasterizer->FlushRegion(cpu_addr_base, copy_amount); + } + if (!IsBigPageContinous(page_index)) [[unlikely]] { + memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); + } else { + u8* physical = memory.GetPointer(cpu_addr_base); + std::memcpy(dest_buffer, physical, copy_amount); + } + dest_buffer = static_cast(dest_buffer) + copy_amount; + }; + auto read_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, mapped_normal, set_to_zero, set_to_zero); + }; + MemoryOperation(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages); +} + void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { - ReadBlockImpl(gpu_src_addr, dest_buffer, size, true); + ReadBlockImpl(gpu_src_addr, dest_buffer, size); } void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, const std::size_t size) const { - ReadBlockImpl(gpu_src_addr, dest_buffer, size, false); + ReadBlockImpl(gpu_src_addr, dest_buffer, size); } -void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, - bool is_safe) { - std::size_t remaining_size{size}; - std::size_t page_index{gpu_dest_addr >> page_bits}; - std::size_t page_offset{gpu_dest_addr & page_mask}; - - while (remaining_size > 0) { - const std::size_t copy_amount{ - std::min(static_cast(page_size) - page_offset, remaining_size)}; - const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (page_addr && *page_addr != 0) { - const auto dest_addr{*page_addr + page_offset}; - - if (is_safe) { - // Invalidate must happen on the rasterizer interface, such that memory is always - // synchronous when it is written (even when in asynchronous GPU mode). - rasterizer->InvalidateRegion(dest_addr, copy_amount); - } - system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); - } - - page_index++; - page_offset = 0; +template +void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, + std::size_t size) { + auto just_advance = [&]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, std::size_t copy_amount) { src_buffer = static_cast(src_buffer) + copy_amount; - remaining_size -= copy_amount; - } + }; + auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + if constexpr (is_safe) { + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + } + u8* physical = memory.GetPointer(cpu_addr_base); + std::memcpy(physical, src_buffer, copy_amount); + src_buffer = static_cast(src_buffer) + copy_amount; + }; + auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + if constexpr (is_safe) { + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + } + if (!IsBigPageContinous(page_index)) [[unlikely]] { + memory.WriteBlockUnsafe(cpu_addr_base, src_buffer, copy_amount); + } else { + u8* physical = memory.GetPointer(cpu_addr_base); + std::memcpy(physical, src_buffer, copy_amount); + } + src_buffer = static_cast(src_buffer) + copy_amount; + }; + auto write_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, mapped_normal, just_advance, just_advance); + }; + MemoryOperation(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages); } void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { - WriteBlockImpl(gpu_dest_addr, src_buffer, size, true); + WriteBlockImpl(gpu_dest_addr, src_buffer, size); } void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { - WriteBlockImpl(gpu_dest_addr, src_buffer, size, false); + WriteBlockImpl(gpu_dest_addr, src_buffer, size); } void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const { - size_t remaining_size{size}; - size_t page_index{gpu_addr >> page_bits}; - size_t page_offset{gpu_addr & page_mask}; - while (remaining_size > 0) { - const size_t num_bytes{std::min(page_size - page_offset, remaining_size)}; - if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) { - rasterizer->FlushRegion(*page_addr + page_offset, num_bytes); + auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) {}; + + auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + rasterizer->FlushRegion(cpu_addr_base, copy_amount); + }; + auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + rasterizer->FlushRegion(cpu_addr_base, copy_amount); + }; + auto flush_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, mapped_normal, do_nothing, do_nothing); + }; + MemoryOperation(gpu_addr, size, mapped_big, do_nothing, flush_short_pages); +} + +bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const { + bool result = false; + auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) { return false; }; + + auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); + return result; + }; + auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + result |= rasterizer->MustFlushRegion(cpu_addr_base, copy_amount); + return result; + }; + auto check_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, mapped_normal, do_nothing, do_nothing); + return result; + }; + MemoryOperation(gpu_addr, size, mapped_big, do_nothing, check_short_pages); + return result; +} + +size_t MemoryManager::MaxContinousRange(GPUVAddr gpu_addr, size_t size) const { + std::optional old_page_addr{}; + size_t range_so_far = 0; + bool result{false}; + auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, + std::size_t copy_amount) { + result = true; + return true; + }; + auto short_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + if (old_page_addr && *old_page_addr != cpu_addr_base) { + result = true; + return true; } - ++page_index; - page_offset = 0; - remaining_size -= num_bytes; - } + range_so_far += copy_amount; + old_page_addr = {cpu_addr_base + copy_amount}; + return false; + }; + auto big_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + if (old_page_addr && *old_page_addr != cpu_addr_base) { + return true; + } + range_so_far += copy_amount; + old_page_addr = {cpu_addr_base + copy_amount}; + return false; + }; + auto check_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, short_check, fail, fail); + return result; + }; + MemoryOperation(gpu_addr, size, big_check, fail, check_short_pages); + return range_so_far; +} + +void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size) const { + auto do_nothing = [&]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) {}; + + auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + }; + auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + rasterizer->InvalidateRegion(cpu_addr_base, copy_amount); + }; + auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, mapped_normal, do_nothing, do_nothing); + }; + MemoryOperation(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages); } void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { @@ -365,87 +605,134 @@ void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std } bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { - const auto cpu_addr{GpuToCpuAddress(gpu_addr)}; - if (!cpu_addr) { + if (GetEntry(gpu_addr) == EntryType::Mapped) [[likely]] { + size_t page_index = gpu_addr >> big_page_bits; + if (IsBigPageContinous(page_index)) [[likely]] { + const std::size_t page{(page_index & big_page_mask) + size}; + return page <= big_page_size; + } + const std::size_t page{(gpu_addr & Core::Memory::YUZU_PAGEMASK) + size}; + return page <= Core::Memory::YUZU_PAGESIZE; + } + if (GetEntry(gpu_addr) != EntryType::Mapped) { return false; } - const std::size_t page{(*cpu_addr & Core::Memory::YUZU_PAGEMASK) + size}; + const std::size_t page{(gpu_addr & Core::Memory::YUZU_PAGEMASK) + size}; return page <= Core::Memory::YUZU_PAGESIZE; } bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const { - size_t page_index{gpu_addr >> page_bits}; - const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits}; std::optional old_page_addr{}; - while (page_index != page_last) { - const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (!page_addr || *page_addr == 0) { - return false; + bool result{true}; + auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, + std::size_t copy_amount) { + result = false; + return true; + }; + auto short_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + if (old_page_addr && *old_page_addr != cpu_addr_base) { + result = false; + return true; } - if (old_page_addr) { - if (*old_page_addr + page_size != *page_addr) { - return false; - } + old_page_addr = {cpu_addr_base + copy_amount}; + return false; + }; + auto big_check = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + if (old_page_addr && *old_page_addr != cpu_addr_base) { + result = false; + return true; } - old_page_addr = page_addr; - ++page_index; - } - return true; + old_page_addr = {cpu_addr_base + copy_amount}; + return false; + }; + auto check_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, short_check, fail, fail); + return !result; + }; + MemoryOperation(gpu_addr, size, big_check, fail, check_short_pages); + return result; } bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const { - size_t page_index{gpu_addr >> page_bits}; - const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits}; - while (page_index < page_last) { - if (!page_table[page_index].IsValid() || page_table[page_index].ToAddress() == 0) { - return false; - } - ++page_index; - } - return true; + bool result{true}; + auto fail = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) { + result = false; + return true; + }; + auto pass = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) { return false; }; + auto check_short_pages = [&](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, pass, pass, fail); + return !result; + }; + MemoryOperation(gpu_addr, size, pass, fail, check_short_pages); + return result; } std::vector> MemoryManager::GetSubmappedRange( GPUVAddr gpu_addr, std::size_t size) const { std::vector> result{}; - size_t page_index{gpu_addr >> page_bits}; - size_t remaining_size{size}; - size_t page_offset{gpu_addr & page_mask}; std::optional> last_segment{}; std::optional old_page_addr{}; - const auto extend_size = [&last_segment, &page_index, &page_offset](std::size_t bytes) { - if (!last_segment) { - const GPUVAddr new_base_addr = (page_index << page_bits) + page_offset; - last_segment = {new_base_addr, bytes}; - } else { - last_segment->second += bytes; - } - }; - const auto split = [&last_segment, &result] { + const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index, + [[maybe_unused]] std::size_t offset, + [[maybe_unused]] std::size_t copy_amount) { if (last_segment) { result.push_back(*last_segment); last_segment = std::nullopt; } }; - while (remaining_size > 0) { - const size_t num_bytes{std::min(page_size - page_offset, remaining_size)}; - const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; - if (!page_addr || *page_addr == 0) { - split(); - } else if (old_page_addr) { - if (*old_page_addr + page_size != *page_addr) { - split(); + const auto extend_size_big = [this, &split, &old_page_addr, + &last_segment](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(big_page_table_cpu[page_index]) << cpu_page_bits) + offset; + if (old_page_addr) { + if (*old_page_addr != cpu_addr_base) { + split(0, 0, 0); } - extend_size(num_bytes); - } else { - extend_size(num_bytes); } - ++page_index; - page_offset = 0; - remaining_size -= num_bytes; - old_page_addr = page_addr; - } - split(); + old_page_addr = {cpu_addr_base + copy_amount}; + if (!last_segment) { + const GPUVAddr new_base_addr = (page_index << big_page_bits) + offset; + last_segment = {new_base_addr, copy_amount}; + } else { + last_segment->second += copy_amount; + } + }; + const auto extend_size_short = [this, &split, &old_page_addr, + &last_segment](std::size_t page_index, std::size_t offset, + std::size_t copy_amount) { + const VAddr cpu_addr_base = + (static_cast(page_table[page_index]) << cpu_page_bits) + offset; + if (old_page_addr) { + if (*old_page_addr != cpu_addr_base) { + split(0, 0, 0); + } + } + old_page_addr = {cpu_addr_base + copy_amount}; + if (!last_segment) { + const GPUVAddr new_base_addr = (page_index << page_bits) + offset; + last_segment = {new_base_addr, copy_amount}; + } else { + last_segment->second += copy_amount; + } + }; + auto do_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { + GPUVAddr base = (page_index << big_page_bits) + offset; + MemoryOperation(base, copy_amount, extend_size_short, split, split); + }; + MemoryOperation(gpu_addr, size, extend_size_big, split, do_short_pages); + split(0, 0, 0); return result; } diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 74f9ce1..ab4bc9e 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -3,73 +3,40 @@ #pragma once +#include #include #include #include #include "common/common_types.h" +#include "common/multi_level_page_table.h" +#include "common/virtual_buffer.h" +#include "video_core/pte_kind.h" namespace VideoCore { class RasterizerInterface; } namespace Core { +class DeviceMemory; +namespace Memory { +class Memory; +} // namespace Memory class System; -} +} // namespace Core namespace Tegra { -class PageEntry final { -public: - enum class State : u32 { - Unmapped = static_cast(-1), - Allocated = static_cast(-2), - }; - - constexpr PageEntry() = default; - constexpr PageEntry(State state_) : state{state_} {} - constexpr PageEntry(VAddr addr) : state{static_cast(addr >> ShiftBits)} {} - - [[nodiscard]] constexpr bool IsUnmapped() const { - return state == State::Unmapped; - } - - [[nodiscard]] constexpr bool IsAllocated() const { - return state == State::Allocated; - } - - [[nodiscard]] constexpr bool IsValid() const { - return !IsUnmapped() && !IsAllocated(); - } - - [[nodiscard]] constexpr VAddr ToAddress() const { - if (!IsValid()) { - return {}; - } - - return static_cast(state) << ShiftBits; - } - - [[nodiscard]] constexpr PageEntry operator+(u64 offset) const { - // If this is a reserved value, offsets do not apply - if (!IsValid()) { - return *this; - } - return PageEntry{(static_cast(state) << ShiftBits) + offset}; - } - -private: - static constexpr std::size_t ShiftBits{12}; - - State state{State::Unmapped}; -}; -static_assert(sizeof(PageEntry) == 4, "PageEntry is too large"); - class MemoryManager final { public: - explicit MemoryManager(Core::System& system_); + explicit MemoryManager(Core::System& system_, u64 address_space_bits_ = 40, + u64 big_page_bits_ = 16, u64 page_bits_ = 12); ~MemoryManager(); + size_t GetID() const { + return unique_identifier; + } + /// Binds a renderer to the memory manager. void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); @@ -86,9 +53,6 @@ public: [[nodiscard]] u8* GetPointer(GPUVAddr addr); [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const; - /// Returns the number of bytes until the end of the memory map containing the given GPU address - [[nodiscard]] size_t BytesToMapEnd(GPUVAddr gpu_addr) const noexcept; - /** * ReadBlock and WriteBlock are full read and write operations over virtual * GPU Memory. It's important to use these when GPU memory may not be continuous @@ -135,54 +99,109 @@ public: std::vector> GetSubmappedRange(GPUVAddr gpu_addr, std::size_t size) const; - [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size); - [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align); - [[nodiscard]] GPUVAddr MapAllocate32(VAddr cpu_addr, std::size_t size); - [[nodiscard]] std::optional AllocateFixed(GPUVAddr gpu_addr, std::size_t size); - [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align); + GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size, + PTEKind kind = PTEKind::INVALID, bool is_big_pages = true); + GPUVAddr MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages = true); void Unmap(GPUVAddr gpu_addr, std::size_t size); void FlushRegion(GPUVAddr gpu_addr, size_t size) const; -private: - [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const; - void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); - GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size); - [[nodiscard]] std::optional FindFreeRange(std::size_t size, std::size_t align, - bool start_32bit_address = false) const; + void InvalidateRegion(GPUVAddr gpu_addr, size_t size) const; - void TryLockPage(PageEntry page_entry, std::size_t size); - void TryUnlockPage(PageEntry page_entry, std::size_t size); + bool IsMemoryDirty(GPUVAddr gpu_addr, size_t size) const; - void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, - bool is_safe) const; - void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, - bool is_safe); + size_t MaxContinousRange(GPUVAddr gpu_addr, size_t size) const; - [[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) { - return (gpu_addr >> page_bits) & page_table_mask; + bool IsWithinGPUAddressRange(GPUVAddr gpu_addr) const { + return gpu_addr < address_space_size; } - static constexpr u64 address_space_size = 1ULL << 40; - static constexpr u64 address_space_start = 1ULL << 32; - static constexpr u64 address_space_start_low = 1ULL << 16; - static constexpr u64 page_bits{16}; - static constexpr u64 page_size{1 << page_bits}; - static constexpr u64 page_mask{page_size - 1}; - static constexpr u64 page_table_bits{24}; - static constexpr u64 page_table_size{1 << page_table_bits}; - static constexpr u64 page_table_mask{page_table_size - 1}; + PTEKind GetPageKind(GPUVAddr gpu_addr) const; + +private: + template + inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, + FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; + + template + void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; + + template + void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); + + template + [[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const { + if constexpr (is_big_page) { + return (gpu_addr >> big_page_bits) & big_page_table_mask; + } else { + return (gpu_addr >> page_bits) & page_table_mask; + } + } + + inline bool IsBigPageContinous(size_t big_page_index) const; + inline void SetBigPageContinous(size_t big_page_index, bool value); Core::System& system; + Core::Memory::Memory& memory; + Core::DeviceMemory& device_memory; + + const u64 address_space_bits; + const u64 page_bits; + u64 address_space_size; + u64 page_size; + u64 page_mask; + u64 page_table_mask; + static constexpr u64 cpu_page_bits{12}; + + const u64 big_page_bits; + u64 big_page_size; + u64 big_page_mask; + u64 big_page_table_mask; VideoCore::RasterizerInterface* rasterizer = nullptr; - std::vector page_table; + enum class EntryType : u64 { + Free = 0, + Reserved = 1, + Mapped = 2, + }; - using MapRange = std::pair; - std::vector map_ranges; + std::vector entries; + std::vector big_entries; - std::vector> cache_invalidate_queue; + template + GPUVAddr PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size, + PTEKind kind); + + template + GPUVAddr BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size, + PTEKind kind); + + template + inline EntryType GetEntry(size_t position) const; + + template + inline void SetEntry(size_t position, EntryType entry); + + std::vector> kinds; + std::vector> big_kinds; + + template + inline PTEKind GetKind(size_t position) const; + + template + inline void SetKind(size_t position, PTEKind kind); + + Common::MultiLevelPageTable page_table; + Common::VirtualBuffer big_page_table_cpu; + + std::vector big_page_continous; + + constexpr static size_t continous_bits = 64; + + const size_t unique_identifier; + + static std::atomic unique_identifier_generator; }; } // namespace Tegra diff --git a/src/video_core/precompiled_headers.h b/src/video_core/precompiled_headers.h new file mode 100644 index 0000000..aabae73 --- /dev/null +++ b/src/video_core/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/video_core/pte_kind.h b/src/video_core/pte_kind.h new file mode 100644 index 0000000..591d721 --- /dev/null +++ b/src/video_core/pte_kind.h @@ -0,0 +1,264 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Tegra { + +// https://github.com/NVIDIA/open-gpu-doc/blob/master/manuals/volta/gv100/dev_mmu.ref.txt +enum class PTEKind : u8 { + INVALID = 0xff, + PITCH = 0x00, + Z16 = 0x01, + Z16_2C = 0x02, + Z16_MS2_2C = 0x03, + Z16_MS4_2C = 0x04, + Z16_MS8_2C = 0x05, + Z16_MS16_2C = 0x06, + Z16_2Z = 0x07, + Z16_MS2_2Z = 0x08, + Z16_MS4_2Z = 0x09, + Z16_MS8_2Z = 0x0a, + Z16_MS16_2Z = 0x0b, + Z16_2CZ = 0x36, + Z16_MS2_2CZ = 0x37, + Z16_MS4_2CZ = 0x38, + Z16_MS8_2CZ = 0x39, + Z16_MS16_2CZ = 0x5f, + Z16_4CZ = 0x0c, + Z16_MS2_4CZ = 0x0d, + Z16_MS4_4CZ = 0x0e, + Z16_MS8_4CZ = 0x0f, + Z16_MS16_4CZ = 0x10, + S8Z24 = 0x11, + S8Z24_1Z = 0x12, + S8Z24_MS2_1Z = 0x13, + S8Z24_MS4_1Z = 0x14, + S8Z24_MS8_1Z = 0x15, + S8Z24_MS16_1Z = 0x16, + S8Z24_2CZ = 0x17, + S8Z24_MS2_2CZ = 0x18, + S8Z24_MS4_2CZ = 0x19, + S8Z24_MS8_2CZ = 0x1a, + S8Z24_MS16_2CZ = 0x1b, + S8Z24_2CS = 0x1c, + S8Z24_MS2_2CS = 0x1d, + S8Z24_MS4_2CS = 0x1e, + S8Z24_MS8_2CS = 0x1f, + S8Z24_MS16_2CS = 0x20, + S8Z24_4CSZV = 0x21, + S8Z24_MS2_4CSZV = 0x22, + S8Z24_MS4_4CSZV = 0x23, + S8Z24_MS8_4CSZV = 0x24, + S8Z24_MS16_4CSZV = 0x25, + V8Z24_MS4_VC12 = 0x26, + V8Z24_MS4_VC4 = 0x27, + V8Z24_MS8_VC8 = 0x28, + V8Z24_MS8_VC24 = 0x29, + V8Z24_MS4_VC12_1ZV = 0x2e, + V8Z24_MS4_VC4_1ZV = 0x2f, + V8Z24_MS8_VC8_1ZV = 0x30, + V8Z24_MS8_VC24_1ZV = 0x31, + V8Z24_MS4_VC12_2CS = 0x32, + V8Z24_MS4_VC4_2CS = 0x33, + V8Z24_MS8_VC8_2CS = 0x34, + V8Z24_MS8_VC24_2CS = 0x35, + V8Z24_MS4_VC12_2CZV = 0x3a, + V8Z24_MS4_VC4_2CZV = 0x3b, + V8Z24_MS8_VC8_2CZV = 0x3c, + V8Z24_MS8_VC24_2CZV = 0x3d, + V8Z24_MS4_VC12_2ZV = 0x3e, + V8Z24_MS4_VC4_2ZV = 0x3f, + V8Z24_MS8_VC8_2ZV = 0x40, + V8Z24_MS8_VC24_2ZV = 0x41, + V8Z24_MS4_VC12_4CSZV = 0x42, + V8Z24_MS4_VC4_4CSZV = 0x43, + V8Z24_MS8_VC8_4CSZV = 0x44, + V8Z24_MS8_VC24_4CSZV = 0x45, + Z24S8 = 0x46, + Z24S8_1Z = 0x47, + Z24S8_MS2_1Z = 0x48, + Z24S8_MS4_1Z = 0x49, + Z24S8_MS8_1Z = 0x4a, + Z24S8_MS16_1Z = 0x4b, + Z24S8_2CS = 0x4c, + Z24S8_MS2_2CS = 0x4d, + Z24S8_MS4_2CS = 0x4e, + Z24S8_MS8_2CS = 0x4f, + Z24S8_MS16_2CS = 0x50, + Z24S8_2CZ = 0x51, + Z24S8_MS2_2CZ = 0x52, + Z24S8_MS4_2CZ = 0x53, + Z24S8_MS8_2CZ = 0x54, + Z24S8_MS16_2CZ = 0x55, + Z24S8_4CSZV = 0x56, + Z24S8_MS2_4CSZV = 0x57, + Z24S8_MS4_4CSZV = 0x58, + Z24S8_MS8_4CSZV = 0x59, + Z24S8_MS16_4CSZV = 0x5a, + Z24V8_MS4_VC12 = 0x5b, + Z24V8_MS4_VC4 = 0x5c, + Z24V8_MS8_VC8 = 0x5d, + Z24V8_MS8_VC24 = 0x5e, + YUV_B8C1_2Y = 0x60, + YUV_B8C2_2Y = 0x61, + YUV_B10C1_2Y = 0x62, + YUV_B10C2_2Y = 0x6b, + YUV_B12C1_2Y = 0x6c, + YUV_B12C2_2Y = 0x6d, + Z24V8_MS4_VC12_1ZV = 0x63, + Z24V8_MS4_VC4_1ZV = 0x64, + Z24V8_MS8_VC8_1ZV = 0x65, + Z24V8_MS8_VC24_1ZV = 0x66, + Z24V8_MS4_VC12_2CS = 0x67, + Z24V8_MS4_VC4_2CS = 0x68, + Z24V8_MS8_VC8_2CS = 0x69, + Z24V8_MS8_VC24_2CS = 0x6a, + Z24V8_MS4_VC12_2CZV = 0x6f, + Z24V8_MS4_VC4_2CZV = 0x70, + Z24V8_MS8_VC8_2CZV = 0x71, + Z24V8_MS8_VC24_2CZV = 0x72, + Z24V8_MS4_VC12_2ZV = 0x73, + Z24V8_MS4_VC4_2ZV = 0x74, + Z24V8_MS8_VC8_2ZV = 0x75, + Z24V8_MS8_VC24_2ZV = 0x76, + Z24V8_MS4_VC12_4CSZV = 0x77, + Z24V8_MS4_VC4_4CSZV = 0x78, + Z24V8_MS8_VC8_4CSZV = 0x79, + Z24V8_MS8_VC24_4CSZV = 0x7a, + ZF32 = 0x7b, + ZF32_1Z = 0x7c, + ZF32_MS2_1Z = 0x7d, + ZF32_MS4_1Z = 0x7e, + ZF32_MS8_1Z = 0x7f, + ZF32_MS16_1Z = 0x80, + ZF32_2CS = 0x81, + ZF32_MS2_2CS = 0x82, + ZF32_MS4_2CS = 0x83, + ZF32_MS8_2CS = 0x84, + ZF32_MS16_2CS = 0x85, + ZF32_2CZ = 0x86, + ZF32_MS2_2CZ = 0x87, + ZF32_MS4_2CZ = 0x88, + ZF32_MS8_2CZ = 0x89, + ZF32_MS16_2CZ = 0x8a, + X8Z24_X16V8S8_MS4_VC12 = 0x8b, + X8Z24_X16V8S8_MS4_VC4 = 0x8c, + X8Z24_X16V8S8_MS8_VC8 = 0x8d, + X8Z24_X16V8S8_MS8_VC24 = 0x8e, + X8Z24_X16V8S8_MS4_VC12_1CS = 0x8f, + X8Z24_X16V8S8_MS4_VC4_1CS = 0x90, + X8Z24_X16V8S8_MS8_VC8_1CS = 0x91, + X8Z24_X16V8S8_MS8_VC24_1CS = 0x92, + X8Z24_X16V8S8_MS4_VC12_1ZV = 0x97, + X8Z24_X16V8S8_MS4_VC4_1ZV = 0x98, + X8Z24_X16V8S8_MS8_VC8_1ZV = 0x99, + X8Z24_X16V8S8_MS8_VC24_1ZV = 0x9a, + X8Z24_X16V8S8_MS4_VC12_1CZV = 0x9b, + X8Z24_X16V8S8_MS4_VC4_1CZV = 0x9c, + X8Z24_X16V8S8_MS8_VC8_1CZV = 0x9d, + X8Z24_X16V8S8_MS8_VC24_1CZV = 0x9e, + X8Z24_X16V8S8_MS4_VC12_2CS = 0x9f, + X8Z24_X16V8S8_MS4_VC4_2CS = 0xa0, + X8Z24_X16V8S8_MS8_VC8_2CS = 0xa1, + X8Z24_X16V8S8_MS8_VC24_2CS = 0xa2, + X8Z24_X16V8S8_MS4_VC12_2CSZV = 0xa3, + X8Z24_X16V8S8_MS4_VC4_2CSZV = 0xa4, + X8Z24_X16V8S8_MS8_VC8_2CSZV = 0xa5, + X8Z24_X16V8S8_MS8_VC24_2CSZV = 0xa6, + ZF32_X16V8S8_MS4_VC12 = 0xa7, + ZF32_X16V8S8_MS4_VC4 = 0xa8, + ZF32_X16V8S8_MS8_VC8 = 0xa9, + ZF32_X16V8S8_MS8_VC24 = 0xaa, + ZF32_X16V8S8_MS4_VC12_1CS = 0xab, + ZF32_X16V8S8_MS4_VC4_1CS = 0xac, + ZF32_X16V8S8_MS8_VC8_1CS = 0xad, + ZF32_X16V8S8_MS8_VC24_1CS = 0xae, + ZF32_X16V8S8_MS4_VC12_1ZV = 0xb3, + ZF32_X16V8S8_MS4_VC4_1ZV = 0xb4, + ZF32_X16V8S8_MS8_VC8_1ZV = 0xb5, + ZF32_X16V8S8_MS8_VC24_1ZV = 0xb6, + ZF32_X16V8S8_MS4_VC12_1CZV = 0xb7, + ZF32_X16V8S8_MS4_VC4_1CZV = 0xb8, + ZF32_X16V8S8_MS8_VC8_1CZV = 0xb9, + ZF32_X16V8S8_MS8_VC24_1CZV = 0xba, + ZF32_X16V8S8_MS4_VC12_2CS = 0xbb, + ZF32_X16V8S8_MS4_VC4_2CS = 0xbc, + ZF32_X16V8S8_MS8_VC8_2CS = 0xbd, + ZF32_X16V8S8_MS8_VC24_2CS = 0xbe, + ZF32_X16V8S8_MS4_VC12_2CSZV = 0xbf, + ZF32_X16V8S8_MS4_VC4_2CSZV = 0xc0, + ZF32_X16V8S8_MS8_VC8_2CSZV = 0xc1, + ZF32_X16V8S8_MS8_VC24_2CSZV = 0xc2, + ZF32_X24S8 = 0xc3, + ZF32_X24S8_1CS = 0xc4, + ZF32_X24S8_MS2_1CS = 0xc5, + ZF32_X24S8_MS4_1CS = 0xc6, + ZF32_X24S8_MS8_1CS = 0xc7, + ZF32_X24S8_MS16_1CS = 0xc8, + ZF32_X24S8_2CSZV = 0xce, + ZF32_X24S8_MS2_2CSZV = 0xcf, + ZF32_X24S8_MS4_2CSZV = 0xd0, + ZF32_X24S8_MS8_2CSZV = 0xd1, + ZF32_X24S8_MS16_2CSZV = 0xd2, + ZF32_X24S8_2CS = 0xd3, + ZF32_X24S8_MS2_2CS = 0xd4, + ZF32_X24S8_MS4_2CS = 0xd5, + ZF32_X24S8_MS8_2CS = 0xd6, + ZF32_X24S8_MS16_2CS = 0xd7, + S8 = 0x2a, + S8_2S = 0x2b, + GENERIC_16BX2 = 0xfe, + C32_2C = 0xd8, + C32_2CBR = 0xd9, + C32_2CBA = 0xda, + C32_2CRA = 0xdb, + C32_2BRA = 0xdc, + C32_MS2_2C = 0xdd, + C32_MS2_2CBR = 0xde, + C32_MS2_4CBRA = 0xcc, + C32_MS4_2C = 0xdf, + C32_MS4_2CBR = 0xe0, + C32_MS4_2CBA = 0xe1, + C32_MS4_2CRA = 0xe2, + C32_MS4_2BRA = 0xe3, + C32_MS4_4CBRA = 0x2c, + C32_MS8_MS16_2C = 0xe4, + C32_MS8_MS16_2CRA = 0xe5, + C64_2C = 0xe6, + C64_2CBR = 0xe7, + C64_2CBA = 0xe8, + C64_2CRA = 0xe9, + C64_2BRA = 0xea, + C64_MS2_2C = 0xeb, + C64_MS2_2CBR = 0xec, + C64_MS2_4CBRA = 0xcd, + C64_MS4_2C = 0xed, + C64_MS4_2CBR = 0xee, + C64_MS4_2CBA = 0xef, + C64_MS4_2CRA = 0xf0, + C64_MS4_2BRA = 0xf1, + C64_MS4_4CBRA = 0x2d, + C64_MS8_MS16_2C = 0xf2, + C64_MS8_MS16_2CRA = 0xf3, + C128_2C = 0xf4, + C128_2CR = 0xf5, + C128_MS2_2C = 0xf6, + C128_MS2_2CR = 0xf7, + C128_MS4_2C = 0xf8, + C128_MS4_2CR = 0xf9, + C128_MS8_MS16_2C = 0xfa, + C128_MS8_MS16_2CR = 0xfb, + X8C24 = 0xfc, + PITCH_NO_SWIZZLE = 0xfd, + SMSKED_MESSAGE = 0xca, + SMHOST_MESSAGE = 0xcb, +}; + +constexpr bool IsPitchKind(PTEKind kind) { + return kind == PTEKind::PITCH || kind == PTEKind::PITCH_NO_SWIZZLE; +} + +} // namespace Tegra diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index 889b606..00ce53e 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h @@ -17,6 +17,7 @@ #include "common/assert.h" #include "common/settings.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" @@ -90,13 +91,10 @@ private: }; template -class QueryCacheBase { +class QueryCacheBase : public VideoCommon::ChannelSetupCaches { public: - explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::MemoryManager& gpu_memory_) - : rasterizer{rasterizer_}, maxwell3d{maxwell3d_}, - gpu_memory{gpu_memory_}, streams{{CounterStream{static_cast(*this), + explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_) + : rasterizer{rasterizer_}, streams{{CounterStream{static_cast(*this), VideoCore::QueryType::SamplesPassed}}} {} void InvalidateRegion(VAddr addr, std::size_t size) { @@ -117,13 +115,13 @@ public: */ void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional timestamp) { std::unique_lock lock{mutex}; - const std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + const std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); ASSERT(cpu_addr); CachedQuery* query = TryGet(*cpu_addr); if (!query) { ASSERT_OR_EXECUTE(cpu_addr, return;); - u8* const host_ptr = gpu_memory.GetPointer(gpu_addr); + u8* const host_ptr = gpu_memory->GetPointer(gpu_addr); query = Register(type, *cpu_addr, host_ptr, timestamp.has_value()); } @@ -137,8 +135,10 @@ public: /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. void UpdateCounters() { std::unique_lock lock{mutex}; - const auto& regs = maxwell3d.regs; - Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable); + if (maxwell3d) { + const auto& regs = maxwell3d->regs; + Stream(VideoCore::QueryType::SamplesPassed).Update(regs.zpass_pixel_count_enable); + } } /// Resets a counter to zero. It doesn't disable the query after resetting. @@ -264,8 +264,6 @@ private: static constexpr unsigned YUZU_PAGEBITS = 12; VideoCore::RasterizerInterface& rasterizer; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::MemoryManager& gpu_memory; std::recursive_mutex mutex; diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index a04a764..b690746 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -6,8 +6,8 @@ #include #include #include -#include #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "video_core/engines/fermi_2d.h" #include "video_core/gpu.h" @@ -16,6 +16,9 @@ class MemoryManager; namespace Engines { class AccelerateDMAInterface; } +namespace Control { +struct ChannelState; +} } // namespace Tegra namespace VideoCore { @@ -37,10 +40,10 @@ public: virtual ~RasterizerInterface() = default; /// Dispatches a draw invocation - virtual void Draw(bool is_indexed, bool is_instanced) = 0; + virtual void Draw(bool is_indexed, u32 instance_count) = 0; /// Clear the current framebuffer - virtual void Clear() = 0; + virtual void Clear(u32 layer_count) = 0; /// Dispatches a compute shader invocation virtual void DispatchCompute() = 0; @@ -59,7 +62,10 @@ public: virtual void DisableGraphicsUniformBuffer(size_t stage, u32 index) = 0; /// Signal a GPU based semaphore as a fence - virtual void SignalSemaphore(GPUVAddr addr, u32 value) = 0; + virtual void SignalFence(std::function&& func) = 0; + + /// Send an operation to be done after a certain amount of flushes. + virtual void SyncOperation(std::function&& func) = 0; /// Signal a GPU based syncpoint as a fence virtual void SignalSyncPoint(u32 value) = 0; @@ -86,13 +92,13 @@ public: virtual void OnCPUWrite(VAddr addr, u64 size) = 0; /// Sync memory between guest and host. - virtual void SyncGuestHost() = 0; + virtual void InvalidateGPUCache() = 0; /// Unmap memory range virtual void UnmapMemory(VAddr addr, u64 size) = 0; /// Remap GPU memory range. This means underneath backing memory changed - virtual void ModifyGPUMemory(GPUVAddr addr, u64 size) = 0; + virtual void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) = 0; /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory /// and invalidated @@ -123,7 +129,7 @@ public: [[nodiscard]] virtual Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() = 0; virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, - std::span memory) = 0; + std::span memory) = 0; /// Attempt to use a faster method to display the framebuffer to screen [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, @@ -137,5 +143,11 @@ public: /// Initialize disk cached resources for the game being emulated virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading, const DiskResourceLoadCallback& callback) {} + + virtual void InitializeChannel(Tegra::Control::ChannelState& channel) {} + + virtual void BindChannel(Tegra::Control::ChannelState& channel) {} + + virtual void ReleaseChannel(s32 channel_id) {} }; } // namespace VideoCore diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index 45791aa..e8761a7 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/logging/log.h" #include "core/frontend/emu_window.h" #include "video_core/renderer_base.h" @@ -35,8 +37,12 @@ void RendererBase::RequestScreenshot(void* data, std::function callb LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); return; } + auto async_callback{[callback = std::move(callback)](bool invert_y) { + std::thread t{callback, invert_y}; + t.detach(); + }}; renderer_settings.screenshot_bits = data; - renderer_settings.screenshot_complete_callback = std::move(callback); + renderer_settings.screenshot_complete_callback = async_callback; renderer_settings.screenshot_framebuffer_layout = layout; renderer_settings.screenshot_requested = true; } diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp new file mode 100644 index 0000000..9734d84 --- /dev/null +++ b/src/video_core/renderer_null/null_rasterizer.cpp @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/host1x/host1x.h" +#include "video_core/memory_manager.h" +#include "video_core/renderer_null/null_rasterizer.h" + +namespace Null { + +AccelerateDMA::AccelerateDMA() = default; + +bool AccelerateDMA::BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) { + return true; +} +bool AccelerateDMA::BufferClear(GPUVAddr src_address, u64 amount, u32 value) { + return true; +} + +RasterizerNull::RasterizerNull(Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu) + : RasterizerAccelerated(cpu_memory_), m_gpu{gpu} {} +RasterizerNull::~RasterizerNull() = default; + +void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {} +void RasterizerNull::Clear(u32 layer_count) {} +void RasterizerNull::DispatchCompute() {} +void RasterizerNull::ResetCounter(VideoCore::QueryType type) {} +void RasterizerNull::Query(GPUVAddr gpu_addr, VideoCore::QueryType type, + std::optional timestamp) { + if (!gpu_memory) { + return; + } + + gpu_memory->Write(gpu_addr, u64{0}); + if (timestamp) { + gpu_memory->Write(gpu_addr + 8, *timestamp); + } +} +void RasterizerNull::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, + u32 size) {} +void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {} +void RasterizerNull::FlushAll() {} +void RasterizerNull::FlushRegion(VAddr addr, u64 size) {} +bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size) { + return false; +} +void RasterizerNull::InvalidateRegion(VAddr addr, u64 size) {} +void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} +void RasterizerNull::InvalidateGPUCache() {} +void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {} +void RasterizerNull::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {} +void RasterizerNull::SignalFence(std::function&& func) { + func(); +} +void RasterizerNull::SyncOperation(std::function&& func) { + func(); +} +void RasterizerNull::SignalSyncPoint(u32 value) { + auto& syncpoint_manager = m_gpu.Host1x().GetSyncpointManager(); + syncpoint_manager.IncrementGuest(value); + syncpoint_manager.IncrementHost(value); +} +void RasterizerNull::SignalReference() {} +void RasterizerNull::ReleaseFences() {} +void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size) {} +void RasterizerNull::WaitForIdle() {} +void RasterizerNull::FragmentBarrier() {} +void RasterizerNull::TiledCacheBarrier() {} +void RasterizerNull::FlushCommands() {} +void RasterizerNull::TickFrame() {} +Tegra::Engines::AccelerateDMAInterface& RasterizerNull::AccessAccelerateDMA() { + return m_accelerate_dma; +} +bool RasterizerNull::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, + const Tegra::Engines::Fermi2D::Surface& dst, + const Tegra::Engines::Fermi2D::Config& copy_config) { + return true; +} +void RasterizerNull::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, + std::span memory) {} +bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config, + VAddr framebuffer_addr, u32 pixel_stride) { + return true; +} +void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, + const VideoCore::DiskResourceLoadCallback& callback) {} +void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {} +void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) {} +void RasterizerNull::ReleaseChannel(s32 channel_id) {} + +} // namespace Null diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h new file mode 100644 index 0000000..ecf77ba --- /dev/null +++ b/src/video_core/renderer_null/null_rasterizer.h @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "video_core/control/channel_state_cache.h" +#include "video_core/engines/maxwell_dma.h" +#include "video_core/rasterizer_accelerated.h" +#include "video_core/rasterizer_interface.h" + +namespace Core { +class System; +} + +namespace Null { + +class RasterizerNull; + +class AccelerateDMA : public Tegra::Engines::AccelerateDMAInterface { +public: + explicit AccelerateDMA(); + bool BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) override; + bool BufferClear(GPUVAddr src_address, u64 amount, u32 value) override; +}; + +class RasterizerNull final : public VideoCore::RasterizerAccelerated, + protected VideoCommon::ChannelSetupCaches { +public: + explicit RasterizerNull(Core::Memory::Memory& cpu_memory, Tegra::GPU& gpu); + ~RasterizerNull() override; + + void Draw(bool is_indexed, u32 instance_count) override; + void Clear(u32 layer_count) override; + void DispatchCompute() override; + void ResetCounter(VideoCore::QueryType type) override; + void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional timestamp) override; + void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; + void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; + void FlushAll() override; + void FlushRegion(VAddr addr, u64 size) override; + bool MustFlushRegion(VAddr addr, u64 size) override; + void InvalidateRegion(VAddr addr, u64 size) override; + void OnCPUWrite(VAddr addr, u64 size) override; + void InvalidateGPUCache() override; + void UnmapMemory(VAddr addr, u64 size) override; + void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; + void SignalFence(std::function&& func) override; + void SyncOperation(std::function&& func) override; + void SignalSyncPoint(u32 value) override; + void SignalReference() override; + void ReleaseFences() override; + void FlushAndInvalidateRegion(VAddr addr, u64 size) override; + void WaitForIdle() override; + void FragmentBarrier() override; + void TiledCacheBarrier() override; + void FlushCommands() override; + void TickFrame() override; + bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, + const Tegra::Engines::Fermi2D::Surface& dst, + const Tegra::Engines::Fermi2D::Config& copy_config) override; + Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; + void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, + std::span memory) override; + bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, + u32 pixel_stride) override; + void LoadDiskResources(u64 title_id, std::stop_token stop_loading, + const VideoCore::DiskResourceLoadCallback& callback) override; + void InitializeChannel(Tegra::Control::ChannelState& channel) override; + void BindChannel(Tegra::Control::ChannelState& channel) override; + void ReleaseChannel(s32 channel_id) override; + +private: + Tegra::GPU& m_gpu; + AccelerateDMA m_accelerate_dma; +}; + +} // namespace Null diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp new file mode 100644 index 0000000..e2a189b --- /dev/null +++ b/src/video_core/renderer_null/renderer_null.cpp @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/renderer_null/renderer_null.h" + +namespace Null { + +RendererNull::RendererNull(Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory, + Tegra::GPU& gpu, + std::unique_ptr context_) + : RendererBase(emu_window, std::move(context_)), m_gpu(gpu), m_rasterizer(cpu_memory, gpu) {} + +RendererNull::~RendererNull() = default; + +void RendererNull::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { + if (!framebuffer) { + return; + } + + m_gpu.RendererFrameEndNotify(); + render_window.OnFrameDisplayed(); +} + +} // namespace Null diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h new file mode 100644 index 0000000..967ff56 --- /dev/null +++ b/src/video_core/renderer_null/renderer_null.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "video_core/renderer_base.h" +#include "video_core/renderer_null/null_rasterizer.h" + +namespace Null { + +class RendererNull final : public VideoCore::RendererBase { +public: + explicit RendererNull(Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory, + Tegra::GPU& gpu, + std::unique_ptr context); + ~RendererNull() override; + + void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; + + VideoCore::RasterizerInterface* ReadRasterizer() override { + return &m_rasterizer; + } + + [[nodiscard]] std::string GetDeviceVendor() const override { + return "NULL"; + } + +private: + Tegra::GPU& m_gpu; + RasterizerNull m_rasterizer; +}; + +} // namespace Null diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 32450ee..6af4ae7 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -29,17 +29,17 @@ constexpr std::array PROGRAM_LUT{ [[nodiscard]] GLenum GetTextureBufferFormat(GLenum gl_format) { switch (gl_format) { case GL_RGBA8_SNORM: - return GL_RGBA8; + return GL_RGBA8I; case GL_R8_SNORM: - return GL_R8; + return GL_R8I; case GL_RGBA16_SNORM: - return GL_RGBA16; + return GL_RGBA16I; case GL_R16_SNORM: - return GL_R16; + return GL_R16I; case GL_RG16_SNORM: - return GL_RG16; + return GL_RG16I; case GL_RG8_SNORM: - return GL_RG8; + return GL_RG8I; default: return gl_format; } @@ -96,9 +96,6 @@ GLuint Buffer::View(u32 offset, u32 size, PixelFormat format) { texture.Create(GL_TEXTURE_BUFFER); const GLenum gl_format{MaxwellToGL::GetFormatTuple(format).internal_format}; const GLenum texture_format{GetTextureBufferFormat(gl_format)}; - if (texture_format != gl_format) { - LOG_WARNING(Render_OpenGL, "Emulating SNORM texture buffer with UNORM."); - } glTextureBufferRange(texture.handle, texture_format, buffer.handle, offset, size); views.push_back({ .offset = offset, @@ -168,7 +165,7 @@ void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) { if (has_unified_vertex_buffers) { buffer.MakeResident(GL_READ_ONLY); glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, buffer.HostGpuAddr() + offset, - static_cast(size)); + static_cast(Common::AlignUp(size, 4))); } else { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle()); index_buffer_offset = offset; diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp index 1f0f156..26d0660 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp @@ -28,12 +28,11 @@ bool ComputePipelineKey::operator==(const ComputePipelineKey& rhs) const noexcep } ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cache_, - BufferCache& buffer_cache_, Tegra::MemoryManager& gpu_memory_, - Tegra::Engines::KeplerCompute& kepler_compute_, - ProgramManager& program_manager_, const Shader::Info& info_, - std::string code, std::vector code_v) - : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, gpu_memory{gpu_memory_}, - kepler_compute{kepler_compute_}, program_manager{program_manager_}, info{info_} { + BufferCache& buffer_cache_, ProgramManager& program_manager_, + const Shader::Info& info_, std::string code, + std::vector code_v) + : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, + program_manager{program_manager_}, info{info_} { switch (device.GetShaderBackend()) { case Settings::ShaderBackend::GLSL: source_program = CreateProgram(code, GL_COMPUTE_SHADER); @@ -86,7 +85,7 @@ void ComputePipeline::Configure() { GLsizei texture_binding{}; GLsizei image_binding{}; - const auto& qmd{kepler_compute.launch_description}; + const auto& qmd{kepler_compute->launch_description}; const auto& cbufs{qmd.const_buffer_config}; const bool via_header_index{qmd.linked_tsc != 0}; const auto read_handle{[&](const auto& desc, u32 index) { @@ -101,12 +100,13 @@ void ComputePipeline::Configure() { const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset}; const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() + secondary_offset}; - const u32 lhs_raw{gpu_memory.Read(addr)}; - const u32 rhs_raw{gpu_memory.Read(separate_addr)}; + const u32 lhs_raw{gpu_memory->Read(addr) << desc.shift_left}; + const u32 rhs_raw{gpu_memory->Read(separate_addr) + << desc.secondary_shift_left}; return TexturePair(lhs_raw | rhs_raw, via_header_index); } } - return TexturePair(gpu_memory.Read(addr), via_header_index); + return TexturePair(gpu_memory->Read(addr), via_header_index); }}; const auto add_image{[&](const auto& desc, bool blacklist) { for (u32 index = 0; index < desc.count; ++index) { diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.h b/src/video_core/renderer_opengl/gl_compute_pipeline.h index 723f27f..6534dec 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.h +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.h @@ -49,10 +49,8 @@ static_assert(std::is_trivially_constructible_v); class ComputePipeline { public: explicit ComputePipeline(const Device& device, TextureCache& texture_cache_, - BufferCache& buffer_cache_, Tegra::MemoryManager& gpu_memory_, - Tegra::Engines::KeplerCompute& kepler_compute_, - ProgramManager& program_manager_, const Shader::Info& info_, - std::string code, std::vector code_v); + BufferCache& buffer_cache_, ProgramManager& program_manager_, + const Shader::Info& info_, std::string code, std::vector code_v); void Configure(); @@ -60,11 +58,17 @@ public: return writes_global_memory; } + void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_, + Tegra::MemoryManager* gpu_memory_) { + kepler_compute = kepler_compute_; + gpu_memory = gpu_memory_; + } + private: TextureCache& texture_cache; BufferCache& buffer_cache; - Tegra::MemoryManager& gpu_memory; - Tegra::Engines::KeplerCompute& kepler_compute; + Tegra::MemoryManager* gpu_memory; + Tegra::Engines::KeplerCompute* kepler_compute; ProgramManager& program_manager; Shader::Info info; diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 1663e27..cee5c32 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -14,6 +14,7 @@ #include "common/literals.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "shader_recompiler/stage.h" #include "video_core/renderer_opengl/gl_device.h" @@ -111,7 +112,7 @@ bool IsASTCSupported() { } } // Anonymous namespace -Device::Device() { +Device::Device(Core::Frontend::EmuWindow& emu_window) { if (!GLAD_GL_VERSION_4_6) { LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available"); throw std::runtime_error{"Insufficient version"}; @@ -125,9 +126,9 @@ Device::Device() { const bool is_intel = vendor_name == "Intel"; #ifdef __unix__ - const bool is_linux = true; + constexpr bool is_linux = true; #else - const bool is_linux = false; + constexpr bool is_linux = false; #endif bool disable_fast_buffer_sub_data = false; @@ -192,9 +193,11 @@ Device::Device() { } } + strict_context_required = emu_window.StrictContextRequired(); // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. + // Blocks EGL on Wayland from using asynchronous shader compilation. use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() && - !(is_amd || (is_intel && !is_linux)); + !(is_amd || (is_intel && !is_linux)) && !strict_context_required; use_driver_cache = is_nvidia; LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 5ef51eb..2a72d84 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -5,6 +5,7 @@ #include #include "common/common_types.h" +#include "core/frontend/emu_window.h" #include "shader_recompiler/stage.h" namespace Settings { @@ -15,7 +16,7 @@ namespace OpenGL { class Device { public: - explicit Device(); + explicit Device(Core::Frontend::EmuWindow& emu_window); [[nodiscard]] std::string GetVendorName() const; @@ -173,6 +174,10 @@ public: return can_report_memory; } + bool StrictContextRequired() const { + return strict_context_required; + } + private: static bool TestVariableAoffi(); static bool TestPreciseBug(); @@ -216,6 +221,7 @@ private: bool has_cbuf_ftou_bug{}; bool has_bool_ref_bug{}; bool can_report_memory{}; + bool strict_context_required{}; std::string vendor_name; }; diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp index 6e82c2e..91463f8 100644 --- a/src/video_core/renderer_opengl/gl_fence_manager.cpp +++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp @@ -10,10 +10,7 @@ namespace OpenGL { -GLInnerFence::GLInnerFence(u32 payload_, bool is_stubbed_) : FenceBase{payload_, is_stubbed_} {} - -GLInnerFence::GLInnerFence(GPUVAddr address_, u32 payload_, bool is_stubbed_) - : FenceBase{address_, payload_, is_stubbed_} {} +GLInnerFence::GLInnerFence(bool is_stubbed_) : FenceBase{is_stubbed_} {} GLInnerFence::~GLInnerFence() = default; @@ -48,12 +45,8 @@ FenceManagerOpenGL::FenceManagerOpenGL(VideoCore::RasterizerInterface& rasterize BufferCache& buffer_cache_, QueryCache& query_cache_) : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_} {} -Fence FenceManagerOpenGL::CreateFence(u32 value, bool is_stubbed) { - return std::make_shared(value, is_stubbed); -} - -Fence FenceManagerOpenGL::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) { - return std::make_shared(addr, value, is_stubbed); +Fence FenceManagerOpenGL::CreateFence(bool is_stubbed) { + return std::make_shared(is_stubbed); } void FenceManagerOpenGL::QueueFence(Fence& fence) { diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h index 14ff00d..f1446e7 100644 --- a/src/video_core/renderer_opengl/gl_fence_manager.h +++ b/src/video_core/renderer_opengl/gl_fence_manager.h @@ -16,8 +16,7 @@ namespace OpenGL { class GLInnerFence : public VideoCommon::FenceBase { public: - explicit GLInnerFence(u32 payload_, bool is_stubbed_); - explicit GLInnerFence(GPUVAddr address_, u32 payload_, bool is_stubbed_); + explicit GLInnerFence(bool is_stubbed_); ~GLInnerFence(); void Queue(); @@ -40,8 +39,7 @@ public: QueryCache& query_cache); protected: - Fence CreateFence(u32 value, bool is_stubbed) override; - Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override; + Fence CreateFence(bool is_stubbed) override; void QueueFence(Fence& fence) override; bool IsFenceSignaled(Fence& fence) const override; void WaitFence(Fence& fence) override; diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 67eae36..c115dab 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -73,8 +73,8 @@ GLenum AssemblyStage(size_t stage_index) { /// @param location Hardware location /// @return Pair of ARB_transform_feedback3 token stream first and third arguments /// @note Read https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_transform_feedback3.txt -std::pair TransformFeedbackEnum(u8 location) { - const u8 index = location / 4; +std::pair TransformFeedbackEnum(u32 location) { + const auto index = location / 4; if (index >= 8 && index <= 39) { return {GL_GENERIC_ATTRIB_NV, index - 8}; } @@ -169,15 +169,15 @@ ConfigureFuncPtr ConfigureFunc(const std::array& infos, u32 ena } } // Anonymous namespace -GraphicsPipeline::GraphicsPipeline( - const Device& device, TextureCache& texture_cache_, BufferCache& buffer_cache_, - Tegra::MemoryManager& gpu_memory_, Tegra::Engines::Maxwell3D& maxwell3d_, - ProgramManager& program_manager_, StateTracker& state_tracker_, ShaderWorker* thread_worker, - VideoCore::ShaderNotify* shader_notify, std::array sources, - std::array, 5> sources_spirv, const std::array& infos, - const GraphicsPipelineKey& key_) - : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, - gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_}, program_manager{program_manager_}, +GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_cache_, + BufferCache& buffer_cache_, ProgramManager& program_manager_, + StateTracker& state_tracker_, ShaderWorker* thread_worker, + VideoCore::ShaderNotify* shader_notify, + std::array sources, + std::array, 5> sources_spirv, + const std::array& infos, + const GraphicsPipelineKey& key_) + : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, state_tracker{state_tracker_}, key{key_} { if (shader_notify) { shader_notify->MarkShaderBuilding(); @@ -285,8 +285,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { buffer_cache.runtime.SetBaseStorageBindings(base_storage_bindings); buffer_cache.runtime.SetEnableStorageBuffers(use_storage_buffers); - const auto& regs{maxwell3d.regs}; - const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex}; + const auto& regs{maxwell3d->regs}; + const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE { const Shader::Info& info{stage_infos[stage]}; buffer_cache.UnbindGraphicsStorageBuffers(stage); @@ -299,7 +299,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { ++ssbo_index; } } - const auto& cbufs{maxwell3d.state.shader_stages[stage].const_buffers}; + const auto& cbufs{maxwell3d->state.shader_stages[stage].const_buffers}; const auto read_handle{[&](const auto& desc, u32 index) { ASSERT(cbufs[desc.cbuf_index].enabled); const u32 index_offset{index << desc.size_shift}; @@ -312,13 +312,14 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { const u32 second_offset{desc.secondary_cbuf_offset + index_offset}; const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address + second_offset}; - const u32 lhs_raw{gpu_memory.Read(addr)}; - const u32 rhs_raw{gpu_memory.Read(separate_addr)}; + const u32 lhs_raw{gpu_memory->Read(addr) << desc.shift_left}; + const u32 rhs_raw{gpu_memory->Read(separate_addr) + << desc.secondary_shift_left}; const u32 raw{lhs_raw | rhs_raw}; return TexturePair(raw, via_header_index); } } - return TexturePair(gpu_memory.Read(addr), via_header_index); + return TexturePair(gpu_memory->Read(addr), via_header_index); }}; const auto add_image{[&](const auto& desc, bool blacklist) LAMBDA_FORCEINLINE { for (u32 index = 0; index < desc.count; ++index) { @@ -502,6 +503,17 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { float_image_scaling_mask, down_factor, 0.0f); } } + if (info.uses_render_area) { + const auto render_area_width(static_cast(regs.surface_clip.width)); + const auto render_area_height(static_cast(regs.surface_clip.height)); + if (use_assembly) { + glProgramLocalParameter4fARB(AssemblyStage(stage), 1, render_area_width, + render_area_height, 0.0f, 0.0f); + } else { + glProgramUniform4f(source_programs[stage].handle, 1, render_area_width, + render_area_height, 0.0f, 0.0f); + } + } }}; if constexpr (Spec::enabled_stages[0]) { prepare_stage(0); @@ -556,10 +568,25 @@ void GraphicsPipeline::GenerateTransformFeedbackState() { ++current_stream; const auto& locations = key.xfb_state.varyings[feedback]; - std::optional current_index; + std::optional current_index; for (u32 offset = 0; offset < layout.varying_count; ++offset) { - const u8 location = locations[offset]; - const u8 index = location / 4; + const auto get_attribute = [&locations](u32 index) -> u32 { + switch (index % 4) { + case 0: + return locations[index / 4].attribute0.Value(); + case 1: + return locations[index / 4].attribute1.Value(); + case 2: + return locations[index / 4].attribute2.Value(); + case 3: + return locations[index / 4].attribute3.Value(); + } + UNREACHABLE(); + return 0; + }; + + const auto attribute{get_attribute(offset)}; + const auto index = attribute / 4U; if (current_index == index) { // Increase number of components of the previous attachment @@ -568,7 +595,7 @@ void GraphicsPipeline::GenerateTransformFeedbackState() { } current_index = index; - std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(location); + std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(attribute); cursor[1] = 1; cursor += XFB_ENTRY_STRIDE; } diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index 4ec15b9..ea53ddb 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h @@ -37,8 +37,8 @@ struct GraphicsPipelineKey { BitField<0, 1, u32> xfb_enabled; BitField<1, 1, u32> early_z; BitField<2, 4, Maxwell::PrimitiveTopology> gs_input_topology; - BitField<6, 2, Maxwell::TessellationPrimitive> tessellation_primitive; - BitField<8, 2, Maxwell::TessellationSpacing> tessellation_spacing; + BitField<6, 2, Maxwell::Tessellation::DomainType> tessellation_primitive; + BitField<8, 2, Maxwell::Tessellation::Spacing> tessellation_spacing; BitField<10, 1, u32> tessellation_clockwise; }; std::array padding; @@ -71,10 +71,9 @@ static_assert(std::is_trivially_constructible_v); class GraphicsPipeline { public: explicit GraphicsPipeline(const Device& device, TextureCache& texture_cache_, - BufferCache& buffer_cache_, Tegra::MemoryManager& gpu_memory_, - Tegra::Engines::Maxwell3D& maxwell3d_, - ProgramManager& program_manager_, StateTracker& state_tracker_, - ShaderWorker* thread_worker, VideoCore::ShaderNotify* shader_notify, + BufferCache& buffer_cache_, ProgramManager& program_manager_, + StateTracker& state_tracker_, ShaderWorker* thread_worker, + VideoCore::ShaderNotify* shader_notify, std::array sources, std::array, 5> sources_spirv, const std::array& infos, @@ -107,6 +106,11 @@ public: }; } + void SetEngine(Tegra::Engines::Maxwell3D* maxwell3d_, Tegra::MemoryManager* gpu_memory_) { + maxwell3d = maxwell3d_; + gpu_memory = gpu_memory_; + } + private: template void ConfigureImpl(bool is_indexed); @@ -119,8 +123,8 @@ private: TextureCache& texture_cache; BufferCache& buffer_cache; - Tegra::MemoryManager& gpu_memory; - Tegra::Engines::Maxwell3D& maxwell3d; + Tegra::MemoryManager* gpu_memory; + Tegra::Engines::Maxwell3D* maxwell3d; ProgramManager& program_manager; StateTracker& state_tracker; const GraphicsPipelineKey key; diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp index ed40f57..5070db4 100644 --- a/src/video_core/renderer_opengl/gl_query_cache.cpp +++ b/src/video_core/renderer_opengl/gl_query_cache.cpp @@ -26,9 +26,8 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) { } // Anonymous namespace -QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::MemoryManager& gpu_memory_) - : QueryCacheBase(rasterizer_, maxwell3d_, gpu_memory_), gl_rasterizer{rasterizer_} {} +QueryCache::QueryCache(RasterizerOpenGL& rasterizer_) + : QueryCacheBase(rasterizer_), gl_rasterizer{rasterizer_} {} QueryCache::~QueryCache() = default; diff --git a/src/video_core/renderer_opengl/gl_query_cache.h b/src/video_core/renderer_opengl/gl_query_cache.h index 8a49f1e..14ce599 100644 --- a/src/video_core/renderer_opengl/gl_query_cache.h +++ b/src/video_core/renderer_opengl/gl_query_cache.h @@ -28,8 +28,7 @@ using CounterStream = VideoCommon::CounterStreamBase; class QueryCache final : public VideoCommon::QueryCacheBase { public: - explicit QueryCache(RasterizerOpenGL& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::MemoryManager& gpu_memory_); + explicit QueryCache(RasterizerOpenGL& rasterizer_); ~QueryCache(); OGLQuery AllocateQuery(VideoCore::QueryType type); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index a0d048b..a44b8c4 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -16,7 +16,7 @@ #include "common/microprofile.h" #include "common/scope_exit.h" #include "common/settings.h" - +#include "video_core/control/channel_state.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/memory_manager.h" @@ -56,22 +56,20 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra Core::Memory::Memory& cpu_memory_, const Device& device_, ScreenInfo& screen_info_, ProgramManager& program_manager_, StateTracker& state_tracker_) - : RasterizerAccelerated(cpu_memory_), gpu(gpu_), maxwell3d(gpu.Maxwell3D()), - kepler_compute(gpu.KeplerCompute()), gpu_memory(gpu.MemoryManager()), device(device_), - screen_info(screen_info_), program_manager(program_manager_), state_tracker(state_tracker_), + : RasterizerAccelerated(cpu_memory_), gpu(gpu_), device(device_), screen_info(screen_info_), + program_manager(program_manager_), state_tracker(state_tracker_), texture_cache_runtime(device, program_manager, state_tracker), - texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), - buffer_cache_runtime(device), - buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), - shader_cache(*this, emu_window_, maxwell3d, kepler_compute, gpu_memory, device, texture_cache, - buffer_cache, program_manager, state_tracker, gpu.ShaderNotify()), - query_cache(*this, maxwell3d, gpu_memory), accelerate_dma(buffer_cache), + texture_cache(texture_cache_runtime, *this), buffer_cache_runtime(device), + buffer_cache(*this, cpu_memory_, buffer_cache_runtime), + shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager, + state_tracker, gpu.ShaderNotify()), + query_cache(*this), accelerate_dma(buffer_cache), fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache) {} RasterizerOpenGL::~RasterizerOpenGL() = default; void RasterizerOpenGL::SyncVertexFormats() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::VertexFormats]) { return; } @@ -89,7 +87,7 @@ void RasterizerOpenGL::SyncVertexFormats() { } flags[Dirty::VertexFormat0 + index] = false; - const auto attrib = maxwell3d.regs.vertex_attrib_format[index]; + const auto& attrib = maxwell3d->regs.vertex_attrib_format[index]; const auto gl_index = static_cast(index); // Disable constant attributes. @@ -99,8 +97,8 @@ void RasterizerOpenGL::SyncVertexFormats() { } glEnableVertexAttribArray(gl_index); - if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt || - attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) { + if (attrib.type == Maxwell::VertexAttribute::Type::SInt || + attrib.type == Maxwell::VertexAttribute::Type::UInt) { glVertexAttribIFormat(gl_index, attrib.ComponentCount(), MaxwellToGL::VertexFormat(attrib), attrib.offset); } else { @@ -113,13 +111,13 @@ void RasterizerOpenGL::SyncVertexFormats() { } void RasterizerOpenGL::SyncVertexInstances() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::VertexInstances]) { return; } flags[Dirty::VertexInstances] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) { if (!flags[Dirty::VertexInstance0 + index]) { continue; @@ -127,8 +125,8 @@ void RasterizerOpenGL::SyncVertexInstances() { flags[Dirty::VertexInstance0 + index] = false; const auto gl_index = static_cast(index); - const bool instancing_enabled = regs.instanced_arrays.IsInstancingEnabled(gl_index); - const GLuint divisor = instancing_enabled ? regs.vertex_array[index].divisor : 0; + const bool instancing_enabled = regs.vertex_stream_instances.IsInstancingEnabled(gl_index); + const GLuint divisor = instancing_enabled ? regs.vertex_streams[index].frequency : 0; glVertexBindingDivisor(gl_index, divisor); } } @@ -138,38 +136,35 @@ void RasterizerOpenGL::LoadDiskResources(u64 title_id, std::stop_token stop_load shader_cache.LoadDiskResources(title_id, stop_loading, callback); } -void RasterizerOpenGL::Clear() { +void RasterizerOpenGL::Clear(u32 layer_count) { MICROPROFILE_SCOPE(OpenGL_Clears); - if (!maxwell3d.ShouldExecute()) { - return; - } - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; bool use_color{}; bool use_depth{}; bool use_stencil{}; - if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || - regs.clear_buffers.A) { + if (regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B || + regs.clear_surface.A) { use_color = true; - const GLuint index = regs.clear_buffers.RT; + const GLuint index = regs.clear_surface.RT; state_tracker.NotifyColorMask(index); - glColorMaski(index, regs.clear_buffers.R != 0, regs.clear_buffers.G != 0, - regs.clear_buffers.B != 0, regs.clear_buffers.A != 0); + glColorMaski(index, regs.clear_surface.R != 0, regs.clear_surface.G != 0, + regs.clear_surface.B != 0, regs.clear_surface.A != 0); // TODO(Rodrigo): Determine if clamping is used on clears SyncFragmentColorClampState(); SyncFramebufferSRGB(); } - if (regs.clear_buffers.Z) { + if (regs.clear_surface.Z) { ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); use_depth = true; state_tracker.NotifyDepthMask(); glDepthMask(GL_TRUE); } - if (regs.clear_buffers.S) { + if (regs.clear_surface.S) { ASSERT_MSG(regs.zeta_enable, "Tried to clear stencil but buffer is not enabled!"); use_stencil = true; } @@ -186,16 +181,16 @@ void RasterizerOpenGL::Clear() { texture_cache.UpdateRenderTargets(true); state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle()); SyncViewport(); - if (regs.clear_flags.scissor) { + if (regs.clear_control.use_scissor) { SyncScissorTest(); } else { state_tracker.NotifyScissor0(); glDisablei(GL_SCISSOR_TEST, 0); } - UNIMPLEMENTED_IF(regs.clear_flags.viewport); + UNIMPLEMENTED_IF(regs.clear_control.use_viewport_clip0); if (use_color) { - glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color); + glClearBufferfv(GL_COLOR, regs.clear_surface.RT, regs.clear_color.data()); } if (use_depth && use_stencil) { glClearBufferfi(GL_DEPTH_STENCIL, 0, regs.clear_depth, regs.clear_stencil); @@ -207,7 +202,7 @@ void RasterizerOpenGL::Clear() { ++num_queued_commands; } -void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { +void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { MICROPROFILE_SCOPE(OpenGL_Drawing); SCOPE_EXIT({ gpu.TickWork(); }); @@ -217,22 +212,27 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { if (!pipeline) { return; } + + gpu.TickWork(); + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->Configure(is_indexed); SyncState(); - const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d.regs.draw.topology); + const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + + const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(draw_state.topology); BeginTransformFeedback(pipeline, primitive_mode); - const GLuint base_instance = static_cast(maxwell3d.regs.vb_base_instance); - const GLsizei num_instances = - static_cast(is_instanced ? maxwell3d.mme_draw.instance_count : 1); + const GLuint base_instance = static_cast(draw_state.base_instance); + const GLsizei num_instances = static_cast(instance_count); if (is_indexed) { - const GLint base_vertex = static_cast(maxwell3d.regs.vb_element_base); - const GLsizei num_vertices = static_cast(maxwell3d.regs.index_array.count); + const GLint base_vertex = static_cast(draw_state.base_index); + const GLsizei num_vertices = static_cast(draw_state.index_buffer.count); const GLvoid* const offset = buffer_cache_runtime.IndexOffset(); - const GLenum format = MaxwellToGL::IndexFormat(maxwell3d.regs.index_array.format); + const GLenum format = MaxwellToGL::IndexFormat(draw_state.index_buffer.format); if (num_instances == 1 && base_instance == 0 && base_vertex == 0) { glDrawElements(primitive_mode, num_vertices, format, offset); } else if (num_instances == 1 && base_instance == 0) { @@ -251,8 +251,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { base_instance); } } else { - const GLint base_vertex = static_cast(maxwell3d.regs.vertex_buffer.first); - const GLsizei num_vertices = static_cast(maxwell3d.regs.vertex_buffer.count); + const GLint base_vertex = static_cast(draw_state.vertex_buffer.first); + const GLsizei num_vertices = static_cast(draw_state.vertex_buffer.count); if (num_instances == 1 && base_instance == 0) { glDrawArrays(primitive_mode, base_vertex, num_vertices); } else if (base_instance == 0) { @@ -273,8 +273,9 @@ void RasterizerOpenGL::DispatchCompute() { if (!pipeline) { return; } + pipeline->SetEngine(kepler_compute, gpu_memory); pipeline->Configure(); - const auto& qmd{kepler_compute.launch_description}; + const auto& qmd{kepler_compute->launch_description}; glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z); ++num_queued_commands; has_written_global_memory |= pipeline->WritesGlobalMemory(); @@ -359,7 +360,7 @@ void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { } } -void RasterizerOpenGL::SyncGuestHost() { +void RasterizerOpenGL::InvalidateGPUCache() { MICROPROFILE_SCOPE(OpenGL_CacheManagement); shader_cache.SyncGuestHost(); { @@ -380,40 +381,30 @@ void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) { shader_cache.OnCPUWrite(addr, size); } -void RasterizerOpenGL::ModifyGPUMemory(GPUVAddr addr, u64 size) { +void RasterizerOpenGL::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { { std::scoped_lock lock{texture_cache.mutex}; - texture_cache.UnmapGPUMemory(addr, size); + texture_cache.UnmapGPUMemory(as_id, addr, size); } } -void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) { - if (!gpu.IsAsync()) { - gpu_memory.Write(addr, value); - return; - } - fence_manager.SignalSemaphore(addr, value); +void RasterizerOpenGL::SignalFence(std::function&& func) { + fence_manager.SignalFence(std::move(func)); +} + +void RasterizerOpenGL::SyncOperation(std::function&& func) { + fence_manager.SyncOperation(std::move(func)); } void RasterizerOpenGL::SignalSyncPoint(u32 value) { - if (!gpu.IsAsync()) { - gpu.IncrementSyncPoint(value); - return; - } fence_manager.SignalSyncPoint(value); } void RasterizerOpenGL::SignalReference() { - if (!gpu.IsAsync()) { - return; - } fence_manager.SignalOrdering(); } void RasterizerOpenGL::ReleaseFences() { - if (!gpu.IsAsync()) { - return; - } fence_manager.WaitPendingFences(); } @@ -430,6 +421,7 @@ void RasterizerOpenGL::WaitForIdle() { } void RasterizerOpenGL::FragmentBarrier() { + glTextureBarrier(); glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT); } @@ -473,8 +465,7 @@ bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf const Tegra::Engines::Fermi2D::Config& copy_config) { MICROPROFILE_SCOPE(OpenGL_Blits); std::scoped_lock lock{texture_cache.mutex}; - texture_cache.BlitImage(dst, src, copy_config); - return true; + return texture_cache.BlitImage(dst, src, copy_config); } Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA() { @@ -482,13 +473,13 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA() } void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, - std::span memory) { - auto cpu_addr = gpu_memory.GpuToCpuAddress(address); + std::span memory) { + auto cpu_addr = gpu_memory->GpuToCpuAddress(address); if (!cpu_addr) [[unlikely]] { - gpu_memory.WriteBlock(address, memory.data(), copy_size); + gpu_memory->WriteBlock(address, memory.data(), copy_size); return; } - gpu_memory.WriteBlockUnsafe(address, memory.data(), copy_size); + gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); { std::unique_lock lock{buffer_cache.mutex}; if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { @@ -551,8 +542,8 @@ void RasterizerOpenGL::SyncState() { } void RasterizerOpenGL::SyncViewport() { - auto& flags = maxwell3d.dirty.flags; - const auto& regs = maxwell3d.regs; + auto& flags = maxwell3d->dirty.flags; + const auto& regs = maxwell3d->regs; const bool rescale_viewports = flags[VideoCommon::Dirty::RescaleViewports]; const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports; @@ -561,9 +552,9 @@ void RasterizerOpenGL::SyncViewport() { if (dirty_viewport || dirty_clip_control || flags[Dirty::FrontFace]) { flags[Dirty::FrontFace] = false; - GLenum mode = MaxwellToGL::FrontFace(regs.front_face); + GLenum mode = MaxwellToGL::FrontFace(regs.gl_front_face); bool flip_faces = true; - if (regs.screen_y_control.triangle_rast_flip != 0) { + if (regs.window_origin.flip_y != 0) { flip_faces = !flip_faces; } if (regs.viewport_transform[0].scale_y < 0.0f) { @@ -588,14 +579,15 @@ void RasterizerOpenGL::SyncViewport() { if (regs.viewport_transform[0].scale_y < 0.0f) { flip_y = !flip_y; } - if (regs.screen_y_control.y_negate != 0) { + const bool lower_left{regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft}; + if (lower_left) { flip_y = !flip_y; } const bool is_zero_to_one = regs.depth_mode == Maxwell::DepthMode::ZeroToOne; const GLenum origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT; const GLenum depth = is_zero_to_one ? GL_ZERO_TO_ONE : GL_NEGATIVE_ONE_TO_ONE; state_tracker.ClipControl(origin, depth); - state_tracker.SetYNegate(regs.screen_y_control.y_negate != 0); + state_tracker.SetYNegate(lower_left); } const bool is_rescaling{texture_cache.IsRescaling()}; const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; @@ -622,6 +614,16 @@ void RasterizerOpenGL::SyncViewport() { } flags[Dirty::Viewport0 + index] = false; + if (!regs.viewport_scale_offset_enabled) { + const auto x = static_cast(regs.surface_clip.x); + const auto y = static_cast(regs.surface_clip.y); + const auto width = static_cast(regs.surface_clip.width); + const auto height = static_cast(regs.surface_clip.height); + glViewportIndexedf(static_cast(index), x, y, width != 0.0f ? width : 1.0f, + height != 0.0f ? height : 1.0f); + continue; + } + const auto& src = regs.viewport_transform[index]; GLfloat x = conv(src.translate_x - src.scale_x); GLfloat y = conv(src.translate_y - src.scale_y); @@ -657,23 +659,29 @@ void RasterizerOpenGL::SyncViewport() { } void RasterizerOpenGL::SyncDepthClamp() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::DepthClampEnabled]) { return; } flags[Dirty::DepthClampEnabled] = false; - oglEnable(GL_DEPTH_CLAMP, maxwell3d.regs.view_volume_clip_control.depth_clamp_disabled == 0); + bool depth_clamp_disabled{maxwell3d->regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::Passthrough || + maxwell3d->regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || + maxwell3d->regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumZ}; + oglEnable(GL_DEPTH_CLAMP, !depth_clamp_disabled); } void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::ClipDistances] && !flags[VideoCommon::Dirty::Shaders]) { return; } flags[Dirty::ClipDistances] = false; - clip_mask &= maxwell3d.regs.clip_distance_enabled; + clip_mask &= maxwell3d->regs.user_clip_enable.raw; if (clip_mask == last_clip_distance_mask) { return; } @@ -689,15 +697,15 @@ void RasterizerOpenGL::SyncClipCoef() { } void RasterizerOpenGL::SyncCullMode() { - auto& flags = maxwell3d.dirty.flags; - const auto& regs = maxwell3d.regs; + auto& flags = maxwell3d->dirty.flags; + const auto& regs = maxwell3d->regs; if (flags[Dirty::CullTest]) { flags[Dirty::CullTest] = false; - if (regs.cull_test_enabled) { + if (regs.gl_cull_test_enabled) { glEnable(GL_CULL_FACE); - glCullFace(MaxwellToGL::CullFace(regs.cull_face)); + glCullFace(MaxwellToGL::CullFace(regs.gl_cull_face)); } else { glDisable(GL_CULL_FACE); } @@ -705,23 +713,23 @@ void RasterizerOpenGL::SyncCullMode() { } void RasterizerOpenGL::SyncPrimitiveRestart() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::PrimitiveRestart]) { return; } flags[Dirty::PrimitiveRestart] = false; - if (maxwell3d.regs.primitive_restart.enabled) { + if (maxwell3d->regs.primitive_restart.enabled) { glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(maxwell3d.regs.primitive_restart.index); + glPrimitiveRestartIndex(maxwell3d->regs.primitive_restart.index); } else { glDisable(GL_PRIMITIVE_RESTART); } } void RasterizerOpenGL::SyncDepthTestState() { - auto& flags = maxwell3d.dirty.flags; - const auto& regs = maxwell3d.regs; + auto& flags = maxwell3d->dirty.flags; + const auto& regs = maxwell3d->regs; if (flags[Dirty::DepthMask]) { flags[Dirty::DepthMask] = false; @@ -740,28 +748,28 @@ void RasterizerOpenGL::SyncDepthTestState() { } void RasterizerOpenGL::SyncStencilTestState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::StencilTest]) { return; } flags[Dirty::StencilTest] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; oglEnable(GL_STENCIL_TEST, regs.stencil_enable); - glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func), - regs.stencil_front_func_ref, regs.stencil_front_func_mask); - glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail), - MaxwellToGL::StencilOp(regs.stencil_front_op_zfail), - MaxwellToGL::StencilOp(regs.stencil_front_op_zpass)); + glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_op.func), + regs.stencil_front_ref, regs.stencil_front_func_mask); + glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op.fail), + MaxwellToGL::StencilOp(regs.stencil_front_op.zfail), + MaxwellToGL::StencilOp(regs.stencil_front_op.zpass)); glStencilMaskSeparate(GL_FRONT, regs.stencil_front_mask); if (regs.stencil_two_side_enable) { - glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_func_func), - regs.stencil_back_func_ref, regs.stencil_back_func_mask); - glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op_fail), - MaxwellToGL::StencilOp(regs.stencil_back_op_zfail), - MaxwellToGL::StencilOp(regs.stencil_back_op_zpass)); + glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_op.func), + regs.stencil_back_ref, regs.stencil_back_func_mask); + glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op.fail), + MaxwellToGL::StencilOp(regs.stencil_back_op.zfail), + MaxwellToGL::StencilOp(regs.stencil_back_op.zpass)); glStencilMaskSeparate(GL_BACK, regs.stencil_back_mask); } else { glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 0xFFFFFFFF); @@ -771,24 +779,24 @@ void RasterizerOpenGL::SyncStencilTestState() { } void RasterizerOpenGL::SyncRasterizeEnable() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::RasterizeEnable]) { return; } flags[Dirty::RasterizeEnable] = false; - oglEnable(GL_RASTERIZER_DISCARD, maxwell3d.regs.rasterize_enable == 0); + oglEnable(GL_RASTERIZER_DISCARD, maxwell3d->regs.rasterize_enable == 0); } void RasterizerOpenGL::SyncPolygonModes() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::PolygonModes]) { return; } flags[Dirty::PolygonModes] = false; - const auto& regs = maxwell3d.regs; - if (regs.fill_rectangle) { + const auto& regs = maxwell3d->regs; + if (regs.fill_via_triangle_mode != Maxwell::FillViaTriangleMode::Disabled) { if (!GLAD_GL_NV_fill_rectangle) { LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported"); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); @@ -820,7 +828,7 @@ void RasterizerOpenGL::SyncPolygonModes() { } void RasterizerOpenGL::SyncColorMask() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::ColorMasks]) { return; } @@ -829,7 +837,7 @@ void RasterizerOpenGL::SyncColorMask() { const bool force = flags[Dirty::ColorMaskCommon]; flags[Dirty::ColorMaskCommon] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (regs.color_mask_common) { if (!force && !flags[Dirty::ColorMask0]) { return; @@ -854,30 +862,31 @@ void RasterizerOpenGL::SyncColorMask() { } void RasterizerOpenGL::SyncMultiSampleState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::MultisampleControl]) { return; } flags[Dirty::MultisampleControl] = false; - const auto& regs = maxwell3d.regs; - oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage); - oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one); + const auto& regs = maxwell3d->regs; + oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.anti_alias_alpha_control.alpha_to_coverage); + oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.anti_alias_alpha_control.alpha_to_one); } void RasterizerOpenGL::SyncFragmentColorClampState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::FragmentClampColor]) { return; } flags[Dirty::FragmentClampColor] = false; - glClampColor(GL_CLAMP_FRAGMENT_COLOR, maxwell3d.regs.frag_color_clamp ? GL_TRUE : GL_FALSE); + glClampColor(GL_CLAMP_FRAGMENT_COLOR, + maxwell3d->regs.frag_color_clamp.AnyEnabled() ? GL_TRUE : GL_FALSE); } void RasterizerOpenGL::SyncBlendState() { - auto& flags = maxwell3d.dirty.flags; - const auto& regs = maxwell3d.regs; + auto& flags = maxwell3d->dirty.flags; + const auto& regs = maxwell3d->regs; if (flags[Dirty::BlendColor]) { flags[Dirty::BlendColor] = false; @@ -892,18 +901,18 @@ void RasterizerOpenGL::SyncBlendState() { } flags[Dirty::BlendStates] = false; - if (!regs.independent_blend_enable) { + if (!regs.blend_per_target_enabled) { if (!regs.blend.enable[0]) { glDisable(GL_BLEND); return; } glEnable(GL_BLEND); - glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb), - MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb), - MaxwellToGL::BlendFunc(regs.blend.factor_source_a), - MaxwellToGL::BlendFunc(regs.blend.factor_dest_a)); - glBlendEquationSeparate(MaxwellToGL::BlendEquation(regs.blend.equation_rgb), - MaxwellToGL::BlendEquation(regs.blend.equation_a)); + glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.color_source), + MaxwellToGL::BlendFunc(regs.blend.color_dest), + MaxwellToGL::BlendFunc(regs.blend.alpha_source), + MaxwellToGL::BlendFunc(regs.blend.alpha_dest)); + glBlendEquationSeparate(MaxwellToGL::BlendEquation(regs.blend.color_op), + MaxwellToGL::BlendEquation(regs.blend.alpha_op)); return; } @@ -922,35 +931,34 @@ void RasterizerOpenGL::SyncBlendState() { } glEnablei(GL_BLEND, static_cast(i)); - const auto& src = regs.independent_blend[i]; - glBlendFuncSeparatei(static_cast(i), MaxwellToGL::BlendFunc(src.factor_source_rgb), - MaxwellToGL::BlendFunc(src.factor_dest_rgb), - MaxwellToGL::BlendFunc(src.factor_source_a), - MaxwellToGL::BlendFunc(src.factor_dest_a)); - glBlendEquationSeparatei(static_cast(i), - MaxwellToGL::BlendEquation(src.equation_rgb), - MaxwellToGL::BlendEquation(src.equation_a)); + const auto& src = regs.blend_per_target[i]; + glBlendFuncSeparatei(static_cast(i), MaxwellToGL::BlendFunc(src.color_source), + MaxwellToGL::BlendFunc(src.color_dest), + MaxwellToGL::BlendFunc(src.alpha_source), + MaxwellToGL::BlendFunc(src.alpha_dest)); + glBlendEquationSeparatei(static_cast(i), MaxwellToGL::BlendEquation(src.color_op), + MaxwellToGL::BlendEquation(src.alpha_op)); } } void RasterizerOpenGL::SyncLogicOpState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::LogicOp]) { return; } flags[Dirty::LogicOp] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (regs.logic_op.enable) { glEnable(GL_COLOR_LOGIC_OP); - glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation)); + glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.op)); } else { glDisable(GL_COLOR_LOGIC_OP); } } void RasterizerOpenGL::SyncScissorTest() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::Scissors] && !flags[VideoCommon::Dirty::RescaleScissors]) { return; } @@ -959,7 +967,7 @@ void RasterizerOpenGL::SyncScissorTest() { const bool force = flags[VideoCommon::Dirty::RescaleScissors]; flags[VideoCommon::Dirty::RescaleScissors] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; const auto& resolution = Settings::values.resolution_info; const bool is_rescaling{texture_cache.IsRescaling()}; @@ -995,39 +1003,39 @@ void RasterizerOpenGL::SyncScissorTest() { } void RasterizerOpenGL::SyncPointState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::PointSize]) { return; } flags[Dirty::PointSize] = false; - oglEnable(GL_POINT_SPRITE, maxwell3d.regs.point_sprite_enable); - oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d.regs.vp_point_size.enable); + oglEnable(GL_POINT_SPRITE, maxwell3d->regs.point_sprite_enable); + oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d->regs.point_size_attribute.enabled); const bool is_rescaling{texture_cache.IsRescaling()}; const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; - glPointSize(std::max(1.0f, maxwell3d.regs.point_size * scale)); + glPointSize(std::max(1.0f, maxwell3d->regs.point_size * scale)); } void RasterizerOpenGL::SyncLineState() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::LineWidth]) { return; } flags[Dirty::LineWidth] = false; - const auto& regs = maxwell3d.regs; - oglEnable(GL_LINE_SMOOTH, regs.line_smooth_enable); - glLineWidth(regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased); + const auto& regs = maxwell3d->regs; + oglEnable(GL_LINE_SMOOTH, regs.line_anti_alias_enable); + glLineWidth(regs.line_anti_alias_enable ? regs.line_width_smooth : regs.line_width_aliased); } void RasterizerOpenGL::SyncPolygonOffset() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::PolygonOffset]) { return; } flags[Dirty::PolygonOffset] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable); oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable); oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable); @@ -1035,19 +1043,19 @@ void RasterizerOpenGL::SyncPolygonOffset() { if (regs.polygon_offset_fill_enable || regs.polygon_offset_line_enable || regs.polygon_offset_point_enable) { // Hardware divides polygon offset units by two - glPolygonOffsetClamp(regs.polygon_offset_factor, regs.polygon_offset_units / 2.0f, - regs.polygon_offset_clamp); + glPolygonOffsetClamp(regs.slope_scale_depth_bias, regs.depth_bias / 2.0f, + regs.depth_bias_clamp); } } void RasterizerOpenGL::SyncAlphaTest() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::AlphaTest]) { return; } flags[Dirty::AlphaTest] = false; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (regs.alpha_test_enabled) { glEnable(GL_ALPHA_TEST); glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref); @@ -1057,25 +1065,25 @@ void RasterizerOpenGL::SyncAlphaTest() { } void RasterizerOpenGL::SyncFramebufferSRGB() { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::FramebufferSRGB]) { return; } flags[Dirty::FramebufferSRGB] = false; - oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d.regs.framebuffer_srgb); + oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d->regs.framebuffer_srgb); } void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum primitive_mode) { - const auto& regs = maxwell3d.regs; - if (regs.tfb_enabled == 0) { + const auto& regs = maxwell3d->regs; + if (regs.transform_feedback_enabled == 0) { return; } program->ConfigureTransformFeedback(); - UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || - regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || - regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); + UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) || + regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) || + regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry)); UNIMPLEMENTED_IF(primitive_mode != GL_POINTS); // We may have to call BeginTransformFeedbackNV here since they seem to call different @@ -1086,11 +1094,48 @@ void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum } void RasterizerOpenGL::EndTransformFeedback() { - if (maxwell3d.regs.tfb_enabled != 0) { + if (maxwell3d->regs.transform_feedback_enabled != 0) { glEndTransformFeedback(); } } +void RasterizerOpenGL::InitializeChannel(Tegra::Control::ChannelState& channel) { + CreateChannel(channel); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.CreateChannel(channel); + buffer_cache.CreateChannel(channel); + } + shader_cache.CreateChannel(channel); + query_cache.CreateChannel(channel); + state_tracker.SetupTables(channel); +} + +void RasterizerOpenGL::BindChannel(Tegra::Control::ChannelState& channel) { + const s32 channel_id = channel.bind_id; + BindToChannel(channel_id); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.BindToChannel(channel_id); + buffer_cache.BindToChannel(channel_id); + } + shader_cache.BindToChannel(channel_id); + query_cache.BindToChannel(channel_id); + state_tracker.ChangeChannel(channel); + state_tracker.InvalidateState(); +} + +void RasterizerOpenGL::ReleaseChannel(s32 channel_id) { + EraseChannel(channel_id); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.EraseChannel(channel_id); + buffer_cache.EraseChannel(channel_id); + } + shader_cache.EraseChannel(channel_id); + query_cache.EraseChannel(channel_id); +} + AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 31a16fc..fc183c3 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -12,6 +12,7 @@ #include #include "common/common_types.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/engines/maxwell_dma.h" #include "video_core/rasterizer_accelerated.h" #include "video_core/rasterizer_interface.h" @@ -58,7 +59,8 @@ private: BufferCache& buffer_cache; }; -class RasterizerOpenGL : public VideoCore::RasterizerAccelerated { +class RasterizerOpenGL : public VideoCore::RasterizerAccelerated, + protected VideoCommon::ChannelSetupCaches { public: explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, Core::Memory::Memory& cpu_memory_, const Device& device_, @@ -66,8 +68,8 @@ public: StateTracker& state_tracker_); ~RasterizerOpenGL() override; - void Draw(bool is_indexed, bool is_instanced) override; - void Clear() override; + void Draw(bool is_indexed, u32 instance_count) override; + void Clear(u32 layer_count) override; void DispatchCompute() override; void ResetCounter(VideoCore::QueryType type) override; void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional timestamp) override; @@ -78,10 +80,11 @@ public: bool MustFlushRegion(VAddr addr, u64 size) override; void InvalidateRegion(VAddr addr, u64 size) override; void OnCPUWrite(VAddr addr, u64 size) override; - void SyncGuestHost() override; + void InvalidateGPUCache() override; void UnmapMemory(VAddr addr, u64 size) override; - void ModifyGPUMemory(GPUVAddr addr, u64 size) override; - void SignalSemaphore(GPUVAddr addr, u32 value) override; + void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; + void SignalFence(std::function&& func) override; + void SyncOperation(std::function&& func) override; void SignalSyncPoint(u32 value) override; void SignalReference() override; void ReleaseFences() override; @@ -96,7 +99,7 @@ public: const Tegra::Engines::Fermi2D::Config& copy_config) override; Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, - std::span memory) override; + std::span memory) override; bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) override; void LoadDiskResources(u64 title_id, std::stop_token stop_loading, @@ -107,6 +110,12 @@ public: return num_queued_commands > 0; } + void InitializeChannel(Tegra::Control::ChannelState& channel) override; + + void BindChannel(Tegra::Control::ChannelState& channel) override; + + void ReleaseChannel(s32 channel_id) override; + private: static constexpr size_t MAX_TEXTURES = 192; static constexpr size_t MAX_IMAGES = 48; @@ -191,9 +200,6 @@ private: void EndTransformFeedback(); Tegra::GPU& gpu; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; - Tegra::MemoryManager& gpu_memory; const Device& device; ScreenInfo& screen_info; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index ddb7093..f8868a0 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -22,6 +22,7 @@ #include "shader_recompiler/frontend/maxwell/control_flow.h" #include "shader_recompiler/frontend/maxwell/translate_program.h" #include "shader_recompiler/profile.h" +#include "video_core/engines/draw_manager.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/memory_manager.h" @@ -39,6 +40,7 @@ using Shader::Backend::GLASM::EmitGLASM; using Shader::Backend::GLSL::EmitGLSL; using Shader::Backend::SPIRV::EmitSPIRV; using Shader::Maxwell::ConvertLegacyToGeneric; +using Shader::Maxwell::GenerateGeometryPassthrough; using Shader::Maxwell::MergeDualVertexPrograms; using Shader::Maxwell::TranslateProgram; using VideoCommon::ComputeEnvironment; @@ -49,13 +51,24 @@ using VideoCommon::LoadPipelines; using VideoCommon::SerializePipeline; using Context = ShaderContext::Context; -constexpr u32 CACHE_VERSION = 6; +constexpr u32 CACHE_VERSION = 7; template auto MakeSpan(Container& container) { return std::span(container.data(), container.size()); } +Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::OutputTopology::PointList; + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::OutputTopology::LineStrip; + default: + return Shader::OutputTopology::TriangleStrip; + } +} + Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, const Shader::IR::Program& program, const Shader::IR::Program* previous_program, @@ -63,6 +76,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, Shader::RuntimeInfo info; if (previous_program) { info.previous_stage_stores = previous_program->info.stores; + info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping; } else { // Mark all stores as available for vertex shaders info.previous_stage_stores.mask.set(); @@ -75,14 +89,15 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, } break; case Shader::Stage::TessellationEval: - info.tess_clockwise = key.tessellation_clockwise != 0; + // Flip the face, as OpenGL's drawing is flipped. + info.tess_clockwise = key.tessellation_clockwise == 0; info.tess_primitive = [&key] { switch (key.tessellation_primitive) { - case Maxwell::TessellationPrimitive::Isolines: + case Maxwell::Tessellation::DomainType::Isolines: return Shader::TessPrimitive::Isolines; - case Maxwell::TessellationPrimitive::Triangles: + case Maxwell::Tessellation::DomainType::Triangles: return Shader::TessPrimitive::Triangles; - case Maxwell::TessellationPrimitive::Quads: + case Maxwell::Tessellation::DomainType::Quads: return Shader::TessPrimitive::Quads; } ASSERT(false); @@ -90,11 +105,11 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, }(); info.tess_spacing = [&] { switch (key.tessellation_spacing) { - case Maxwell::TessellationSpacing::Equal: + case Maxwell::Tessellation::Spacing::Integer: return Shader::TessSpacing::Equal; - case Maxwell::TessellationSpacing::FractionalOdd: + case Maxwell::Tessellation::Spacing::FractionalOdd: return Shader::TessSpacing::FractionalOdd; - case Maxwell::TessellationSpacing::FractionalEven: + case Maxwell::Tessellation::Spacing::FractionalEven: return Shader::TessSpacing::FractionalEven; } ASSERT(false); @@ -139,28 +154,27 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, } void SetXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) { - std::ranges::transform(regs.tfb_layouts, state.layouts.begin(), [](const auto& layout) { - return VideoCommon::TransformFeedbackState::Layout{ - .stream = layout.stream, - .varying_count = layout.varying_count, - .stride = layout.stride, - }; - }); - state.varyings = regs.tfb_varying_locs; + std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(), + [](const auto& layout) { + return VideoCommon::TransformFeedbackState::Layout{ + .stream = layout.stream, + .varying_count = layout.varying_count, + .stride = layout.stride, + }; + }); + state.varyings = regs.stream_out_layout; } } // Anonymous namespace ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindow& emu_window_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_, const Device& device_, - TextureCache& texture_cache_, BufferCache& buffer_cache_, - ProgramManager& program_manager_, StateTracker& state_tracker_, - VideoCore::ShaderNotify& shader_notify_) - : VideoCommon::ShaderCache{rasterizer_, gpu_memory_, maxwell3d_, kepler_compute_}, - emu_window{emu_window_}, device{device_}, texture_cache{texture_cache_}, - buffer_cache{buffer_cache_}, program_manager{program_manager_}, state_tracker{state_tracker_}, - shader_notify{shader_notify_}, use_asynchronous_shaders{device.UseAsynchronousShaders()}, + const Device& device_, TextureCache& texture_cache_, + BufferCache& buffer_cache_, ProgramManager& program_manager_, + StateTracker& state_tracker_, VideoCore::ShaderNotify& shader_notify_) + : VideoCommon::ShaderCache{rasterizer_}, emu_window{emu_window_}, device{device_}, + texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, + state_tracker{state_tracker_}, shader_notify{shader_notify_}, + use_asynchronous_shaders{device.UseAsynchronousShaders()}, + strict_context_required{device.StrictContextRequired()}, profile{ .supported_spirv = 0x00010000, @@ -190,6 +204,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo .support_int64_atomics = false, .support_derivative_control = device.HasDerivativeControl(), .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), + .support_native_ndc = true, .support_gl_nv_gpu_shader_5 = device.HasNvGpuShader5(), .support_gl_amd_gpu_shader_half_float = device.HasAmdShaderHalfFloat(), .support_gl_texture_shadow_lod = device.HasTextureShadowLod(), @@ -219,6 +234,8 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo .support_float16 = false, .support_int64 = device.HasShaderInt64(), .needs_demote_reorder = device.IsAmd(), + .support_snorm_render_buffer = false, + .support_viewport_index_layer = device.HasVertexViewportLayer(), } { if (use_asynchronous_shaders) { workers = CreateWorkers(); @@ -240,9 +257,14 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, } shader_cache_filename = base_dir / "opengl.bin"; - if (!workers) { + if (!workers && !strict_context_required) { workers = CreateWorkers(); } + std::optional strict_context; + if (strict_context_required) { + strict_context.emplace(emu_window); + } + struct { std::mutex mutex; size_t total{}; @@ -250,44 +272,49 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, bool has_loaded{}; } state; + const auto queue_work{[&](Common::UniqueFunction&& work) { + if (strict_context_required) { + work(&strict_context.value()); + } else { + workers->QueueWork(std::move(work)); + } + }}; const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { ComputePipelineKey key; file.read(reinterpret_cast(&key), sizeof(key)); - workers->QueueWork( - [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { - ctx->pools.ReleaseContents(); - auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; - std::scoped_lock lock{state.mutex}; - if (pipeline) { - compute_cache.emplace(key, std::move(pipeline)); - } - ++state.built; - if (state.has_loaded) { - callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); - } - }); + queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { + ctx->pools.ReleaseContents(); + auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; + std::scoped_lock lock{state.mutex}; + if (pipeline) { + compute_cache.emplace(key, std::move(pipeline)); + } + ++state.built; + if (state.has_loaded) { + callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); + } + }); ++state.total; }}; const auto load_graphics{[&](std::ifstream& file, std::vector envs) { GraphicsPipelineKey key; file.read(reinterpret_cast(&key), sizeof(key)); - workers->QueueWork( - [this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { - boost::container::static_vector env_ptrs; - for (auto& env : envs) { - env_ptrs.push_back(&env); - } - ctx->pools.ReleaseContents(); - auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; - std::scoped_lock lock{state.mutex}; - if (pipeline) { - graphics_cache.emplace(key, std::move(pipeline)); - } - ++state.built; - if (state.has_loaded) { - callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); - } - }); + queue_work([this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { + boost::container::static_vector env_ptrs; + for (auto& env : envs) { + env_ptrs.push_back(&env); + } + ctx->pools.ReleaseContents(); + auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; + std::scoped_lock lock{state.mutex}; + if (pipeline) { + graphics_cache.emplace(key, std::move(pipeline)); + } + ++state.built; + if (state.has_loaded) { + callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); + } + }); ++state.total; }}; LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics); @@ -299,6 +326,9 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, state.has_loaded = true; lock.unlock(); + if (strict_context_required) { + return; + } workers->WaitForRequests(stop_loading); if (!use_asynchronous_shaders) { workers.reset(); @@ -310,16 +340,16 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() { current_pipeline = nullptr; return nullptr; } - const auto& regs{maxwell3d.regs}; + const auto& regs{maxwell3d->regs}; graphics_key.raw = 0; - graphics_key.early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0); - graphics_key.gs_input_topology.Assign(graphics_key.unique_hashes[4] != 0 - ? regs.draw.topology.Value() - : Maxwell::PrimitiveTopology{}); - graphics_key.tessellation_primitive.Assign(regs.tess_mode.prim.Value()); - graphics_key.tessellation_spacing.Assign(regs.tess_mode.spacing.Value()); - graphics_key.tessellation_clockwise.Assign(regs.tess_mode.cw.Value()); - graphics_key.xfb_enabled.Assign(regs.tfb_enabled != 0 ? 1 : 0); + graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0); + graphics_key.gs_input_topology.Assign(maxwell3d->draw_manager->GetDrawState().topology); + graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value()); + graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value()); + graphics_key.tessellation_clockwise.Assign( + regs.tessellation.params.output_primitives.Value() == + Maxwell::Tessellation::OutputPrimitives::Triangles_CW); + graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0); if (graphics_key.xfb_enabled) { SetXfbState(graphics_key.xfb_state, regs); } @@ -351,13 +381,14 @@ GraphicsPipeline* ShaderCache::BuiltPipeline(GraphicsPipeline* pipeline) const n } // If something is using depth, we can assume that games are not rendering anything which // will be used one time. - if (maxwell3d.regs.zeta_enable) { + if (maxwell3d->regs.zeta_enable) { return nullptr; } // If games are using a small index count, we can assume these are full screen quads. // Usually these shaders are only used once for building textures so we can assume they // can't be built async - if (maxwell3d.regs.index_array.count <= 6 || maxwell3d.regs.vertex_buffer.count <= 6) { + const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + if (draw_state.index_buffer.count <= 6 || draw_state.vertex_buffer.count <= 6) { return pipeline; } return nullptr; @@ -368,7 +399,7 @@ ComputePipeline* ShaderCache::CurrentComputePipeline() { if (!shader) { return nullptr; } - const auto& qmd{kepler_compute.launch_description}; + const auto& qmd{kepler_compute->launch_description}; const ComputePipelineKey key{ .unique_hash = shader->unique_hash, .shared_memory_size = qmd.shared_alloc, @@ -412,7 +443,19 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( std::array programs; const bool uses_vertex_a{key.unique_hashes[0] != 0}; const bool uses_vertex_b{key.unique_hashes[1] != 0}; + + // Layer passthrough generation for devices without GL_ARB_shader_viewport_layer_array + Shader::IR::Program* layer_source_program{}; + for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && is_emulated_stage) { + auto topology = MaxwellToOutputTopology(key.gs_input_topology); + programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology); + continue; + } if (key.unique_hashes[index] == 0) { continue; } @@ -440,6 +483,10 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } + + if (programs[index].info.requires_layer_emulation) { + layer_source_program = &programs[index]; + } } const u32 glasm_storage_buffer_limit{device.GetMaxGLASMStorageBufferBlocks()}; const bool glasm_use_storage_buffers{total_storage_buffers <= glasm_storage_buffer_limit}; @@ -453,7 +500,9 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( const bool use_glasm{device.UseAssemblyShaders()}; const size_t first_index = uses_vertex_a && uses_vertex_b ? 1 : 0; for (size_t index = first_index; index < Maxwell::MaxShaderProgram; ++index) { - if (key.unique_hashes[index] == 0) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && !is_emulated_stage) { continue; } UNIMPLEMENTED_IF(index == 0); @@ -480,9 +529,9 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( previous_program = &program; } auto* const thread_worker{build_in_parallel ? workers.get() : nullptr}; - return std::make_unique( - device, texture_cache, buffer_cache, gpu_memory, maxwell3d, program_manager, state_tracker, - thread_worker, &shader_notify, sources, sources_spirv, infos, key); + return std::make_unique(device, texture_cache, buffer_cache, program_manager, + state_tracker, thread_worker, &shader_notify, sources, + sources_spirv, infos, key); } catch (Shader::Exception& exception) { LOG_ERROR(Render_OpenGL, "{}", exception.what()); @@ -491,9 +540,9 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( std::unique_ptr ShaderCache::CreateComputePipeline( const ComputePipelineKey& key, const VideoCommon::ShaderInfo* shader) { - const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()}; - const auto& qmd{kepler_compute.launch_description}; - ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start}; + const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()}; + const auto& qmd{kepler_compute->launch_description}; + ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start}; env.SetCachedSize(shader->size_bytes); main_pools.ReleaseContents(); @@ -536,9 +585,8 @@ std::unique_ptr ShaderCache::CreateComputePipeline( break; } - return std::make_unique(device, texture_cache, buffer_cache, gpu_memory, - kepler_compute, program_manager, program.info, code, - code_spirv); + return std::make_unique(device, texture_cache, buffer_cache, program_manager, + program.info, code, code_spirv); } catch (Shader::Exception& exception) { LOG_ERROR(Render_OpenGL, "{}", exception.what()); return nullptr; @@ -546,7 +594,7 @@ std::unique_ptr ShaderCache::CreateComputePipeline( std::unique_ptr ShaderCache::CreateWorkers() const { return std::make_unique(std::max(std::thread::hardware_concurrency(), 2U) - 1, - "yuzu:ShaderBuilder", + "GlShaderBuilder", [this] { return Context{emu_window}; }); } diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index a14269d..f824205 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -4,7 +4,6 @@ #pragma once #include -#include #include #include "common/common_types.h" @@ -30,12 +29,9 @@ using ShaderWorker = Common::StatefulThreadWorker; class ShaderCache : public VideoCommon::ShaderCache { public: explicit ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindow& emu_window_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_, const Device& device_, - TextureCache& texture_cache_, BufferCache& buffer_cache_, - ProgramManager& program_manager_, StateTracker& state_tracker_, - VideoCore::ShaderNotify& shader_notify_); + const Device& device_, TextureCache& texture_cache_, + BufferCache& buffer_cache_, ProgramManager& program_manager_, + StateTracker& state_tracker_, VideoCore::ShaderNotify& shader_notify_); ~ShaderCache(); void LoadDiskResources(u64 title_id, std::stop_token stop_loading, @@ -73,6 +69,7 @@ private: StateTracker& state_tracker; VideoCore::ShaderNotify& shader_notify; const bool use_asynchronous_shaders; + const bool strict_context_required; GraphicsPipelineKey graphics_key{}; GraphicsPipeline* current_pipeline{}; diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp index 912725e..d53b422 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.cpp +++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp @@ -7,8 +7,8 @@ #include "common/common_types.h" #include "core/core.h" +#include "video_core/control/channel_state.h" #include "video_core/engines/maxwell_3d.h" -#include "video_core/gpu.h" #include "video_core/renderer_opengl/gl_state_tracker.h" #define OFF(field_name) MAXWELL3D_REG_INDEX(field_name) @@ -38,12 +38,12 @@ void SetupDirtyColorMasks(Tables& tables) { void SetupDirtyVertexInstances(Tables& tables) { static constexpr std::size_t instance_base_offset = 3; for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) { - const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]); + const std::size_t array_offset = OFF(vertex_streams) + i * NUM(vertex_streams[0]); const std::size_t instance_array_offset = array_offset + instance_base_offset; tables[0][instance_array_offset] = static_cast(VertexInstance0 + i); tables[1][instance_array_offset] = VertexInstances; - const std::size_t instance_offset = OFF(instanced_arrays) + i; + const std::size_t instance_offset = OFF(vertex_stream_instances) + i; tables[0][instance_offset] = static_cast(VertexInstance0 + i); tables[1][instance_offset] = VertexInstances; } @@ -70,8 +70,8 @@ void SetupDirtyViewports(Tables& tables) { FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports); FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports); - tables[0][OFF(viewport_transform_enabled)] = ViewportTransform; - tables[1][OFF(viewport_transform_enabled)] = Viewports; + tables[0][OFF(viewport_scale_offset_enabled)] = ViewportTransform; + tables[1][OFF(viewport_scale_offset_enabled)] = Viewports; } void SetupDirtyScissors(Tables& tables) { @@ -88,7 +88,7 @@ void SetupDirtyPolygonModes(Tables& tables) { tables[1][OFF(polygon_mode_front)] = PolygonModes; tables[1][OFF(polygon_mode_back)] = PolygonModes; - tables[0][OFF(fill_rectangle)] = PolygonModes; + tables[0][OFF(fill_via_triangle_mode)] = PolygonModes; } void SetupDirtyDepthTest(Tables& tables) { @@ -100,11 +100,11 @@ void SetupDirtyDepthTest(Tables& tables) { void SetupDirtyStencilTest(Tables& tables) { static constexpr std::array offsets = { - OFF(stencil_enable), OFF(stencil_front_func_func), OFF(stencil_front_func_ref), - OFF(stencil_front_func_mask), OFF(stencil_front_op_fail), OFF(stencil_front_op_zfail), - OFF(stencil_front_op_zpass), OFF(stencil_front_mask), OFF(stencil_two_side_enable), - OFF(stencil_back_func_func), OFF(stencil_back_func_ref), OFF(stencil_back_func_mask), - OFF(stencil_back_op_fail), OFF(stencil_back_op_zfail), OFF(stencil_back_op_zpass), + OFF(stencil_enable), OFF(stencil_front_op.func), OFF(stencil_front_ref), + OFF(stencil_front_func_mask), OFF(stencil_front_op.fail), OFF(stencil_front_op.zfail), + OFF(stencil_front_op.zpass), OFF(stencil_front_mask), OFF(stencil_two_side_enable), + OFF(stencil_back_op.func), OFF(stencil_back_ref), OFF(stencil_back_func_mask), + OFF(stencil_back_op.fail), OFF(stencil_back_op.zfail), OFF(stencil_back_op.zpass), OFF(stencil_back_mask)}; for (const auto offset : offsets) { tables[0][offset] = StencilTest; @@ -121,15 +121,15 @@ void SetupDirtyAlphaTest(Tables& tables) { void SetupDirtyBlend(Tables& tables) { FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendColor); - tables[0][OFF(independent_blend_enable)] = BlendIndependentEnabled; + tables[0][OFF(blend_per_target_enabled)] = BlendIndependentEnabled; for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) { - const std::size_t offset = OFF(independent_blend) + i * NUM(independent_blend[0]); - FillBlock(tables[0], offset, NUM(independent_blend[0]), BlendState0 + i); + const std::size_t offset = OFF(blend_per_target) + i * NUM(blend_per_target[0]); + FillBlock(tables[0], offset, NUM(blend_per_target[0]), BlendState0 + i); tables[0][OFF(blend.enable) + i] = static_cast(BlendState0 + i); } - FillBlock(tables[1], OFF(independent_blend), NUM(independent_blend), BlendStates); + FillBlock(tables[1], OFF(blend_per_target), NUM(blend_per_target), BlendStates); FillBlock(tables[1], OFF(blend), NUM(blend), BlendStates); } @@ -142,13 +142,14 @@ void SetupDirtyPolygonOffset(Tables& tables) { table[OFF(polygon_offset_fill_enable)] = PolygonOffset; table[OFF(polygon_offset_line_enable)] = PolygonOffset; table[OFF(polygon_offset_point_enable)] = PolygonOffset; - table[OFF(polygon_offset_factor)] = PolygonOffset; - table[OFF(polygon_offset_units)] = PolygonOffset; - table[OFF(polygon_offset_clamp)] = PolygonOffset; + table[OFF(slope_scale_depth_bias)] = PolygonOffset; + table[OFF(depth_bias)] = PolygonOffset; + table[OFF(depth_bias_clamp)] = PolygonOffset; } void SetupDirtyMultisampleControl(Tables& tables) { - FillBlock(tables[0], OFF(multisample_control), NUM(multisample_control), MultisampleControl); + FillBlock(tables[0], OFF(anti_alias_alpha_control), NUM(anti_alias_alpha_control), + MultisampleControl); } void SetupDirtyRasterizeEnable(Tables& tables) { @@ -168,7 +169,7 @@ void SetupDirtyFragmentClampColor(Tables& tables) { } void SetupDirtyPointSize(Tables& tables) { - tables[0][OFF(vp_point_size)] = PointSize; + tables[0][OFF(point_size_attribute)] = PointSize; tables[0][OFF(point_size)] = PointSize; tables[0][OFF(point_sprite_enable)] = PointSize; } @@ -176,35 +177,34 @@ void SetupDirtyPointSize(Tables& tables) { void SetupDirtyLineWidth(Tables& tables) { tables[0][OFF(line_width_smooth)] = LineWidth; tables[0][OFF(line_width_aliased)] = LineWidth; - tables[0][OFF(line_smooth_enable)] = LineWidth; + tables[0][OFF(line_anti_alias_enable)] = LineWidth; } void SetupDirtyClipControl(Tables& tables) { auto& table = tables[0]; - table[OFF(screen_y_control)] = ClipControl; + table[OFF(window_origin)] = ClipControl; table[OFF(depth_mode)] = ClipControl; } void SetupDirtyDepthClampEnabled(Tables& tables) { - tables[0][OFF(view_volume_clip_control)] = DepthClampEnabled; + tables[0][OFF(viewport_clip_control)] = DepthClampEnabled; } void SetupDirtyMisc(Tables& tables) { auto& table = tables[0]; - table[OFF(clip_distance_enabled)] = ClipDistances; + table[OFF(user_clip_enable)] = ClipDistances; - table[OFF(front_face)] = FrontFace; + table[OFF(gl_front_face)] = FrontFace; - table[OFF(cull_test_enabled)] = CullTest; - table[OFF(cull_face)] = CullTest; + table[OFF(gl_cull_test_enabled)] = CullTest; + table[OFF(gl_cull_face)] = CullTest; } } // Anonymous namespace -StateTracker::StateTracker(Tegra::GPU& gpu) : flags{gpu.Maxwell3D().dirty.flags} { - auto& dirty = gpu.Maxwell3D().dirty; - auto& tables = dirty.tables; +void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { + auto& tables{channel_state.maxwell_3d->dirty.tables}; SetupDirtyFlags(tables); SetupDirtyColorMasks(tables); SetupDirtyViewports(tables); @@ -230,4 +230,14 @@ StateTracker::StateTracker(Tegra::GPU& gpu) : flags{gpu.Maxwell3D().dirty.flags} SetupDirtyMisc(tables); } +void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { + flags = &channel_state.maxwell_3d->dirty.flags; +} + +void StateTracker::InvalidateState() { + flags->set(); +} + +StateTracker::StateTracker() : flags{&default_flags} {} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h index 04e024f..19bcf3f 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.h +++ b/src/video_core/renderer_opengl/gl_state_tracker.h @@ -12,8 +12,10 @@ #include "video_core/engines/maxwell_3d.h" namespace Tegra { -class GPU; +namespace Control { +struct ChannelState; } +} // namespace Tegra namespace OpenGL { @@ -83,7 +85,7 @@ static_assert(Last <= std::numeric_limits::max()); class StateTracker { public: - explicit StateTracker(Tegra::GPU& gpu); + explicit StateTracker(); void BindIndexBuffer(GLuint new_index_buffer) { if (index_buffer == new_index_buffer) { @@ -121,94 +123,107 @@ public: } void NotifyScreenDrawVertexArray() { - flags[OpenGL::Dirty::VertexFormats] = true; - flags[OpenGL::Dirty::VertexFormat0 + 0] = true; - flags[OpenGL::Dirty::VertexFormat0 + 1] = true; + (*flags)[OpenGL::Dirty::VertexFormats] = true; + (*flags)[OpenGL::Dirty::VertexFormat0 + 0] = true; + (*flags)[OpenGL::Dirty::VertexFormat0 + 1] = true; - flags[VideoCommon::Dirty::VertexBuffers] = true; - flags[VideoCommon::Dirty::VertexBuffer0] = true; + (*flags)[VideoCommon::Dirty::VertexBuffers] = true; + (*flags)[VideoCommon::Dirty::VertexBuffer0] = true; - flags[OpenGL::Dirty::VertexInstances] = true; - flags[OpenGL::Dirty::VertexInstance0 + 0] = true; - flags[OpenGL::Dirty::VertexInstance0 + 1] = true; + (*flags)[OpenGL::Dirty::VertexInstances] = true; + (*flags)[OpenGL::Dirty::VertexInstance0 + 0] = true; + (*flags)[OpenGL::Dirty::VertexInstance0 + 1] = true; } void NotifyPolygonModes() { - flags[OpenGL::Dirty::PolygonModes] = true; - flags[OpenGL::Dirty::PolygonModeFront] = true; - flags[OpenGL::Dirty::PolygonModeBack] = true; + (*flags)[OpenGL::Dirty::PolygonModes] = true; + (*flags)[OpenGL::Dirty::PolygonModeFront] = true; + (*flags)[OpenGL::Dirty::PolygonModeBack] = true; } void NotifyViewport0() { - flags[OpenGL::Dirty::Viewports] = true; - flags[OpenGL::Dirty::Viewport0] = true; + (*flags)[OpenGL::Dirty::Viewports] = true; + (*flags)[OpenGL::Dirty::Viewport0] = true; } void NotifyScissor0() { - flags[OpenGL::Dirty::Scissors] = true; - flags[OpenGL::Dirty::Scissor0] = true; + (*flags)[OpenGL::Dirty::Scissors] = true; + (*flags)[OpenGL::Dirty::Scissor0] = true; } void NotifyColorMask(size_t index) { - flags[OpenGL::Dirty::ColorMasks] = true; - flags[OpenGL::Dirty::ColorMask0 + index] = true; + (*flags)[OpenGL::Dirty::ColorMasks] = true; + (*flags)[OpenGL::Dirty::ColorMask0 + index] = true; } void NotifyBlend0() { - flags[OpenGL::Dirty::BlendStates] = true; - flags[OpenGL::Dirty::BlendState0] = true; + (*flags)[OpenGL::Dirty::BlendStates] = true; + (*flags)[OpenGL::Dirty::BlendState0] = true; } void NotifyFramebuffer() { - flags[VideoCommon::Dirty::RenderTargets] = true; + (*flags)[VideoCommon::Dirty::RenderTargets] = true; } void NotifyFrontFace() { - flags[OpenGL::Dirty::FrontFace] = true; + (*flags)[OpenGL::Dirty::FrontFace] = true; } void NotifyCullTest() { - flags[OpenGL::Dirty::CullTest] = true; + (*flags)[OpenGL::Dirty::CullTest] = true; } void NotifyDepthMask() { - flags[OpenGL::Dirty::DepthMask] = true; + (*flags)[OpenGL::Dirty::DepthMask] = true; } void NotifyDepthTest() { - flags[OpenGL::Dirty::DepthTest] = true; + (*flags)[OpenGL::Dirty::DepthTest] = true; } void NotifyStencilTest() { - flags[OpenGL::Dirty::StencilTest] = true; + (*flags)[OpenGL::Dirty::StencilTest] = true; } void NotifyPolygonOffset() { - flags[OpenGL::Dirty::PolygonOffset] = true; + (*flags)[OpenGL::Dirty::PolygonOffset] = true; } void NotifyRasterizeEnable() { - flags[OpenGL::Dirty::RasterizeEnable] = true; + (*flags)[OpenGL::Dirty::RasterizeEnable] = true; } void NotifyFramebufferSRGB() { - flags[OpenGL::Dirty::FramebufferSRGB] = true; + (*flags)[OpenGL::Dirty::FramebufferSRGB] = true; } void NotifyLogicOp() { - flags[OpenGL::Dirty::LogicOp] = true; + (*flags)[OpenGL::Dirty::LogicOp] = true; } void NotifyClipControl() { - flags[OpenGL::Dirty::ClipControl] = true; + (*flags)[OpenGL::Dirty::ClipControl] = true; } void NotifyAlphaTest() { - flags[OpenGL::Dirty::AlphaTest] = true; + (*flags)[OpenGL::Dirty::AlphaTest] = true; } + void NotifyRange(u8 start, u8 end) { + for (auto flag = start; flag <= end; flag++) { + (*flags)[flag] = true; + } + } + + void SetupTables(Tegra::Control::ChannelState& channel_state); + + void ChangeChannel(Tegra::Control::ChannelState& channel_state); + + void InvalidateState(); + private: - Tegra::Engines::Maxwell3D::DirtyState::Flags& flags; + Tegra::Engines::Maxwell3D::DirtyState::Flags* flags; + Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags{}; GLuint framebuffer = 0; GLuint index_buffer = 0; diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 99cd11d..9f7ce74 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -891,6 +891,7 @@ void Image::CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t b break; default: ASSERT(false); + break; } } @@ -927,6 +928,7 @@ void Image::CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t b break; default: ASSERT(false); + break; } // Compressed formats don't have a pixel format or type const bool is_compressed = gl_format == GL_NONE; diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 9a72d0d..ef1190e 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -28,6 +28,7 @@ constexpr std::array FORMAT_TAB {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1R5G5B5_UNORM {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT + {GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2R10G10B10_UNORM {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // A5B5G5R1_UNORM {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM @@ -87,7 +88,7 @@ constexpr std::array FORMAT_TAB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM - {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R4G4_UNORM + {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // G4R4_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB @@ -99,6 +100,8 @@ constexpr std::array FORMAT_TAB {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM + {GL_COMPRESSED_RGBA_ASTC_10x5_KHR}, // ASTC_2D_10X5_UNORM + {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR}, // ASTC_2D_10X5_SRGB {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM @@ -124,51 +127,60 @@ inline const FormatTuple& GetFormatTuple(VideoCore::Surface::PixelFormat pixel_f inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) { switch (attrib.type) { - case Maxwell::VertexAttribute::Type::UnsignedNorm: - case Maxwell::VertexAttribute::Type::UnsignedScaled: - case Maxwell::VertexAttribute::Type::UnsignedInt: + case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway: + ASSERT_MSG(false, "Invalid vertex attribute type!"); + break; + case Maxwell::VertexAttribute::Type::UNorm: + case Maxwell::VertexAttribute::Type::UScaled: + case Maxwell::VertexAttribute::Type::UInt: switch (attrib.size) { - case Maxwell::VertexAttribute::Size::Size_8: - case Maxwell::VertexAttribute::Size::Size_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return GL_UNSIGNED_BYTE; - case Maxwell::VertexAttribute::Size::Size_16: - case Maxwell::VertexAttribute::Size::Size_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return GL_UNSIGNED_SHORT; - case Maxwell::VertexAttribute::Size::Size_32: - case Maxwell::VertexAttribute::Size::Size_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return GL_UNSIGNED_INT; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return GL_UNSIGNED_INT_2_10_10_10_REV; default: break; } break; - case Maxwell::VertexAttribute::Type::SignedNorm: - case Maxwell::VertexAttribute::Type::SignedScaled: - case Maxwell::VertexAttribute::Type::SignedInt: + case Maxwell::VertexAttribute::Type::SNorm: + case Maxwell::VertexAttribute::Type::SScaled: + case Maxwell::VertexAttribute::Type::SInt: switch (attrib.size) { - case Maxwell::VertexAttribute::Size::Size_8: - case Maxwell::VertexAttribute::Size::Size_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return GL_BYTE; - case Maxwell::VertexAttribute::Size::Size_16: - case Maxwell::VertexAttribute::Size::Size_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return GL_SHORT; - case Maxwell::VertexAttribute::Size::Size_32: - case Maxwell::VertexAttribute::Size::Size_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return GL_INT; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return GL_INT_2_10_10_10_REV; default: break; @@ -176,17 +188,17 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) { break; case Maxwell::VertexAttribute::Type::Float: switch (attrib.size) { - case Maxwell::VertexAttribute::Size::Size_16: - case Maxwell::VertexAttribute::Size::Size_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return GL_HALF_FLOAT; - case Maxwell::VertexAttribute::Size::Size_32: - case Maxwell::VertexAttribute::Size::Size_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32: - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return GL_FLOAT; - case Maxwell::VertexAttribute::Size::Size_11_11_10: + case Maxwell::VertexAttribute::Size::Size_B10_G11_R11: return GL_UNSIGNED_INT_10F_11F_11F_REV; default: break; @@ -333,20 +345,20 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { switch (equation) { - case Maxwell::Blend::Equation::Add: - case Maxwell::Blend::Equation::AddGL: + case Maxwell::Blend::Equation::Add_D3D: + case Maxwell::Blend::Equation::Add_GL: return GL_FUNC_ADD; - case Maxwell::Blend::Equation::Subtract: - case Maxwell::Blend::Equation::SubtractGL: + case Maxwell::Blend::Equation::Subtract_D3D: + case Maxwell::Blend::Equation::Subtract_GL: return GL_FUNC_SUBTRACT; - case Maxwell::Blend::Equation::ReverseSubtract: - case Maxwell::Blend::Equation::ReverseSubtractGL: + case Maxwell::Blend::Equation::ReverseSubtract_D3D: + case Maxwell::Blend::Equation::ReverseSubtract_GL: return GL_FUNC_REVERSE_SUBTRACT; - case Maxwell::Blend::Equation::Min: - case Maxwell::Blend::Equation::MinGL: + case Maxwell::Blend::Equation::Min_D3D: + case Maxwell::Blend::Equation::Min_GL: return GL_MIN; - case Maxwell::Blend::Equation::Max: - case Maxwell::Blend::Equation::MaxGL: + case Maxwell::Blend::Equation::Max_D3D: + case Maxwell::Blend::Equation::Max_GL: return GL_MAX; } UNIMPLEMENTED_MSG("Unimplemented blend equation={}", equation); @@ -355,62 +367,62 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { switch (factor) { - case Maxwell::Blend::Factor::Zero: - case Maxwell::Blend::Factor::ZeroGL: + case Maxwell::Blend::Factor::Zero_D3D: + case Maxwell::Blend::Factor::Zero_GL: return GL_ZERO; - case Maxwell::Blend::Factor::One: - case Maxwell::Blend::Factor::OneGL: + case Maxwell::Blend::Factor::One_D3D: + case Maxwell::Blend::Factor::One_GL: return GL_ONE; - case Maxwell::Blend::Factor::SourceColor: - case Maxwell::Blend::Factor::SourceColorGL: + case Maxwell::Blend::Factor::SourceColor_D3D: + case Maxwell::Blend::Factor::SourceColor_GL: return GL_SRC_COLOR; - case Maxwell::Blend::Factor::OneMinusSourceColor: - case Maxwell::Blend::Factor::OneMinusSourceColorGL: + case Maxwell::Blend::Factor::OneMinusSourceColor_D3D: + case Maxwell::Blend::Factor::OneMinusSourceColor_GL: return GL_ONE_MINUS_SRC_COLOR; - case Maxwell::Blend::Factor::SourceAlpha: - case Maxwell::Blend::Factor::SourceAlphaGL: + case Maxwell::Blend::Factor::SourceAlpha_D3D: + case Maxwell::Blend::Factor::SourceAlpha_GL: return GL_SRC_ALPHA; - case Maxwell::Blend::Factor::OneMinusSourceAlpha: - case Maxwell::Blend::Factor::OneMinusSourceAlphaGL: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL: return GL_ONE_MINUS_SRC_ALPHA; - case Maxwell::Blend::Factor::DestAlpha: - case Maxwell::Blend::Factor::DestAlphaGL: + case Maxwell::Blend::Factor::DestAlpha_D3D: + case Maxwell::Blend::Factor::DestAlpha_GL: return GL_DST_ALPHA; - case Maxwell::Blend::Factor::OneMinusDestAlpha: - case Maxwell::Blend::Factor::OneMinusDestAlphaGL: + case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusDestAlpha_GL: return GL_ONE_MINUS_DST_ALPHA; - case Maxwell::Blend::Factor::DestColor: - case Maxwell::Blend::Factor::DestColorGL: + case Maxwell::Blend::Factor::DestColor_D3D: + case Maxwell::Blend::Factor::DestColor_GL: return GL_DST_COLOR; - case Maxwell::Blend::Factor::OneMinusDestColor: - case Maxwell::Blend::Factor::OneMinusDestColorGL: + case Maxwell::Blend::Factor::OneMinusDestColor_D3D: + case Maxwell::Blend::Factor::OneMinusDestColor_GL: return GL_ONE_MINUS_DST_COLOR; - case Maxwell::Blend::Factor::SourceAlphaSaturate: - case Maxwell::Blend::Factor::SourceAlphaSaturateGL: + case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D: + case Maxwell::Blend::Factor::SourceAlphaSaturate_GL: return GL_SRC_ALPHA_SATURATE; - case Maxwell::Blend::Factor::Source1Color: - case Maxwell::Blend::Factor::Source1ColorGL: + case Maxwell::Blend::Factor::Source1Color_D3D: + case Maxwell::Blend::Factor::Source1Color_GL: return GL_SRC1_COLOR; - case Maxwell::Blend::Factor::OneMinusSource1Color: - case Maxwell::Blend::Factor::OneMinusSource1ColorGL: + case Maxwell::Blend::Factor::OneMinusSource1Color_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Color_GL: return GL_ONE_MINUS_SRC1_COLOR; - case Maxwell::Blend::Factor::Source1Alpha: - case Maxwell::Blend::Factor::Source1AlphaGL: + case Maxwell::Blend::Factor::Source1Alpha_D3D: + case Maxwell::Blend::Factor::Source1Alpha_GL: return GL_SRC1_ALPHA; - case Maxwell::Blend::Factor::OneMinusSource1Alpha: - case Maxwell::Blend::Factor::OneMinusSource1AlphaGL: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL: return GL_ONE_MINUS_SRC1_ALPHA; - case Maxwell::Blend::Factor::ConstantColor: - case Maxwell::Blend::Factor::ConstantColorGL: + case Maxwell::Blend::Factor::BlendFactor_D3D: + case Maxwell::Blend::Factor::ConstantColor_GL: return GL_CONSTANT_COLOR; - case Maxwell::Blend::Factor::OneMinusConstantColor: - case Maxwell::Blend::Factor::OneMinusConstantColorGL: + case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D: + case Maxwell::Blend::Factor::OneMinusConstantColor_GL: return GL_ONE_MINUS_CONSTANT_COLOR; - case Maxwell::Blend::Factor::ConstantAlpha: - case Maxwell::Blend::Factor::ConstantAlphaGL: + case Maxwell::Blend::Factor::BothSourceAlpha_D3D: + case Maxwell::Blend::Factor::ConstantAlpha_GL: return GL_CONSTANT_ALPHA; - case Maxwell::Blend::Factor::OneMinusConstantAlpha: - case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: + case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL: return GL_ONE_MINUS_CONSTANT_ALPHA; } UNIMPLEMENTED_MSG("Unimplemented blend factor={}", factor); @@ -419,60 +431,60 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { switch (comparison) { - case Maxwell::ComparisonOp::Never: - case Maxwell::ComparisonOp::NeverOld: + case Maxwell::ComparisonOp::Never_D3D: + case Maxwell::ComparisonOp::Never_GL: return GL_NEVER; - case Maxwell::ComparisonOp::Less: - case Maxwell::ComparisonOp::LessOld: + case Maxwell::ComparisonOp::Less_D3D: + case Maxwell::ComparisonOp::Less_GL: return GL_LESS; - case Maxwell::ComparisonOp::Equal: - case Maxwell::ComparisonOp::EqualOld: + case Maxwell::ComparisonOp::Equal_D3D: + case Maxwell::ComparisonOp::Equal_GL: return GL_EQUAL; - case Maxwell::ComparisonOp::LessEqual: - case Maxwell::ComparisonOp::LessEqualOld: + case Maxwell::ComparisonOp::LessEqual_D3D: + case Maxwell::ComparisonOp::LessEqual_GL: return GL_LEQUAL; - case Maxwell::ComparisonOp::Greater: - case Maxwell::ComparisonOp::GreaterOld: + case Maxwell::ComparisonOp::Greater_D3D: + case Maxwell::ComparisonOp::Greater_GL: return GL_GREATER; - case Maxwell::ComparisonOp::NotEqual: - case Maxwell::ComparisonOp::NotEqualOld: + case Maxwell::ComparisonOp::NotEqual_D3D: + case Maxwell::ComparisonOp::NotEqual_GL: return GL_NOTEQUAL; - case Maxwell::ComparisonOp::GreaterEqual: - case Maxwell::ComparisonOp::GreaterEqualOld: + case Maxwell::ComparisonOp::GreaterEqual_D3D: + case Maxwell::ComparisonOp::GreaterEqual_GL: return GL_GEQUAL; - case Maxwell::ComparisonOp::Always: - case Maxwell::ComparisonOp::AlwaysOld: + case Maxwell::ComparisonOp::Always_D3D: + case Maxwell::ComparisonOp::Always_GL: return GL_ALWAYS; } UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); return GL_ALWAYS; } -inline GLenum StencilOp(Maxwell::StencilOp stencil) { +inline GLenum StencilOp(Maxwell::StencilOp::Op stencil) { switch (stencil) { - case Maxwell::StencilOp::Keep: - case Maxwell::StencilOp::KeepOGL: + case Maxwell::StencilOp::Op::Keep_D3D: + case Maxwell::StencilOp::Op::Keep_GL: return GL_KEEP; - case Maxwell::StencilOp::Zero: - case Maxwell::StencilOp::ZeroOGL: + case Maxwell::StencilOp::Op::Zero_D3D: + case Maxwell::StencilOp::Op::Zero_GL: return GL_ZERO; - case Maxwell::StencilOp::Replace: - case Maxwell::StencilOp::ReplaceOGL: + case Maxwell::StencilOp::Op::Replace_D3D: + case Maxwell::StencilOp::Op::Replace_GL: return GL_REPLACE; - case Maxwell::StencilOp::Incr: - case Maxwell::StencilOp::IncrOGL: + case Maxwell::StencilOp::Op::IncrSaturate_D3D: + case Maxwell::StencilOp::Op::IncrSaturate_GL: return GL_INCR; - case Maxwell::StencilOp::Decr: - case Maxwell::StencilOp::DecrOGL: + case Maxwell::StencilOp::Op::DecrSaturate_D3D: + case Maxwell::StencilOp::Op::DecrSaturate_GL: return GL_DECR; - case Maxwell::StencilOp::Invert: - case Maxwell::StencilOp::InvertOGL: + case Maxwell::StencilOp::Op::Invert_D3D: + case Maxwell::StencilOp::Op::Invert_GL: return GL_INVERT; - case Maxwell::StencilOp::IncrWrap: - case Maxwell::StencilOp::IncrWrapOGL: + case Maxwell::StencilOp::Op::Incr_D3D: + case Maxwell::StencilOp::Op::Incr_GL: return GL_INCR_WRAP; - case Maxwell::StencilOp::DecrWrap: - case Maxwell::StencilOp::DecrWrapOGL: + case Maxwell::StencilOp::Op::Decr_D3D: + case Maxwell::StencilOp::Op::Decr_GL: return GL_DECR_WRAP; } UNIMPLEMENTED_MSG("Unimplemented stencil op={}", stencil); @@ -503,39 +515,39 @@ inline GLenum CullFace(Maxwell::CullFace cull_face) { return GL_BACK; } -inline GLenum LogicOp(Maxwell::LogicOperation operation) { +inline GLenum LogicOp(Maxwell::LogicOp::Op operation) { switch (operation) { - case Maxwell::LogicOperation::Clear: + case Maxwell::LogicOp::Op::Clear: return GL_CLEAR; - case Maxwell::LogicOperation::And: + case Maxwell::LogicOp::Op::And: return GL_AND; - case Maxwell::LogicOperation::AndReverse: + case Maxwell::LogicOp::Op::AndReverse: return GL_AND_REVERSE; - case Maxwell::LogicOperation::Copy: + case Maxwell::LogicOp::Op::Copy: return GL_COPY; - case Maxwell::LogicOperation::AndInverted: + case Maxwell::LogicOp::Op::AndInverted: return GL_AND_INVERTED; - case Maxwell::LogicOperation::NoOp: + case Maxwell::LogicOp::Op::NoOp: return GL_NOOP; - case Maxwell::LogicOperation::Xor: + case Maxwell::LogicOp::Op::Xor: return GL_XOR; - case Maxwell::LogicOperation::Or: + case Maxwell::LogicOp::Op::Or: return GL_OR; - case Maxwell::LogicOperation::Nor: + case Maxwell::LogicOp::Op::Nor: return GL_NOR; - case Maxwell::LogicOperation::Equiv: + case Maxwell::LogicOp::Op::Equiv: return GL_EQUIV; - case Maxwell::LogicOperation::Invert: + case Maxwell::LogicOp::Op::Invert: return GL_INVERT; - case Maxwell::LogicOperation::OrReverse: + case Maxwell::LogicOp::Op::OrReverse: return GL_OR_REVERSE; - case Maxwell::LogicOperation::CopyInverted: + case Maxwell::LogicOp::Op::CopyInverted: return GL_COPY_INVERTED; - case Maxwell::LogicOperation::OrInverted: + case Maxwell::LogicOp::Op::OrInverted: return GL_OR_INVERTED; - case Maxwell::LogicOperation::Nand: + case Maxwell::LogicOp::Op::Nand: return GL_NAND; - case Maxwell::LogicOperation::Set: + case Maxwell::LogicOp::Op::Set: return GL_SET; } UNIMPLEMENTED_MSG("Unimplemented logic operation={}", operation); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 34f3f7a..bc75680 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -22,12 +22,21 @@ #include "video_core/host_shaders/opengl_present_frag.h" #include "video_core/host_shaders/opengl_present_scaleforce_frag.h" #include "video_core/host_shaders/opengl_present_vert.h" +#include "video_core/host_shaders/opengl_smaa_glsl.h" #include "video_core/host_shaders/present_bicubic_frag.h" #include "video_core/host_shaders/present_gaussian_frag.h" +#include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h" +#include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h" +#include "video_core/host_shaders/smaa_edge_detection_frag.h" +#include "video_core/host_shaders/smaa_edge_detection_vert.h" +#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" +#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/renderer_opengl.h" +#include "video_core/smaa_area_tex.h" +#include "video_core/smaa_search_tex.h" #include "video_core/textures/decoders.h" namespace OpenGL { @@ -131,8 +140,8 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, std::unique_ptr context_) : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, - emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{gpu}, - program_manager{device}, + emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, device{emu_window_}, + state_tracker{}, program_manager{device}, rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) { if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { glEnable(GL_DEBUG_OUTPUT); @@ -258,6 +267,28 @@ void RendererOpenGL::InitOpenGLObjects() { // Create shader programs fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); + + const auto SmaaShader = [](std::string_view specialized_source, GLenum stage) { + std::string shader_source{specialized_source}; + constexpr std::string_view include_string = "#include \"opengl_smaa.glsl\""; + const std::size_t pos = shader_source.find(include_string); + ASSERT(pos != std::string::npos); + shader_source.replace(pos, include_string.size(), HostShaders::OPENGL_SMAA_GLSL); + return CreateProgram(shader_source, stage); + }; + + smaa_edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER); + smaa_edge_detection_frag = + SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER); + smaa_blending_weight_calculation_vert = + SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER); + smaa_blending_weight_calculation_frag = + SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER); + smaa_neighborhood_blending_vert = + SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER); + smaa_neighborhood_blending_frag = + SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER); + present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER); present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER); @@ -293,7 +324,16 @@ void RendererOpenGL::InitOpenGLObjects() { // Clear screen to black LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); - fxaa_framebuffer.Create(); + aa_framebuffer.Create(); + + smaa_area_tex.Create(GL_TEXTURE_2D); + glTextureStorage2D(smaa_area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT); + glTextureSubImage2D(smaa_area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG, + GL_UNSIGNED_BYTE, areaTexBytes); + smaa_search_tex.Create(GL_TEXTURE_2D); + glTextureStorage2D(smaa_search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT); + glTextureSubImage2D(smaa_search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED, + GL_UNSIGNED_BYTE, searchTexBytes); } void RendererOpenGL::AddTelemetryFields() { @@ -340,18 +380,28 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", // static_cast(framebuffer.pixel_format)); + break; } texture.resource.Release(); texture.resource.Create(GL_TEXTURE_2D); glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height); - fxaa_texture.Release(); - fxaa_texture.Create(GL_TEXTURE_2D); - glTextureStorage2D(fxaa_texture.handle, 1, GL_RGBA16F, + aa_texture.Release(); + aa_texture.Create(GL_TEXTURE_2D); + glTextureStorage2D(aa_texture.handle, 1, GL_RGBA16F, + Settings::values.resolution_info.ScaleUp(screen_info.texture.width), + Settings::values.resolution_info.ScaleUp(screen_info.texture.height)); + glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, aa_texture.handle, 0); + smaa_edges_tex.Release(); + smaa_edges_tex.Create(GL_TEXTURE_2D); + glTextureStorage2D(smaa_edges_tex.handle, 1, GL_RG16F, + Settings::values.resolution_info.ScaleUp(screen_info.texture.width), + Settings::values.resolution_info.ScaleUp(screen_info.texture.height)); + smaa_blend_tex.Release(); + smaa_blend_tex.Create(GL_TEXTURE_2D); + glTextureStorage2D(smaa_blend_tex.handle, 1, GL_RGBA16F, Settings::values.resolution_info.ScaleUp(screen_info.texture.width), Settings::values.resolution_info.ScaleUp(screen_info.texture.height)); - glNamedFramebufferTexture(fxaa_framebuffer.handle, GL_COLOR_ATTACHMENT0, fxaa_texture.handle, - 0); } void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { @@ -376,11 +426,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); - // Update background color before drawing - glClearColor(Settings::values.bg_red.GetValue() / 255.0f, - Settings::values.bg_green.GetValue() / 255.0f, - Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); - glEnable(GL_CULL_FACE); glDisable(GL_COLOR_LOGIC_OP); glDisable(GL_DEPTH_TEST); @@ -393,12 +438,12 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { glCullFace(GL_BACK); glFrontFace(GL_CW); glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthRangeIndexed(0, 0.0, 0.0); glBindTextureUnit(0, screen_info.display_texture); - if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa) { - program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle); - + const auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); + if (anti_aliasing != Settings::AntiAliasing::None) { glEnablei(GL_SCISSOR_TEST, 0); auto viewport_width = screen_info.texture.width; auto scissor_width = framebuffer_crop_rect.GetWidth(); @@ -419,21 +464,60 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { glScissorIndexed(0, 0, 0, scissor_width, scissor_height); glViewportIndexedf(0, 0.0f, 0.0f, static_cast(viewport_width), static_cast(viewport_height)); - glDepthRangeIndexed(0, 0.0, 0.0); glBindSampler(0, present_sampler.handle); GLint old_read_fb; GLint old_draw_fb; glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fxaa_framebuffer.handle); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + switch (anti_aliasing) { + case Settings::AntiAliasing::Fxaa: { + program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } break; + case Settings::AntiAliasing::Smaa: { + glClearColor(0, 0, 0, 0); + glFrontFace(GL_CCW); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); + glBindSampler(1, present_sampler.handle); + glBindSampler(2, present_sampler.handle); + + glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, + smaa_edges_tex.handle, 0); + glClear(GL_COLOR_BUFFER_BIT); + program_manager.BindPresentPrograms(smaa_edge_detection_vert.handle, + smaa_edge_detection_frag.handle); + glDrawArrays(GL_TRIANGLES, 0, 3); + + glBindTextureUnit(0, smaa_edges_tex.handle); + glBindTextureUnit(1, smaa_area_tex.handle); + glBindTextureUnit(2, smaa_search_tex.handle); + glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, + smaa_blend_tex.handle, 0); + glClear(GL_COLOR_BUFFER_BIT); + program_manager.BindPresentPrograms(smaa_blending_weight_calculation_vert.handle, + smaa_blending_weight_calculation_frag.handle); + glDrawArrays(GL_TRIANGLES, 0, 3); + + glBindTextureUnit(0, screen_info.display_texture); + glBindTextureUnit(1, smaa_blend_tex.handle); + glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, + aa_texture.handle, 0); + program_manager.BindPresentPrograms(smaa_neighborhood_blending_vert.handle, + smaa_neighborhood_blending_frag.handle); + glDrawArrays(GL_TRIANGLES, 0, 3); + glFrontFace(GL_CW); + } break; + default: + UNREACHABLE(); + } glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); - glBindTextureUnit(0, fxaa_texture.handle); + glBindTextureUnit(0, aa_texture.handle); } const std::array ortho_matrix = MakeOrthographicMatrix(static_cast(layout.width), static_cast(layout.height)); @@ -550,6 +634,11 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { glBindSampler(0, present_sampler_nn.handle); } + // Update background color before drawing + glClearColor(Settings::values.bg_red.GetValue() / 255.0f, + Settings::values.bg_green.GetValue() / 255.0f, + Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 1a32e73..cc97d7b 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -127,8 +127,19 @@ private: /// Display information for Switch screen ScreenInfo screen_info; - OGLTexture fxaa_texture; - OGLFramebuffer fxaa_framebuffer; + OGLTexture aa_texture; + OGLFramebuffer aa_framebuffer; + + OGLProgram smaa_edge_detection_vert; + OGLProgram smaa_blending_weight_calculation_vert; + OGLProgram smaa_neighborhood_blending_vert; + OGLProgram smaa_edge_detection_frag; + OGLProgram smaa_blending_weight_calculation_frag; + OGLProgram smaa_neighborhood_blending_frag; + OGLTexture smaa_area_tex; + OGLTexture smaa_search_tex; + OGLTexture smaa_edges_tex; + OGLTexture smaa_blend_tex; /// OpenGL framebuffer data std::vector gl_framebuffer_data; diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 733b454..e62b368 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -7,6 +7,8 @@ #include "common/bit_cast.h" #include "common/cityhash.h" #include "common/common_types.h" +#include "common/polyfill_ranges.h" +#include "video_core/engines/draw_manager.h" #include "video_core/renderer_vulkan/fixed_pipeline_state.h" #include "video_core/renderer_vulkan/vk_state_tracker.h" @@ -34,57 +36,67 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = { }; void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) { - std::ranges::transform(regs.tfb_layouts, state.layouts.begin(), [](const auto& layout) { - return VideoCommon::TransformFeedbackState::Layout{ - .stream = layout.stream, - .varying_count = layout.varying_count, - .stride = layout.stride, - }; - }); - state.varyings = regs.tfb_varying_locs; + std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(), + [](const auto& layout) { + return VideoCommon::TransformFeedbackState::Layout{ + .stream = layout.stream, + .varying_count = layout.varying_count, + .stride = layout.stride, + }; + }); + state.varyings = regs.stream_out_layout; } } // Anonymous namespace void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state, bool has_dynamic_vertex_input) { const Maxwell& regs = maxwell3d.regs; + const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology; const std::array enabled_lut{ regs.polygon_offset_point_enable, regs.polygon_offset_line_enable, regs.polygon_offset_fill_enable, }; - const u32 topology_index = static_cast(regs.draw.topology.Value()); + const u32 topology_index = static_cast(topology_); raw1 = 0; extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0); dynamic_vertex_input.Assign(has_dynamic_vertex_input ? 1 : 0); - xfb_enabled.Assign(regs.tfb_enabled != 0); + xfb_enabled.Assign(regs.transform_feedback_enabled != 0); primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); - depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); + depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::Passthrough || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumZ); ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); patch_control_points_minus_one.Assign(regs.patch_vertices - 1); - tessellation_primitive.Assign(static_cast(regs.tess_mode.prim.Value())); - tessellation_spacing.Assign(static_cast(regs.tess_mode.spacing.Value())); - tessellation_clockwise.Assign(regs.tess_mode.cw.Value()); + tessellation_primitive.Assign(static_cast(regs.tessellation.params.domain_type.Value())); + tessellation_spacing.Assign(static_cast(regs.tessellation.params.spacing.Value())); + tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == + Maxwell::Tessellation::OutputPrimitives::Triangles_CW); logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); - logic_op.Assign(PackLogicOp(regs.logic_op.operation)); - topology.Assign(regs.draw.topology); - msaa_mode.Assign(regs.multisample_mode); + logic_op.Assign(PackLogicOp(regs.logic_op.op)); + topology.Assign(topology_); + msaa_mode.Assign(regs.anti_alias_samples_mode); raw2 = 0; rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); const auto test_func = - regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always; + regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL; alpha_test_func.Assign(PackComparisonOp(test_func)); - early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0); + early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0); depth_enabled.Assign(regs.zeta_enable != 0 ? 1 : 0); depth_format.Assign(static_cast(regs.zeta.format)); - y_negate.Assign(regs.screen_y_control.y_negate != 0 ? 1 : 0); - provoking_vertex_last.Assign(regs.provoking_vertex_last != 0 ? 1 : 0); + y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0); + provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0); conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); - smooth_lines.Assign(regs.line_smooth_enable != 0 ? 1 : 0); + smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); + alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); + alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); for (size_t i = 0; i < regs.rt.size(); ++i) { color_formats[i] = static_cast(regs.rt[i].format); @@ -116,8 +128,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, maxwell3d.dirty.flags[Dirty::VertexInput] = false; enabled_divisors = 0; for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { - const bool is_enabled = regs.instanced_arrays.IsInstancingEnabled(index); - binding_divisors[index] = is_enabled ? regs.vertex_array[index].divisor : 0; + const bool is_enabled = regs.vertex_stream_instances.IsInstancingEnabled(index); + binding_divisors[index] = is_enabled ? regs.vertex_streams[index].frequency : 0; enabled_divisors |= (is_enabled ? u64{1} : 0) << index; } for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { @@ -164,17 +176,17 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t // TODO: C++20 Use templated lambda to deduplicate code - if (!regs.independent_blend_enable) { - const auto& src = regs.blend; - if (!src.enable[index]) { + if (!regs.blend_per_target_enabled) { + if (!regs.blend.enable[index]) { return; } - equation_rgb.Assign(PackBlendEquation(src.equation_rgb)); - equation_a.Assign(PackBlendEquation(src.equation_a)); - factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb)); - factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb)); - factor_source_a.Assign(PackBlendFactor(src.factor_source_a)); - factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a)); + const auto& src = regs.blend; + equation_rgb.Assign(PackBlendEquation(src.color_op)); + equation_a.Assign(PackBlendEquation(src.alpha_op)); + factor_source_rgb.Assign(PackBlendFactor(src.color_source)); + factor_dest_rgb.Assign(PackBlendFactor(src.color_dest)); + factor_source_a.Assign(PackBlendFactor(src.alpha_source)); + factor_dest_a.Assign(PackBlendFactor(src.alpha_dest)); enable.Assign(1); return; } @@ -182,34 +194,34 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t if (!regs.blend.enable[index]) { return; } - const auto& src = regs.independent_blend[index]; - equation_rgb.Assign(PackBlendEquation(src.equation_rgb)); - equation_a.Assign(PackBlendEquation(src.equation_a)); - factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb)); - factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb)); - factor_source_a.Assign(PackBlendFactor(src.factor_source_a)); - factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a)); + const auto& src = regs.blend_per_target[index]; + equation_rgb.Assign(PackBlendEquation(src.color_op)); + equation_a.Assign(PackBlendEquation(src.alpha_op)); + factor_source_rgb.Assign(PackBlendFactor(src.color_source)); + factor_dest_rgb.Assign(PackBlendFactor(src.color_dest)); + factor_source_a.Assign(PackBlendFactor(src.alpha_source)); + factor_dest_a.Assign(PackBlendFactor(src.alpha_dest)); enable.Assign(1); } void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { - u32 packed_front_face = PackFrontFace(regs.front_face); - if (regs.screen_y_control.triangle_rast_flip != 0) { + u32 packed_front_face = PackFrontFace(regs.gl_front_face); + if (regs.window_origin.flip_y != 0) { // Flip front face packed_front_face = 1 - packed_front_face; } raw1 = 0; raw2 = 0; - front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail)); - front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail)); - front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass)); - front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func)); + front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail)); + front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail)); + front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass)); + front.test_func.Assign(PackComparisonOp(regs.stencil_front_op.func)); if (regs.stencil_two_side_enable) { - back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail)); - back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail)); - back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass)); - back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func)); + back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op.fail)); + back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op.zfail)); + back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op.zpass)); + back.test_func.Assign(PackComparisonOp(regs.stencil_back_op.func)); } else { back.action_stencil_fail.Assign(front.action_stencil_fail); back.action_depth_fail.Assign(front.action_depth_fail); @@ -222,9 +234,9 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { depth_test_enable.Assign(regs.depth_test_enable); front_face.Assign(packed_front_face); depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); - cull_face.Assign(PackCullFace(regs.cull_face)); - cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); - std::ranges::transform(regs.vertex_array, vertex_strides.begin(), [](const auto& array) { + cull_face.Assign(PackCullFace(regs.gl_cull_face)); + cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0); + std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { return static_cast(array.stride.Value()); }); } @@ -251,41 +263,42 @@ Maxwell::ComparisonOp FixedPipelineState::UnpackComparisonOp(u32 packed) noexcep return static_cast(packed + 1); } -u32 FixedPipelineState::PackStencilOp(Maxwell::StencilOp op) noexcept { +u32 FixedPipelineState::PackStencilOp(Maxwell::StencilOp::Op op) noexcept { switch (op) { - case Maxwell::StencilOp::Keep: - case Maxwell::StencilOp::KeepOGL: + case Maxwell::StencilOp::Op::Keep_D3D: + case Maxwell::StencilOp::Op::Keep_GL: return 0; - case Maxwell::StencilOp::Zero: - case Maxwell::StencilOp::ZeroOGL: + case Maxwell::StencilOp::Op::Zero_D3D: + case Maxwell::StencilOp::Op::Zero_GL: return 1; - case Maxwell::StencilOp::Replace: - case Maxwell::StencilOp::ReplaceOGL: + case Maxwell::StencilOp::Op::Replace_D3D: + case Maxwell::StencilOp::Op::Replace_GL: return 2; - case Maxwell::StencilOp::Incr: - case Maxwell::StencilOp::IncrOGL: + case Maxwell::StencilOp::Op::IncrSaturate_D3D: + case Maxwell::StencilOp::Op::IncrSaturate_GL: return 3; - case Maxwell::StencilOp::Decr: - case Maxwell::StencilOp::DecrOGL: + case Maxwell::StencilOp::Op::DecrSaturate_D3D: + case Maxwell::StencilOp::Op::DecrSaturate_GL: return 4; - case Maxwell::StencilOp::Invert: - case Maxwell::StencilOp::InvertOGL: + case Maxwell::StencilOp::Op::Invert_D3D: + case Maxwell::StencilOp::Op::Invert_GL: return 5; - case Maxwell::StencilOp::IncrWrap: - case Maxwell::StencilOp::IncrWrapOGL: + case Maxwell::StencilOp::Op::Incr_D3D: + case Maxwell::StencilOp::Op::Incr_GL: return 6; - case Maxwell::StencilOp::DecrWrap: - case Maxwell::StencilOp::DecrWrapOGL: + case Maxwell::StencilOp::Op::Decr_D3D: + case Maxwell::StencilOp::Op::Decr_GL: return 7; } return 0; } -Maxwell::StencilOp FixedPipelineState::UnpackStencilOp(u32 packed) noexcept { - static constexpr std::array LUT = {Maxwell::StencilOp::Keep, Maxwell::StencilOp::Zero, - Maxwell::StencilOp::Replace, Maxwell::StencilOp::Incr, - Maxwell::StencilOp::Decr, Maxwell::StencilOp::Invert, - Maxwell::StencilOp::IncrWrap, Maxwell::StencilOp::DecrWrap}; +Maxwell::StencilOp::Op FixedPipelineState::UnpackStencilOp(u32 packed) noexcept { + static constexpr std::array LUT = { + Maxwell::StencilOp::Op::Keep_D3D, Maxwell::StencilOp::Op::Zero_D3D, + Maxwell::StencilOp::Op::Replace_D3D, Maxwell::StencilOp::Op::IncrSaturate_D3D, + Maxwell::StencilOp::Op::DecrSaturate_D3D, Maxwell::StencilOp::Op::Invert_D3D, + Maxwell::StencilOp::Op::Incr_D3D, Maxwell::StencilOp::Op::Decr_D3D}; return LUT[packed]; } @@ -318,30 +331,30 @@ Maxwell::PolygonMode FixedPipelineState::UnpackPolygonMode(u32 packed) noexcept return static_cast(packed + 0x1B00); } -u32 FixedPipelineState::PackLogicOp(Maxwell::LogicOperation op) noexcept { +u32 FixedPipelineState::PackLogicOp(Maxwell::LogicOp::Op op) noexcept { return static_cast(op) - 0x1500; } -Maxwell::LogicOperation FixedPipelineState::UnpackLogicOp(u32 packed) noexcept { - return static_cast(packed + 0x1500); +Maxwell::LogicOp::Op FixedPipelineState::UnpackLogicOp(u32 packed) noexcept { + return static_cast(packed + 0x1500); } u32 FixedPipelineState::PackBlendEquation(Maxwell::Blend::Equation equation) noexcept { switch (equation) { - case Maxwell::Blend::Equation::Add: - case Maxwell::Blend::Equation::AddGL: + case Maxwell::Blend::Equation::Add_D3D: + case Maxwell::Blend::Equation::Add_GL: return 0; - case Maxwell::Blend::Equation::Subtract: - case Maxwell::Blend::Equation::SubtractGL: + case Maxwell::Blend::Equation::Subtract_D3D: + case Maxwell::Blend::Equation::Subtract_GL: return 1; - case Maxwell::Blend::Equation::ReverseSubtract: - case Maxwell::Blend::Equation::ReverseSubtractGL: + case Maxwell::Blend::Equation::ReverseSubtract_D3D: + case Maxwell::Blend::Equation::ReverseSubtract_GL: return 2; - case Maxwell::Blend::Equation::Min: - case Maxwell::Blend::Equation::MinGL: + case Maxwell::Blend::Equation::Min_D3D: + case Maxwell::Blend::Equation::Min_GL: return 3; - case Maxwell::Blend::Equation::Max: - case Maxwell::Blend::Equation::MaxGL: + case Maxwell::Blend::Equation::Max_D3D: + case Maxwell::Blend::Equation::Max_GL: return 4; } return 0; @@ -349,97 +362,99 @@ u32 FixedPipelineState::PackBlendEquation(Maxwell::Blend::Equation equation) noe Maxwell::Blend::Equation FixedPipelineState::UnpackBlendEquation(u32 packed) noexcept { static constexpr std::array LUT = { - Maxwell::Blend::Equation::Add, Maxwell::Blend::Equation::Subtract, - Maxwell::Blend::Equation::ReverseSubtract, Maxwell::Blend::Equation::Min, - Maxwell::Blend::Equation::Max}; + Maxwell::Blend::Equation::Add_D3D, Maxwell::Blend::Equation::Subtract_D3D, + Maxwell::Blend::Equation::ReverseSubtract_D3D, Maxwell::Blend::Equation::Min_D3D, + Maxwell::Blend::Equation::Max_D3D}; return LUT[packed]; } u32 FixedPipelineState::PackBlendFactor(Maxwell::Blend::Factor factor) noexcept { switch (factor) { - case Maxwell::Blend::Factor::Zero: - case Maxwell::Blend::Factor::ZeroGL: + case Maxwell::Blend::Factor::Zero_D3D: + case Maxwell::Blend::Factor::Zero_GL: return 0; - case Maxwell::Blend::Factor::One: - case Maxwell::Blend::Factor::OneGL: + case Maxwell::Blend::Factor::One_D3D: + case Maxwell::Blend::Factor::One_GL: return 1; - case Maxwell::Blend::Factor::SourceColor: - case Maxwell::Blend::Factor::SourceColorGL: + case Maxwell::Blend::Factor::SourceColor_D3D: + case Maxwell::Blend::Factor::SourceColor_GL: return 2; - case Maxwell::Blend::Factor::OneMinusSourceColor: - case Maxwell::Blend::Factor::OneMinusSourceColorGL: + case Maxwell::Blend::Factor::OneMinusSourceColor_D3D: + case Maxwell::Blend::Factor::OneMinusSourceColor_GL: return 3; - case Maxwell::Blend::Factor::SourceAlpha: - case Maxwell::Blend::Factor::SourceAlphaGL: + case Maxwell::Blend::Factor::SourceAlpha_D3D: + case Maxwell::Blend::Factor::SourceAlpha_GL: return 4; - case Maxwell::Blend::Factor::OneMinusSourceAlpha: - case Maxwell::Blend::Factor::OneMinusSourceAlphaGL: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL: return 5; - case Maxwell::Blend::Factor::DestAlpha: - case Maxwell::Blend::Factor::DestAlphaGL: + case Maxwell::Blend::Factor::DestAlpha_D3D: + case Maxwell::Blend::Factor::DestAlpha_GL: return 6; - case Maxwell::Blend::Factor::OneMinusDestAlpha: - case Maxwell::Blend::Factor::OneMinusDestAlphaGL: + case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusDestAlpha_GL: return 7; - case Maxwell::Blend::Factor::DestColor: - case Maxwell::Blend::Factor::DestColorGL: + case Maxwell::Blend::Factor::DestColor_D3D: + case Maxwell::Blend::Factor::DestColor_GL: return 8; - case Maxwell::Blend::Factor::OneMinusDestColor: - case Maxwell::Blend::Factor::OneMinusDestColorGL: + case Maxwell::Blend::Factor::OneMinusDestColor_D3D: + case Maxwell::Blend::Factor::OneMinusDestColor_GL: return 9; - case Maxwell::Blend::Factor::SourceAlphaSaturate: - case Maxwell::Blend::Factor::SourceAlphaSaturateGL: + case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D: + case Maxwell::Blend::Factor::SourceAlphaSaturate_GL: return 10; - case Maxwell::Blend::Factor::Source1Color: - case Maxwell::Blend::Factor::Source1ColorGL: + case Maxwell::Blend::Factor::Source1Color_D3D: + case Maxwell::Blend::Factor::Source1Color_GL: return 11; - case Maxwell::Blend::Factor::OneMinusSource1Color: - case Maxwell::Blend::Factor::OneMinusSource1ColorGL: + case Maxwell::Blend::Factor::OneMinusSource1Color_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Color_GL: return 12; - case Maxwell::Blend::Factor::Source1Alpha: - case Maxwell::Blend::Factor::Source1AlphaGL: + case Maxwell::Blend::Factor::Source1Alpha_D3D: + case Maxwell::Blend::Factor::Source1Alpha_GL: return 13; - case Maxwell::Blend::Factor::OneMinusSource1Alpha: - case Maxwell::Blend::Factor::OneMinusSource1AlphaGL: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL: return 14; - case Maxwell::Blend::Factor::ConstantColor: - case Maxwell::Blend::Factor::ConstantColorGL: + case Maxwell::Blend::Factor::BlendFactor_D3D: + case Maxwell::Blend::Factor::ConstantColor_GL: return 15; - case Maxwell::Blend::Factor::OneMinusConstantColor: - case Maxwell::Blend::Factor::OneMinusConstantColorGL: + case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D: + case Maxwell::Blend::Factor::OneMinusConstantColor_GL: return 16; - case Maxwell::Blend::Factor::ConstantAlpha: - case Maxwell::Blend::Factor::ConstantAlphaGL: + case Maxwell::Blend::Factor::BothSourceAlpha_D3D: + case Maxwell::Blend::Factor::ConstantAlpha_GL: return 17; - case Maxwell::Blend::Factor::OneMinusConstantAlpha: - case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: + case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL: return 18; } + UNIMPLEMENTED_MSG("Unknown blend factor {}", static_cast(factor)); return 0; } Maxwell::Blend::Factor FixedPipelineState::UnpackBlendFactor(u32 packed) noexcept { static constexpr std::array LUT = { - Maxwell::Blend::Factor::Zero, - Maxwell::Blend::Factor::One, - Maxwell::Blend::Factor::SourceColor, - Maxwell::Blend::Factor::OneMinusSourceColor, - Maxwell::Blend::Factor::SourceAlpha, - Maxwell::Blend::Factor::OneMinusSourceAlpha, - Maxwell::Blend::Factor::DestAlpha, - Maxwell::Blend::Factor::OneMinusDestAlpha, - Maxwell::Blend::Factor::DestColor, - Maxwell::Blend::Factor::OneMinusDestColor, - Maxwell::Blend::Factor::SourceAlphaSaturate, - Maxwell::Blend::Factor::Source1Color, - Maxwell::Blend::Factor::OneMinusSource1Color, - Maxwell::Blend::Factor::Source1Alpha, - Maxwell::Blend::Factor::OneMinusSource1Alpha, - Maxwell::Blend::Factor::ConstantColor, - Maxwell::Blend::Factor::OneMinusConstantColor, - Maxwell::Blend::Factor::ConstantAlpha, - Maxwell::Blend::Factor::OneMinusConstantAlpha, + Maxwell::Blend::Factor::Zero_D3D, + Maxwell::Blend::Factor::One_D3D, + Maxwell::Blend::Factor::SourceColor_D3D, + Maxwell::Blend::Factor::OneMinusSourceColor_D3D, + Maxwell::Blend::Factor::SourceAlpha_D3D, + Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D, + Maxwell::Blend::Factor::DestAlpha_D3D, + Maxwell::Blend::Factor::OneMinusDestAlpha_D3D, + Maxwell::Blend::Factor::DestColor_D3D, + Maxwell::Blend::Factor::OneMinusDestColor_D3D, + Maxwell::Blend::Factor::SourceAlphaSaturate_D3D, + Maxwell::Blend::Factor::Source1Color_D3D, + Maxwell::Blend::Factor::OneMinusSource1Color_D3D, + Maxwell::Blend::Factor::Source1Alpha_D3D, + Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D, + Maxwell::Blend::Factor::BlendFactor_D3D, + Maxwell::Blend::Factor::OneMinusBlendFactor_D3D, + Maxwell::Blend::Factor::BothSourceAlpha_D3D, + Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D, }; + ASSERT(packed < LUT.size()); return LUT[packed]; } diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 9d60756..ab79fb8 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -21,8 +21,8 @@ struct FixedPipelineState { static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept; static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept; - static u32 PackStencilOp(Maxwell::StencilOp op) noexcept; - static Maxwell::StencilOp UnpackStencilOp(u32 packed) noexcept; + static u32 PackStencilOp(Maxwell::StencilOp::Op op) noexcept; + static Maxwell::StencilOp::Op UnpackStencilOp(u32 packed) noexcept; static u32 PackCullFace(Maxwell::CullFace cull) noexcept; static Maxwell::CullFace UnpackCullFace(u32 packed) noexcept; @@ -33,8 +33,8 @@ struct FixedPipelineState { static u32 PackPolygonMode(Maxwell::PolygonMode mode) noexcept; static Maxwell::PolygonMode UnpackPolygonMode(u32 packed) noexcept; - static u32 PackLogicOp(Maxwell::LogicOperation op) noexcept; - static Maxwell::LogicOperation UnpackLogicOp(u32 packed) noexcept; + static u32 PackLogicOp(Maxwell::LogicOp::Op op) noexcept; + static Maxwell::LogicOp::Op UnpackLogicOp(u32 packed) noexcept; static u32 PackBlendEquation(Maxwell::Blend::Equation equation) noexcept; static Maxwell::Blend::Equation UnpackBlendEquation(u32 packed) noexcept; @@ -113,15 +113,15 @@ struct FixedPipelineState { BitField action_depth_pass; BitField test_func; - Maxwell::StencilOp ActionStencilFail() const noexcept { + Maxwell::StencilOp::Op ActionStencilFail() const noexcept { return UnpackStencilOp(action_stencil_fail); } - Maxwell::StencilOp ActionDepthFail() const noexcept { + Maxwell::StencilOp::Op ActionDepthFail() const noexcept { return UnpackStencilOp(action_depth_fail); } - Maxwell::StencilOp ActionDepthPass() const noexcept { + Maxwell::StencilOp::Op ActionDepthPass() const noexcept { return UnpackStencilOp(action_depth_pass); } @@ -195,6 +195,8 @@ struct FixedPipelineState { BitField<12, 1, u32> provoking_vertex_last; BitField<13, 1, u32> conservative_raster_enable; BitField<14, 1, u32> smooth_lines; + BitField<15, 1, u32> alpha_to_coverage_enabled; + BitField<16, 1, u32> alpha_to_one_enabled; }; std::array color_formats; diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index bdb71dc..3e03c5c 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -58,7 +58,7 @@ VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wra case Tegra::Texture::WrapMode::Border: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; case Tegra::Texture::WrapMode::Clamp: - if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) { + if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY) { // Nvidia's Vulkan driver defaults to GL_CLAMP on invalid enumerations, we can hack this // by sending an invalid enumeration. return static_cast(0xcafe); @@ -125,6 +125,7 @@ struct FormatTuple { {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1R5G5B5_UNORM {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT + {VK_FORMAT_A2R10G10B10_UNORM_PACK32, Attachable | Storage}, // A2R10G10B10_UNORM {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle) {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled) {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM @@ -149,7 +150,7 @@ struct FormatTuple { {VK_FORMAT_BC6H_UFLOAT_BLOCK}, // BC6H_UFLOAT {VK_FORMAT_BC6H_SFLOAT_BLOCK}, // BC6H_SFLOAT {VK_FORMAT_ASTC_4x4_UNORM_BLOCK}, // ASTC_2D_4X4_UNORM - {VK_FORMAT_B8G8R8A8_UNORM, Attachable}, // B8G8R8A8_UNORM + {VK_FORMAT_B8G8R8A8_UNORM, Attachable | Storage}, // B8G8R8A8_UNORM {VK_FORMAT_R32G32B32A32_SFLOAT, Attachable | Storage}, // R32G32B32A32_FLOAT {VK_FORMAT_R32G32B32A32_SINT, Attachable | Storage}, // R32G32B32A32_SINT {VK_FORMAT_R32G32_SFLOAT, Attachable | Storage}, // R32G32_FLOAT @@ -159,7 +160,7 @@ struct FormatTuple { {VK_FORMAT_R16_UNORM, Attachable | Storage}, // R16_UNORM {VK_FORMAT_R16_SNORM, Attachable | Storage}, // R16_SNORM {VK_FORMAT_R16_UINT, Attachable | Storage}, // R16_UINT - {VK_FORMAT_UNDEFINED}, // R16_SINT + {VK_FORMAT_R16_SINT, Attachable | Storage}, // R16_SINT {VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // R16G16_UNORM {VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // R16G16_FLOAT {VK_FORMAT_R16G16_UINT, Attachable | Storage}, // R16G16_UINT @@ -183,8 +184,8 @@ struct FormatTuple { {VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB - {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM - {VK_FORMAT_R4G4_UNORM_PACK8}, // R4G4_UNORM + {VK_FORMAT_R4G4B4A4_UNORM_PACK16}, // A4B4G4R4_UNORM + {VK_FORMAT_R4G4_UNORM_PACK8}, // G4R4_UNORM {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB {VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, // ASTC_2D_8X5_SRGB @@ -196,6 +197,8 @@ struct FormatTuple { {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM + {VK_FORMAT_ASTC_10x5_UNORM_BLOCK}, // ASTC_2D_10X5_UNORM + {VK_FORMAT_ASTC_10x5_SRGB_BLOCK}, // ASTC_2D_10X5_SRGB {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM @@ -321,161 +324,182 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { const VkFormat format{([&]() { switch (type) { - case Maxwell::VertexAttribute::Type::UnsignedNorm: + case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway: + ASSERT_MSG(false, "Invalid vertex attribute type!"); + break; + case Maxwell::VertexAttribute::Type::UNorm: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_UNORM; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_UNORM; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_UNORM; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_UNORM; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_UNORM; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_UNORM; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_UNORM; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_UNORM; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_UNORM_PACK32; default: break; } break; - case Maxwell::VertexAttribute::Type::SignedNorm: + case Maxwell::VertexAttribute::Type::SNorm: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_SNORM; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_SNORM; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_SNORM; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_SNORM; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_SNORM; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_SNORM; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_SNORM; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_SNORM; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_SNORM_PACK32; default: break; } break; - case Maxwell::VertexAttribute::Type::UnsignedScaled: + case Maxwell::VertexAttribute::Type::UScaled: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_USCALED; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_USCALED; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_USCALED; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_USCALED; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_USCALED; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_USCALED; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_USCALED; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_USCALED; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_USCALED_PACK32; default: break; } break; - case Maxwell::VertexAttribute::Type::SignedScaled: + case Maxwell::VertexAttribute::Type::SScaled: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_SSCALED; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_SSCALED_PACK32; default: break; } break; - case Maxwell::VertexAttribute::Type::UnsignedInt: + case Maxwell::VertexAttribute::Type::UInt: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_UINT; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_UINT; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_UINT; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_UINT; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_UINT; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_UINT; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_UINT; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_UINT; - case Maxwell::VertexAttribute::Size::Size_32: + case Maxwell::VertexAttribute::Size::Size_R32: return VK_FORMAT_R32_UINT; - case Maxwell::VertexAttribute::Size::Size_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: return VK_FORMAT_R32G32_UINT; - case Maxwell::VertexAttribute::Size::Size_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: return VK_FORMAT_R32G32B32_UINT; - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return VK_FORMAT_R32G32B32A32_UINT; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_UINT_PACK32; default: break; } break; - case Maxwell::VertexAttribute::Type::SignedInt: + case Maxwell::VertexAttribute::Type::SInt: switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: + case Maxwell::VertexAttribute::Size::Size_R8: + case Maxwell::VertexAttribute::Size::Size_A8: return VK_FORMAT_R8_SINT; - case Maxwell::VertexAttribute::Size::Size_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8: + case Maxwell::VertexAttribute::Size::Size_G8_R8: return VK_FORMAT_R8G8_SINT; - case Maxwell::VertexAttribute::Size::Size_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8: return VK_FORMAT_R8G8B8_SINT; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8: + case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8: return VK_FORMAT_R8G8B8A8_SINT; - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_SINT; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_SINT; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_SINT; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_SINT; - case Maxwell::VertexAttribute::Size::Size_32: + case Maxwell::VertexAttribute::Size::Size_R32: return VK_FORMAT_R32_SINT; - case Maxwell::VertexAttribute::Size::Size_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: return VK_FORMAT_R32G32_SINT; - case Maxwell::VertexAttribute::Size::Size_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: return VK_FORMAT_R32G32B32_SINT; - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return VK_FORMAT_R32G32B32A32_SINT; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10: return VK_FORMAT_A2B10G10R10_SINT_PACK32; default: break; @@ -483,23 +507,23 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, break; case Maxwell::VertexAttribute::Type::Float: switch (size) { - case Maxwell::VertexAttribute::Size::Size_16: + case Maxwell::VertexAttribute::Size::Size_R16: return VK_FORMAT_R16_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16: return VK_FORMAT_R16G16_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16: return VK_FORMAT_R16G16B16_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16: return VK_FORMAT_R16G16B16A16_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_32: + case Maxwell::VertexAttribute::Size::Size_R32: return VK_FORMAT_R32_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32: return VK_FORMAT_R32G32_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32: return VK_FORMAT_R32G32B32_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32: return VK_FORMAT_R32G32B32A32_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_11_11_10: + case Maxwell::VertexAttribute::Size::Size_B10_G11_R11: return VK_FORMAT_B10G11R11_UFLOAT_PACK32; default: break; @@ -519,29 +543,29 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison) { switch (comparison) { - case Maxwell::ComparisonOp::Never: - case Maxwell::ComparisonOp::NeverOld: + case Maxwell::ComparisonOp::Never_D3D: + case Maxwell::ComparisonOp::Never_GL: return VK_COMPARE_OP_NEVER; - case Maxwell::ComparisonOp::Less: - case Maxwell::ComparisonOp::LessOld: + case Maxwell::ComparisonOp::Less_D3D: + case Maxwell::ComparisonOp::Less_GL: return VK_COMPARE_OP_LESS; - case Maxwell::ComparisonOp::Equal: - case Maxwell::ComparisonOp::EqualOld: + case Maxwell::ComparisonOp::Equal_D3D: + case Maxwell::ComparisonOp::Equal_GL: return VK_COMPARE_OP_EQUAL; - case Maxwell::ComparisonOp::LessEqual: - case Maxwell::ComparisonOp::LessEqualOld: + case Maxwell::ComparisonOp::LessEqual_D3D: + case Maxwell::ComparisonOp::LessEqual_GL: return VK_COMPARE_OP_LESS_OR_EQUAL; - case Maxwell::ComparisonOp::Greater: - case Maxwell::ComparisonOp::GreaterOld: + case Maxwell::ComparisonOp::Greater_D3D: + case Maxwell::ComparisonOp::Greater_GL: return VK_COMPARE_OP_GREATER; - case Maxwell::ComparisonOp::NotEqual: - case Maxwell::ComparisonOp::NotEqualOld: + case Maxwell::ComparisonOp::NotEqual_D3D: + case Maxwell::ComparisonOp::NotEqual_GL: return VK_COMPARE_OP_NOT_EQUAL; - case Maxwell::ComparisonOp::GreaterEqual: - case Maxwell::ComparisonOp::GreaterEqualOld: + case Maxwell::ComparisonOp::GreaterEqual_D3D: + case Maxwell::ComparisonOp::GreaterEqual_GL: return VK_COMPARE_OP_GREATER_OR_EQUAL; - case Maxwell::ComparisonOp::Always: - case Maxwell::ComparisonOp::AlwaysOld: + case Maxwell::ComparisonOp::Always_D3D: + case Maxwell::ComparisonOp::Always_GL: return VK_COMPARE_OP_ALWAYS; } UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); @@ -561,31 +585,31 @@ VkIndexType IndexFormat(Maxwell::IndexFormat index_format) { return {}; } -VkStencilOp StencilOp(Maxwell::StencilOp stencil_op) { +VkStencilOp StencilOp(Maxwell::StencilOp::Op stencil_op) { switch (stencil_op) { - case Maxwell::StencilOp::Keep: - case Maxwell::StencilOp::KeepOGL: + case Maxwell::StencilOp::Op::Keep_D3D: + case Maxwell::StencilOp::Op::Keep_GL: return VK_STENCIL_OP_KEEP; - case Maxwell::StencilOp::Zero: - case Maxwell::StencilOp::ZeroOGL: + case Maxwell::StencilOp::Op::Zero_D3D: + case Maxwell::StencilOp::Op::Zero_GL: return VK_STENCIL_OP_ZERO; - case Maxwell::StencilOp::Replace: - case Maxwell::StencilOp::ReplaceOGL: + case Maxwell::StencilOp::Op::Replace_D3D: + case Maxwell::StencilOp::Op::Replace_GL: return VK_STENCIL_OP_REPLACE; - case Maxwell::StencilOp::Incr: - case Maxwell::StencilOp::IncrOGL: + case Maxwell::StencilOp::Op::IncrSaturate_D3D: + case Maxwell::StencilOp::Op::IncrSaturate_GL: return VK_STENCIL_OP_INCREMENT_AND_CLAMP; - case Maxwell::StencilOp::Decr: - case Maxwell::StencilOp::DecrOGL: + case Maxwell::StencilOp::Op::DecrSaturate_D3D: + case Maxwell::StencilOp::Op::DecrSaturate_GL: return VK_STENCIL_OP_DECREMENT_AND_CLAMP; - case Maxwell::StencilOp::Invert: - case Maxwell::StencilOp::InvertOGL: + case Maxwell::StencilOp::Op::Invert_D3D: + case Maxwell::StencilOp::Op::Invert_GL: return VK_STENCIL_OP_INVERT; - case Maxwell::StencilOp::IncrWrap: - case Maxwell::StencilOp::IncrWrapOGL: + case Maxwell::StencilOp::Op::Incr_D3D: + case Maxwell::StencilOp::Op::Incr_GL: return VK_STENCIL_OP_INCREMENT_AND_WRAP; - case Maxwell::StencilOp::DecrWrap: - case Maxwell::StencilOp::DecrWrapOGL: + case Maxwell::StencilOp::Op::Decr_D3D: + case Maxwell::StencilOp::Op::Decr_GL: return VK_STENCIL_OP_DECREMENT_AND_WRAP; } UNIMPLEMENTED_MSG("Unimplemented stencil op={}", stencil_op); @@ -594,20 +618,20 @@ VkStencilOp StencilOp(Maxwell::StencilOp stencil_op) { VkBlendOp BlendEquation(Maxwell::Blend::Equation equation) { switch (equation) { - case Maxwell::Blend::Equation::Add: - case Maxwell::Blend::Equation::AddGL: + case Maxwell::Blend::Equation::Add_D3D: + case Maxwell::Blend::Equation::Add_GL: return VK_BLEND_OP_ADD; - case Maxwell::Blend::Equation::Subtract: - case Maxwell::Blend::Equation::SubtractGL: + case Maxwell::Blend::Equation::Subtract_D3D: + case Maxwell::Blend::Equation::Subtract_GL: return VK_BLEND_OP_SUBTRACT; - case Maxwell::Blend::Equation::ReverseSubtract: - case Maxwell::Blend::Equation::ReverseSubtractGL: + case Maxwell::Blend::Equation::ReverseSubtract_D3D: + case Maxwell::Blend::Equation::ReverseSubtract_GL: return VK_BLEND_OP_REVERSE_SUBTRACT; - case Maxwell::Blend::Equation::Min: - case Maxwell::Blend::Equation::MinGL: + case Maxwell::Blend::Equation::Min_D3D: + case Maxwell::Blend::Equation::Min_GL: return VK_BLEND_OP_MIN; - case Maxwell::Blend::Equation::Max: - case Maxwell::Blend::Equation::MaxGL: + case Maxwell::Blend::Equation::Max_D3D: + case Maxwell::Blend::Equation::Max_GL: return VK_BLEND_OP_MAX; } UNIMPLEMENTED_MSG("Unimplemented blend equation={}", equation); @@ -616,62 +640,62 @@ VkBlendOp BlendEquation(Maxwell::Blend::Equation equation) { VkBlendFactor BlendFactor(Maxwell::Blend::Factor factor) { switch (factor) { - case Maxwell::Blend::Factor::Zero: - case Maxwell::Blend::Factor::ZeroGL: + case Maxwell::Blend::Factor::Zero_D3D: + case Maxwell::Blend::Factor::Zero_GL: return VK_BLEND_FACTOR_ZERO; - case Maxwell::Blend::Factor::One: - case Maxwell::Blend::Factor::OneGL: + case Maxwell::Blend::Factor::One_D3D: + case Maxwell::Blend::Factor::One_GL: return VK_BLEND_FACTOR_ONE; - case Maxwell::Blend::Factor::SourceColor: - case Maxwell::Blend::Factor::SourceColorGL: + case Maxwell::Blend::Factor::SourceColor_D3D: + case Maxwell::Blend::Factor::SourceColor_GL: return VK_BLEND_FACTOR_SRC_COLOR; - case Maxwell::Blend::Factor::OneMinusSourceColor: - case Maxwell::Blend::Factor::OneMinusSourceColorGL: + case Maxwell::Blend::Factor::OneMinusSourceColor_D3D: + case Maxwell::Blend::Factor::OneMinusSourceColor_GL: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; - case Maxwell::Blend::Factor::SourceAlpha: - case Maxwell::Blend::Factor::SourceAlphaGL: + case Maxwell::Blend::Factor::SourceAlpha_D3D: + case Maxwell::Blend::Factor::SourceAlpha_GL: return VK_BLEND_FACTOR_SRC_ALPHA; - case Maxwell::Blend::Factor::OneMinusSourceAlpha: - case Maxwell::Blend::Factor::OneMinusSourceAlphaGL: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - case Maxwell::Blend::Factor::DestAlpha: - case Maxwell::Blend::Factor::DestAlphaGL: + case Maxwell::Blend::Factor::DestAlpha_D3D: + case Maxwell::Blend::Factor::DestAlpha_GL: return VK_BLEND_FACTOR_DST_ALPHA; - case Maxwell::Blend::Factor::OneMinusDestAlpha: - case Maxwell::Blend::Factor::OneMinusDestAlphaGL: + case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusDestAlpha_GL: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; - case Maxwell::Blend::Factor::DestColor: - case Maxwell::Blend::Factor::DestColorGL: + case Maxwell::Blend::Factor::DestColor_D3D: + case Maxwell::Blend::Factor::DestColor_GL: return VK_BLEND_FACTOR_DST_COLOR; - case Maxwell::Blend::Factor::OneMinusDestColor: - case Maxwell::Blend::Factor::OneMinusDestColorGL: + case Maxwell::Blend::Factor::OneMinusDestColor_D3D: + case Maxwell::Blend::Factor::OneMinusDestColor_GL: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; - case Maxwell::Blend::Factor::SourceAlphaSaturate: - case Maxwell::Blend::Factor::SourceAlphaSaturateGL: + case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D: + case Maxwell::Blend::Factor::SourceAlphaSaturate_GL: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; - case Maxwell::Blend::Factor::Source1Color: - case Maxwell::Blend::Factor::Source1ColorGL: + case Maxwell::Blend::Factor::Source1Color_D3D: + case Maxwell::Blend::Factor::Source1Color_GL: return VK_BLEND_FACTOR_SRC1_COLOR; - case Maxwell::Blend::Factor::OneMinusSource1Color: - case Maxwell::Blend::Factor::OneMinusSource1ColorGL: + case Maxwell::Blend::Factor::OneMinusSource1Color_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Color_GL: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR; - case Maxwell::Blend::Factor::Source1Alpha: - case Maxwell::Blend::Factor::Source1AlphaGL: + case Maxwell::Blend::Factor::Source1Alpha_D3D: + case Maxwell::Blend::Factor::Source1Alpha_GL: return VK_BLEND_FACTOR_SRC1_ALPHA; - case Maxwell::Blend::Factor::OneMinusSource1Alpha: - case Maxwell::Blend::Factor::OneMinusSource1AlphaGL: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D: + case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA; - case Maxwell::Blend::Factor::ConstantColor: - case Maxwell::Blend::Factor::ConstantColorGL: + case Maxwell::Blend::Factor::BlendFactor_D3D: + case Maxwell::Blend::Factor::ConstantColor_GL: return VK_BLEND_FACTOR_CONSTANT_COLOR; - case Maxwell::Blend::Factor::OneMinusConstantColor: - case Maxwell::Blend::Factor::OneMinusConstantColorGL: + case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D: + case Maxwell::Blend::Factor::OneMinusConstantColor_GL: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; - case Maxwell::Blend::Factor::ConstantAlpha: - case Maxwell::Blend::Factor::ConstantAlphaGL: + case Maxwell::Blend::Factor::BothSourceAlpha_D3D: + case Maxwell::Blend::Factor::ConstantAlpha_GL: return VK_BLEND_FACTOR_CONSTANT_ALPHA; - case Maxwell::Blend::Factor::OneMinusConstantAlpha: - case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: + case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D: + case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA; } UNIMPLEMENTED_MSG("Unimplemented blend factor={}", factor); diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h index 356d462..6f65502 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.h +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h @@ -55,7 +55,7 @@ VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison); VkIndexType IndexFormat(Maxwell::IndexFormat index_format); -VkStencilOp StencilOp(Maxwell::StencilOp stencil_op); +VkStencilOp StencilOp(Maxwell::StencilOp::Op stencil_op); VkBlendOp BlendEquation(Maxwell::Blend::Equation equation); diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index b24f342..28b893e 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -44,17 +44,17 @@ public: }); } - vk::DescriptorUpdateTemplateKHR CreateTemplate(VkDescriptorSetLayout descriptor_set_layout, - VkPipelineLayout pipeline_layout, - bool use_push_descriptor) const { + vk::DescriptorUpdateTemplate CreateTemplate(VkDescriptorSetLayout descriptor_set_layout, + VkPipelineLayout pipeline_layout, + bool use_push_descriptor) const { if (entries.empty()) { return nullptr; } const VkDescriptorUpdateTemplateType type = use_push_descriptor ? VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR - : VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR; - return device->GetLogical().CreateDescriptorUpdateTemplateKHR({ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR, + : VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET; + return device->GetLogical().CreateDescriptorUpdateTemplate({ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .descriptorUpdateEntryCount = static_cast(entries.size()), @@ -68,13 +68,15 @@ public: } vk::PipelineLayout CreatePipelineLayout(VkDescriptorSetLayout descriptor_set_layout) const { + using Shader::Backend::SPIRV::RenderAreaLayout; using Shader::Backend::SPIRV::RescalingLayout; const u32 size_offset = is_compute ? sizeof(RescalingLayout::down_factor) : 0u; const VkPushConstantRange range{ .stageFlags = static_cast( is_compute ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS), .offset = 0, - .size = static_cast(sizeof(RescalingLayout)) - size_offset, + .size = static_cast(sizeof(RescalingLayout)) - size_offset + + static_cast(sizeof(RenderAreaLayout)), }; return device->GetLogical().CreatePipelineLayout({ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, @@ -127,7 +129,7 @@ private: const Device* device{}; bool is_compute{}; boost::container::small_vector bindings; - boost::container::small_vector entries; + boost::container::small_vector entries; u32 binding{}; u32 num_descriptors{}; size_t offset{}; @@ -167,6 +169,12 @@ private: u32 image_bit{1u}; }; +class RenderAreaPushConstant { +public: + bool uses_render_area{}; + std::array words{}; +}; + inline void PushImageDescriptors(TextureCache& texture_cache, UpdateDescriptorQueue& update_descriptor_queue, const Shader::Info& info, RescalingPushConstant& rescaling, diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 7c78d02..f502a7d 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -45,14 +45,14 @@ std::string GetDriverVersion(const Device& device) { // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314 const u32 version = device.GetDriverVersion(); - if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) { + if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY) { const u32 major = (version >> 22) & 0x3ff; const u32 minor = (version >> 14) & 0x0ff; const u32 secondary = (version >> 6) & 0x0ff; const u32 tertiary = version & 0x003f; return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary); } - if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR) { + if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { const u32 major = version >> 14; const u32 minor = version & 0x3fff; return fmt::format("{}.{}", major, minor); @@ -102,13 +102,13 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), surface(CreateSurface(instance, render_window)), device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), - state_tracker(gpu), scheduler(device, state_tracker), + state_tracker(), scheduler(device, state_tracker), swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, render_window.GetFramebufferLayout().height, false), blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler, screen_info), - rasterizer(render_window, gpu, gpu.MemoryManager(), cpu_memory, screen_info, device, - memory_allocator, state_tracker, scheduler) { + rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator, + state_tracker, scheduler) { Report(); } catch (const vk::Exception& exception) { LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what()); @@ -139,23 +139,25 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { RenderScreenshot(*framebuffer, use_accelerated); bool has_been_recreated = false; - const auto recreate_swapchain = [&] { + const auto recreate_swapchain = [&](u32 width, u32 height) { if (!has_been_recreated) { has_been_recreated = true; - scheduler.WaitWorker(); + scheduler.Finish(); } - const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); - swapchain.Create(layout.width, layout.height, is_srgb); + swapchain.Create(width, height, is_srgb); }; - if (swapchain.NeedsRecreation(is_srgb)) { - recreate_swapchain(); + + const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); + if (swapchain.NeedsRecreation(is_srgb) || swapchain.GetWidth() != layout.width || + swapchain.GetHeight() != layout.height) { + recreate_swapchain(layout.width, layout.height); } bool is_outdated; do { swapchain.AcquireNextImage(); is_outdated = swapchain.IsOutDated(); if (is_outdated) { - recreate_swapchain(); + recreate_swapchain(layout.width, layout.height); } } while (is_outdated); if (has_been_recreated) { @@ -172,6 +174,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { } void RendererVulkan::Report() const { + using namespace Common::Literals; const std::string vendor_name{device.GetVendorName()}; const std::string model_name{device.GetModelName()}; const std::string driver_version = GetDriverVersion(device); @@ -181,9 +184,12 @@ void RendererVulkan::Report() const { const std::string extensions = BuildCommaSeparatedExtensions(device.GetAvailableExtensions()); + const auto available_vram = static_cast(device.GetDeviceLocalMemory()) / f64{1_GiB}; + LOG_INFO(Render_Vulkan, "Driver: {}", driver_name); LOG_INFO(Render_Vulkan, "Device: {}", model_name); LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version); + LOG_INFO(Render_Vulkan, "Available VRAM: {:.2f} GiB", available_vram); static constexpr auto field = Common::Telemetry::FieldType::UserSystem; telemetry_session.AddField(field, "GPU_Vendor", vendor_name); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 444c29f..2f0cc27 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/math_util.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "core/core.h" #include "core/frontend/emu_window.h" @@ -28,6 +29,7 @@ #include "video_core/renderer_vulkan/vk_fsr.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/renderer_vulkan/vk_smaa.h" #include "video_core/renderer_vulkan/vk_swapchain.h" #include "video_core/surface.h" #include "video_core/textures/decoders.h" @@ -145,11 +147,17 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, // Finish any pending renderpass scheduler.RequestOutsideRenderPassOperationContext(); + if (const auto swapchain_images = swapchain.GetImageCount(); swapchain_images != image_count) { + image_count = swapchain_images; + Recreate(); + } + const std::size_t image_index = swapchain.GetImageIndex(); scheduler.Wait(resource_ticks[image_index]); resource_ticks[image_index] = scheduler.CurrentTick(); + VkImage source_image = use_accelerated ? screen_info.image : *raw_images[image_index]; VkImageView source_image_view = use_accelerated ? screen_info.image_view : *raw_image_views[image_index]; @@ -236,7 +244,7 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, } const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue(); - if (use_accelerated && anti_alias_pass != Settings::AntiAliasing::None) { + if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) { UpdateAADescriptorSet(image_index, source_image_view, false); const u32 up_scale = Settings::values.resolution_info.up_scale; const u32 down_shift = Settings::values.resolution_info.down_shift; @@ -334,7 +342,18 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, }); source_image_view = *aa_image_view; } - + if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Smaa) { + if (!smaa) { + const u32 up_scale = Settings::values.resolution_info.up_scale; + const u32 down_shift = Settings::values.resolution_info.down_shift; + const VkExtent2D smaa_size{ + .width = (up_scale * framebuffer.width) >> down_shift, + .height = (up_scale * framebuffer.height) >> down_shift, + }; + CreateSMAA(smaa_size); + } + source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view); + } if (fsr) { auto crop_rect = framebuffer.crop_rect; if (crop_rect.GetWidth() == 0) { @@ -448,19 +467,20 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkE void BlitScreen::CreateStaticResources() { CreateShaders(); + CreateSampler(); +} + +void BlitScreen::CreateDynamicResources() { CreateSemaphores(); CreateDescriptorPool(); CreateDescriptorSetLayout(); CreateDescriptorSets(); CreatePipelineLayout(); - CreateSampler(); -} - -void BlitScreen::CreateDynamicResources() { CreateRenderPass(); CreateFramebuffers(); CreateGraphicsPipeline(); fsr.reset(); + smaa.reset(); if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { CreateFSR(); } @@ -475,11 +495,16 @@ void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { fsr.reset(); } - if (framebuffer.width == raw_width && framebuffer.height == raw_height && !raw_images.empty()) { + if (framebuffer.width == raw_width && framebuffer.height == raw_height && + framebuffer.pixel_format == pixel_format && !raw_images.empty()) { return; } + raw_width = framebuffer.width; raw_height = framebuffer.height; + pixel_format = framebuffer.pixel_format; + + smaa.reset(); ReleaseRawImages(); CreateStagingBuffer(framebuffer); @@ -1438,6 +1463,10 @@ void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, left_start + right * scale_v); } +void BlitScreen::CreateSMAA(VkExtent2D smaa_size) { + smaa = std::make_unique(device, memory_allocator, image_count, smaa_size); +} + void BlitScreen::CreateFSR() { const auto& layout = render_window.GetFramebufferLayout(); const VkExtent2D fsr_size{ diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index b8c67be..ebe10b0 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -28,6 +28,10 @@ namespace VideoCore { class RasterizerInterface; } +namespace Service::android { +enum class PixelFormat : u32; +} + namespace Vulkan { struct ScreenInfo; @@ -36,9 +40,11 @@ class Device; class FSR; class RasterizerVulkan; class Scheduler; +class SMAA; class Swapchain; struct ScreenInfo { + VkImage image{}; VkImageView image_view{}; u32 width{}; u32 height{}; @@ -97,6 +103,7 @@ private: void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, const Layout::FramebufferLayout layout) const; + void CreateSMAA(VkExtent2D smaa_size); void CreateFSR(); u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; @@ -109,7 +116,7 @@ private: MemoryAllocator& memory_allocator; Swapchain& swapchain; Scheduler& scheduler; - const std::size_t image_count; + std::size_t image_count; const ScreenInfo& screen_info; vk::ShaderModule vertex_shader; @@ -156,8 +163,10 @@ private: u32 raw_width = 0; u32 raw_height = 0; + Service::android::PixelFormat pixel_format{}; std::unique_ptr fsr; + std::unique_ptr smaa; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 558b8db..84d36fe 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -285,6 +285,9 @@ void BufferCacheRuntime::BindQuadArrayIndexBuffer(u32 first, u32 count) { void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride) { + if (index >= device.GetMaxVertexInputBindings()) { + return; + } if (device.IsExtExtendedDynamicStateSupported()) { scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index f17a5cc..2c00979 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -26,8 +26,6 @@ namespace Vulkan { -using Tegra::Texture::SWIZZLE_TABLE; - namespace { constexpr u32 ASTC_BINDING_INPUT_BUFFER = 0; @@ -95,7 +93,7 @@ constexpr DescriptorBankInfo ASTC_BANK_INFO{ .score = 2, }; -constexpr VkDescriptorUpdateTemplateEntryKHR INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE{ +constexpr VkDescriptorUpdateTemplateEntry INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE{ .dstBinding = 0, .dstArrayElement = 0, .descriptorCount = 2, @@ -104,7 +102,7 @@ constexpr VkDescriptorUpdateTemplateEntryKHR INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMP .stride = sizeof(DescriptorUpdateEntry), }; -constexpr std::array +constexpr std::array ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY{{ { .dstBinding = ASTC_BINDING_INPUT_BUFFER, @@ -136,7 +134,7 @@ struct AstcPushConstants { ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, vk::Span bindings, - vk::Span templates, + vk::Span templates, const DescriptorBankInfo& bank_info, vk::Span push_constants, std::span code) : device{device_} { @@ -157,13 +155,13 @@ ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, .pPushConstantRanges = push_constants.data(), }); if (!templates.empty()) { - descriptor_template = device.GetLogical().CreateDescriptorUpdateTemplateKHR({ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR, + descriptor_template = device.GetLogical().CreateDescriptorUpdateTemplate({ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .descriptorUpdateEntryCount = templates.size(), .pDescriptorUpdateEntries = templates.data(), - .templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR, + .templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET, .descriptorSetLayout = *descriptor_set_layout, .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .pipelineLayout = *layout, diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index dcc691a..5d32e3c 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -29,14 +29,14 @@ class ComputePass { public: explicit ComputePass(const Device& device, DescriptorPool& descriptor_pool, vk::Span bindings, - vk::Span templates, + vk::Span templates, const DescriptorBankInfo& bank_info, vk::Span push_constants, std::span code); ~ComputePass(); protected: const Device& device; - vk::DescriptorUpdateTemplateKHR descriptor_template; + vk::DescriptorUpdateTemplate descriptor_template; vk::PipelineLayout layout; vk::Pipeline pipeline; vk::DescriptorSetLayout descriptor_set_layout; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 6447210..04a3a86 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -53,7 +53,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript .requiredSubgroupSize = GuestWarpSize, }; VkPipelineCreateFlags flags{}; - if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { + if (device.IsKhrPipelineExecutablePropertiesEnabled()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } pipeline = device.GetLogical().CreateComputePipeline({ @@ -126,8 +126,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset}; const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() + secondary_offset}; - const u32 lhs_raw{gpu_memory.Read(addr)}; - const u32 rhs_raw{gpu_memory.Read(separate_addr)}; + const u32 lhs_raw{gpu_memory.Read(addr) << desc.shift_left}; + const u32 rhs_raw{gpu_memory.Read(separate_addr) << desc.secondary_shift_left}; return TexturePair(lhs_raw | rhs_raw, via_header_index); } } diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index 9879735..d70837f 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -55,7 +55,7 @@ private: vk::DescriptorSetLayout descriptor_set_layout; DescriptorAllocator descriptor_allocator; vk::PipelineLayout pipeline_layout; - vk::DescriptorUpdateTemplateKHR descriptor_update_template; + vk::DescriptorUpdateTemplate descriptor_update_template; vk::Pipeline pipeline; std::condition_variable build_condvar; diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp index c7196b6..b5ae644 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp @@ -7,6 +7,7 @@ #include #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_resource_pool.h" #include "video_core/renderer_vulkan/vk_scheduler.h" diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp index c249b34..0214b10 100644 --- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp @@ -11,11 +11,8 @@ namespace Vulkan { -InnerFence::InnerFence(Scheduler& scheduler_, u32 payload_, bool is_stubbed_) - : FenceBase{payload_, is_stubbed_}, scheduler{scheduler_} {} - -InnerFence::InnerFence(Scheduler& scheduler_, GPUVAddr address_, u32 payload_, bool is_stubbed_) - : FenceBase{address_, payload_, is_stubbed_}, scheduler{scheduler_} {} +InnerFence::InnerFence(Scheduler& scheduler_, bool is_stubbed_) + : FenceBase{is_stubbed_}, scheduler{scheduler_} {} InnerFence::~InnerFence() = default; @@ -48,12 +45,8 @@ FenceManager::FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::G : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_}, scheduler{scheduler_} {} -Fence FenceManager::CreateFence(u32 value, bool is_stubbed) { - return std::make_shared(scheduler, value, is_stubbed); -} - -Fence FenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) { - return std::make_shared(scheduler, addr, value, is_stubbed); +Fence FenceManager::CreateFence(bool is_stubbed) { + return std::make_shared(scheduler, is_stubbed); } void FenceManager::QueueFence(Fence& fence) { diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h index 7c0bbd8..7fe2afc 100644 --- a/src/video_core/renderer_vulkan/vk_fence_manager.h +++ b/src/video_core/renderer_vulkan/vk_fence_manager.h @@ -25,8 +25,7 @@ class Scheduler; class InnerFence : public VideoCommon::FenceBase { public: - explicit InnerFence(Scheduler& scheduler_, u32 payload_, bool is_stubbed_); - explicit InnerFence(Scheduler& scheduler_, GPUVAddr address_, u32 payload_, bool is_stubbed_); + explicit InnerFence(Scheduler& scheduler_, bool is_stubbed_); ~InnerFence(); void Queue(); @@ -50,8 +49,7 @@ public: QueryCache& query_cache, const Device& device, Scheduler& scheduler); protected: - Fence CreateFence(u32 value, bool is_stubbed) override; - Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override; + Fence CreateFence(bool is_stubbed) override; void QueueFence(Fence& fence) override; bool IsFenceSignaled(Fence& fence) const override; void WaitFence(Fence& fence) override; diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp index dd45016..33daa8c 100644 --- a/src/video_core/renderer_vulkan/vk_fsr.cpp +++ b/src/video_core/renderer_vulkan/vk_fsr.cpp @@ -5,6 +5,7 @@ #include "common/bit_cast.h" #include "common/common_types.h" #include "common/div_ceil.h" +#include "common/settings.h" #include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h" #include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h" @@ -227,7 +228,10 @@ VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView imag cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *rcas_pipeline); - FsrRcasCon(push_constants.data(), 0.25f); + const float sharpening = + static_cast(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; + + FsrRcasCon(push_constants.data(), sharpening); cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 5aca8f0..515d8d8 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -31,6 +31,7 @@ namespace { using boost::container::small_vector; using boost::container::static_vector; using Shader::ImageBufferDescriptor; +using Shader::Backend::SPIRV::RENDERAREA_LAYOUT_OFFSET; using Shader::Backend::SPIRV::RESCALING_LAYOUT_DOWN_FACTOR_OFFSET; using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; using Tegra::Texture::TexturePair; @@ -215,15 +216,14 @@ ConfigureFuncPtr ConfigureFunc(const std::array& m } // Anonymous namespace GraphicsPipeline::GraphicsPipeline( - Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, Scheduler& scheduler_, - BufferCache& buffer_cache_, TextureCache& texture_cache_, + Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_, std::array stages, const std::array& infos) - : key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_}, - texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_}, + : key{key_}, device{device_}, texture_cache{texture_cache_}, + buffer_cache{buffer_cache_}, scheduler{scheduler_}, update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} { if (shader_notify) { shader_notify->MarkShaderBuilding(); @@ -288,8 +288,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { buffer_cache.SetUniformBuffersState(enabled_uniform_buffer_masks, &uniform_buffer_sizes); - const auto& regs{maxwell3d.regs}; - const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex}; + const auto& regs{maxwell3d->regs}; + const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE { const Shader::Info& info{stage_infos[stage]}; buffer_cache.UnbindGraphicsStorageBuffers(stage); @@ -302,7 +302,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { ++ssbo_index; } } - const auto& cbufs{maxwell3d.state.shader_stages[stage].const_buffers}; + const auto& cbufs{maxwell3d->state.shader_stages[stage].const_buffers}; const auto read_handle{[&](const auto& desc, u32 index) { ASSERT(cbufs[desc.cbuf_index].enabled); const u32 index_offset{index << desc.size_shift}; @@ -315,13 +315,14 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { const u32 second_offset{desc.secondary_cbuf_offset + index_offset}; const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address + second_offset}; - const u32 lhs_raw{gpu_memory.Read(addr)}; - const u32 rhs_raw{gpu_memory.Read(separate_addr)}; + const u32 lhs_raw{gpu_memory->Read(addr) << desc.shift_left}; + const u32 rhs_raw{gpu_memory->Read(separate_addr) + << desc.secondary_shift_left}; const u32 raw{lhs_raw | rhs_raw}; return TexturePair(raw, via_header_index); } } - return TexturePair(gpu_memory.Read(addr), via_header_index); + return TexturePair(gpu_memory->Read(addr), via_header_index); }}; const auto add_image{[&](const auto& desc, bool blacklist) LAMBDA_FORCEINLINE { for (u32 index = 0; index < desc.count; ++index) { @@ -433,12 +434,19 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { update_descriptor_queue.Acquire(); RescalingPushConstant rescaling; + RenderAreaPushConstant render_area; const VkSampler* samplers_it{samplers.data()}; const VideoCommon::ImageViewInOut* views_it{views.data()}; const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { buffer_cache.BindHostStageBuffers(stage); PushImageDescriptors(texture_cache, update_descriptor_queue, stage_infos[stage], rescaling, samplers_it, views_it); + const auto& info{stage_infos[0]}; + if (info.uses_render_area) { + render_area.uses_render_area = true; + render_area.words = {static_cast(regs.surface_clip.width), + static_cast(regs.surface_clip.height)}; + } }}; if constexpr (Spec::enabled_stages[0]) { prepare_stage(0); @@ -455,10 +463,11 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { if constexpr (Spec::enabled_stages[4]) { prepare_stage(4); } - ConfigureDraw(rescaling); + ConfigureDraw(rescaling, render_area); } -void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { +void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, + const RenderAreaPushConstant& render_area) { texture_cache.UpdateRenderTargets(false); scheduler.RequestRenderpass(texture_cache.GetFramebuffer()); @@ -474,7 +483,9 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)}; const void* const descriptor_data{update_descriptor_queue.UpdateData()}; scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(), - is_rescaling, update_rescaling](vk::CommandBuffer cmdbuf) { + is_rescaling, update_rescaling, + uses_render_area = render_area.uses_render_area, + render_area_data = render_area.words](vk::CommandBuffer cmdbuf) { if (bind_pipeline) { cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); } @@ -488,6 +499,11 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { RESCALING_LAYOUT_DOWN_FACTOR_OFFSET, sizeof(scale_down_factor), &scale_down_factor); } + if (uses_render_area) { + cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS, + RENDERAREA_LAYOUT_OFFSET, sizeof(render_area_data), + &render_area_data); + } if (!descriptor_set_layout) { return; } @@ -513,7 +529,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { static_vector vertex_binding_divisors; static_vector vertex_attributes; if (key.state.dynamic_vertex_input) { - for (size_t index = 0; index < key.state.attributes.size(); ++index) { + const size_t num_vertex_arrays = std::min( + key.state.attributes.size(), static_cast(device.GetMaxVertexInputBindings())); + for (size_t index = 0; index < num_vertex_arrays; ++index) { const u32 type = key.state.DynamicAttributeType(index); if (!stage_infos[0].loads.Generic(index) || type == 0) { continue; @@ -535,7 +553,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { }); } } else { - for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { + const size_t num_vertex_arrays = std::min( + Maxwell::NumVertexArrays, static_cast(device.GetMaxVertexInputBindings())); + for (size_t index = 0; index < num_vertex_arrays; ++index) { const bool instanced = key.state.binding_divisors[index] != 0; const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; @@ -564,6 +584,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { }); } } + ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes()); + VkPipelineVertexInputStateCreateInfo vertex_input_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pNext = nullptr, @@ -618,23 +640,33 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { }; std::array swizzles; std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle); - const VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{ + VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV, .pNext = nullptr, .flags = 0, .viewportCount = Maxwell::NumViewports, .pViewportSwizzles = swizzles.data(), }; - const VkPipelineViewportStateCreateInfo viewport_ci{ + VkPipelineViewportDepthClipControlCreateInfoEXT ndc_info{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT, + .pNext = nullptr, + .negativeOneToOne = key.state.ndc_minus_one_to_one.Value() != 0 ? VK_TRUE : VK_FALSE, + }; + VkPipelineViewportStateCreateInfo viewport_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, - .pNext = device.IsNvViewportSwizzleSupported() ? &swizzle_ci : nullptr, + .pNext = nullptr, .flags = 0, .viewportCount = Maxwell::NumViewports, .pViewports = nullptr, .scissorCount = Maxwell::NumViewports, .pScissors = nullptr, }; - + if (device.IsNvViewportSwizzleSupported()) { + swizzle_ci.pNext = std::exchange(viewport_ci.pNext, &swizzle_ci); + } + if (device.IsExtDepthClipControlSupported()) { + ndc_info.pNext = std::exchange(viewport_ci.pNext, &ndc_info); + } VkPipelineRasterizationStateCreateInfo rasterization_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pNext = nullptr, @@ -698,8 +730,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .sampleShadingEnable = VK_FALSE, .minSampleShading = 0.0f, .pSampleMask = nullptr, - .alphaToCoverageEnable = VK_FALSE, - .alphaToOneEnable = VK_FALSE, + .alphaToCoverageEnable = key.state.alpha_to_coverage_enabled != 0 ? VK_TRUE : VK_FALSE, + .alphaToOneEnable = key.state.alpha_to_one_enabled != 0 ? VK_TRUE : VK_FALSE, }; const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, @@ -814,7 +846,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { */ } VkPipelineCreateFlags flags{}; - if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { + if (device.IsKhrPipelineExecutablePropertiesEnabled()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } pipeline = device.GetLogical().CreateGraphicsPipeline({ diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index e8949a9..1ed2967 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -62,6 +62,7 @@ class Device; class PipelineStatistics; class RenderPassCache; class RescalingPushConstant; +class RenderAreaPushConstant; class Scheduler; class UpdateDescriptorQueue; @@ -69,15 +70,16 @@ class GraphicsPipeline { static constexpr size_t NUM_STAGES = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; public: - explicit GraphicsPipeline( - Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory, - Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, - VideoCore::ShaderNotify* shader_notify, const Device& device, - DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue, - Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, - RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key, - std::array stages, - const std::array& infos); + explicit GraphicsPipeline(Scheduler& scheduler, BufferCache& buffer_cache, + TextureCache& texture_cache, VideoCore::ShaderNotify* shader_notify, + const Device& device, DescriptorPool& descriptor_pool, + UpdateDescriptorQueue& update_descriptor_queue, + Common::ThreadWorker* worker_thread, + PipelineStatistics* pipeline_statistics, + RenderPassCache& render_pass_cache, + const GraphicsPipelineCacheKey& key, + std::array stages, + const std::array& infos); GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; GraphicsPipeline(GraphicsPipeline&&) noexcept = delete; @@ -109,19 +111,25 @@ public: return [](GraphicsPipeline* pl, bool is_indexed) { pl->ConfigureImpl(is_indexed); }; } + void SetEngine(Tegra::Engines::Maxwell3D* maxwell3d_, Tegra::MemoryManager* gpu_memory_) { + maxwell3d = maxwell3d_; + gpu_memory = gpu_memory_; + } + private: template void ConfigureImpl(bool is_indexed); - void ConfigureDraw(const RescalingPushConstant& rescaling); + void ConfigureDraw(const RescalingPushConstant& rescaling, + const RenderAreaPushConstant& render_are); void MakePipeline(VkRenderPass render_pass); void Validate(); const GraphicsPipelineCacheKey key; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::MemoryManager& gpu_memory; + Tegra::Engines::Maxwell3D* maxwell3d; + Tegra::MemoryManager* gpu_memory; const Device& device; TextureCache& texture_cache; BufferCache& buffer_cache; @@ -143,7 +151,7 @@ private: vk::DescriptorSetLayout descriptor_set_layout; DescriptorAllocator descriptor_allocator; vk::PipelineLayout pipeline_layout; - vk::DescriptorUpdateTemplateKHR descriptor_update_template; + vk::DescriptorUpdateTemplate descriptor_update_template; vk::Pipeline pipeline; std::condition_variable build_condvar; diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index 4e81d3d..8aa07ef 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -11,10 +11,10 @@ namespace Vulkan { MasterSemaphore::MasterSemaphore(const Device& device) { - static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR, + static constexpr VkSemaphoreTypeCreateInfo semaphore_type_ci{ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, .pNext = nullptr, - .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE_KHR, + .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE, .initialValue = 0, }; static constexpr VkSemaphoreCreateInfo semaphore_ci{ @@ -28,7 +28,7 @@ MasterSemaphore::MasterSemaphore(const Device& device) { return; } // Validation layers have a bug where they fail to track resource usage when using timeline - // semaphores and synchronizing with GetSemaphoreCounterValueKHR. To workaround this issue, have + // semaphores and synchronizing with GetSemaphoreCounterValue. To workaround this issue, have // a separate thread waiting for each timeline semaphore value. debug_thread = std::jthread([this](std::stop_token stop_token) { u64 counter = 0; diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h index 362ed57..689f02e 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h @@ -7,6 +7,7 @@ #include #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 9708dc4..e726242 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -46,6 +46,7 @@ MICROPROFILE_DECLARE(Vulkan_PipelineCache); namespace { using Shader::Backend::SPIRV::EmitSPIRV; using Shader::Maxwell::ConvertLegacyToGeneric; +using Shader::Maxwell::GenerateGeometryPassthrough; using Shader::Maxwell::MergeDualVertexPrograms; using Shader::Maxwell::TranslateProgram; using VideoCommon::ComputeEnvironment; @@ -53,38 +54,49 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 6; +constexpr u32 CACHE_VERSION = 8; template auto MakeSpan(Container& container) { return std::span(container.data(), container.size()); } +Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::OutputTopology::PointList; + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::OutputTopology::LineStrip; + default: + return Shader::OutputTopology::TriangleStrip; + } +} + Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) { switch (comparison) { - case Maxwell::ComparisonOp::Never: - case Maxwell::ComparisonOp::NeverOld: + case Maxwell::ComparisonOp::Never_D3D: + case Maxwell::ComparisonOp::Never_GL: return Shader::CompareFunction::Never; - case Maxwell::ComparisonOp::Less: - case Maxwell::ComparisonOp::LessOld: + case Maxwell::ComparisonOp::Less_D3D: + case Maxwell::ComparisonOp::Less_GL: return Shader::CompareFunction::Less; - case Maxwell::ComparisonOp::Equal: - case Maxwell::ComparisonOp::EqualOld: + case Maxwell::ComparisonOp::Equal_D3D: + case Maxwell::ComparisonOp::Equal_GL: return Shader::CompareFunction::Equal; - case Maxwell::ComparisonOp::LessEqual: - case Maxwell::ComparisonOp::LessEqualOld: + case Maxwell::ComparisonOp::LessEqual_D3D: + case Maxwell::ComparisonOp::LessEqual_GL: return Shader::CompareFunction::LessThanEqual; - case Maxwell::ComparisonOp::Greater: - case Maxwell::ComparisonOp::GreaterOld: + case Maxwell::ComparisonOp::Greater_D3D: + case Maxwell::ComparisonOp::Greater_GL: return Shader::CompareFunction::Greater; - case Maxwell::ComparisonOp::NotEqual: - case Maxwell::ComparisonOp::NotEqualOld: + case Maxwell::ComparisonOp::NotEqual_D3D: + case Maxwell::ComparisonOp::NotEqual_GL: return Shader::CompareFunction::NotEqual; - case Maxwell::ComparisonOp::GreaterEqual: - case Maxwell::ComparisonOp::GreaterEqualOld: + case Maxwell::ComparisonOp::GreaterEqual_D3D: + case Maxwell::ComparisonOp::GreaterEqual_GL: return Shader::CompareFunction::GreaterThanEqual; - case Maxwell::ComparisonOp::Always: - case Maxwell::ComparisonOp::AlwaysOld: + case Maxwell::ComparisonOp::Always_D3D: + case Maxwell::ComparisonOp::Always_GL: return Shader::CompareFunction::Always; } UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); @@ -96,15 +108,18 @@ Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribut return Shader::AttributeType::Disabled; } switch (attr.Type()) { - case Maxwell::VertexAttribute::Type::SignedNorm: - case Maxwell::VertexAttribute::Type::UnsignedNorm: - case Maxwell::VertexAttribute::Type::UnsignedScaled: - case Maxwell::VertexAttribute::Type::SignedScaled: + case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway: + ASSERT_MSG(false, "Invalid vertex attribute type!"); + return Shader::AttributeType::Disabled; + case Maxwell::VertexAttribute::Type::SNorm: + case Maxwell::VertexAttribute::Type::UNorm: + case Maxwell::VertexAttribute::Type::UScaled: + case Maxwell::VertexAttribute::Type::SScaled: case Maxwell::VertexAttribute::Type::Float: return Shader::AttributeType::Float; - case Maxwell::VertexAttribute::Type::SignedInt: + case Maxwell::VertexAttribute::Type::SInt: return Shader::AttributeType::SignedInt; - case Maxwell::VertexAttribute::Type::UnsignedInt: + case Maxwell::VertexAttribute::Type::UInt: return Shader::AttributeType::UnsignedInt; } return Shader::AttributeType::Float; @@ -131,6 +146,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program Shader::RuntimeInfo info; if (previous_program) { info.previous_stage_stores = previous_program->info.stores; + info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping; if (previous_program->is_geometry_passthrough) { info.previous_stage_stores.mask |= previous_program->info.passthrough.mask; } @@ -162,16 +178,15 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program } break; case Shader::Stage::TessellationEval: - // We have to flip tessellation clockwise for some reason... - info.tess_clockwise = key.state.tessellation_clockwise == 0; + info.tess_clockwise = key.state.tessellation_clockwise != 0; info.tess_primitive = [&key] { const u32 raw{key.state.tessellation_primitive.Value()}; - switch (static_cast(raw)) { - case Maxwell::TessellationPrimitive::Isolines: + switch (static_cast(raw)) { + case Maxwell::Tessellation::DomainType::Isolines: return Shader::TessPrimitive::Isolines; - case Maxwell::TessellationPrimitive::Triangles: + case Maxwell::Tessellation::DomainType::Triangles: return Shader::TessPrimitive::Triangles; - case Maxwell::TessellationPrimitive::Quads: + case Maxwell::Tessellation::DomainType::Quads: return Shader::TessPrimitive::Quads; } ASSERT(false); @@ -179,12 +194,12 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program }(); info.tess_spacing = [&] { const u32 raw{key.state.tessellation_spacing}; - switch (static_cast(raw)) { - case Maxwell::TessellationSpacing::Equal: + switch (static_cast(raw)) { + case Maxwell::Tessellation::Spacing::Integer: return Shader::TessSpacing::Equal; - case Maxwell::TessellationSpacing::FractionalOdd: + case Maxwell::Tessellation::Spacing::FractionalOdd: return Shader::TessSpacing::FractionalOdd; - case Maxwell::TessellationSpacing::FractionalEven: + case Maxwell::Tessellation::Spacing::FractionalEven: return Shader::TessSpacing::FractionalEven; } ASSERT(false); @@ -259,24 +274,22 @@ bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) c return std::memcmp(&rhs, this, Size()) == 0; } -PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_, const Device& device_, +PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, UpdateDescriptorQueue& update_descriptor_queue_, RenderPassCache& render_pass_cache_, BufferCache& buffer_cache_, TextureCache& texture_cache_, VideoCore::ShaderNotify& shader_notify_) - : VideoCommon::ShaderCache{rasterizer_, gpu_memory_, maxwell3d_, kepler_compute_}, - device{device_}, scheduler{scheduler_}, descriptor_pool{descriptor_pool_}, - update_descriptor_queue{update_descriptor_queue_}, render_pass_cache{render_pass_cache_}, - buffer_cache{buffer_cache_}, texture_cache{texture_cache_}, shader_notify{shader_notify_}, + : VideoCommon::ShaderCache{rasterizer_}, device{device_}, scheduler{scheduler_}, + descriptor_pool{descriptor_pool_}, update_descriptor_queue{update_descriptor_queue_}, + render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_}, + texture_cache{texture_cache_}, shader_notify{shader_notify_}, use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, - workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "yuzu:PipelineBuilder"), - serialization_thread(1, "yuzu:PipelineSerialization") { + workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), + serialization_thread(1, "VkPipelineSerialization") { const auto& float_control{device.FloatControlProperties()}; - const VkDriverIdKHR driver_id{device.GetDriverID()}; + const VkDriverId driver_id{device.GetDriverID()}; profile = Shader::Profile{ - .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, + .supported_spirv = device.SupportedSpirvVersion(), .unified_descriptor_binding = true, .support_descriptor_aliasing = true, .support_int8 = device.IsInt8Supported(), @@ -284,10 +297,10 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw .support_int64 = device.IsShaderInt64Supported(), .support_vertex_instance_id = false, .support_float_controls = true, - .support_separate_denorm_behavior = float_control.denormBehaviorIndependence == - VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR, + .support_separate_denorm_behavior = + float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL, .support_separate_rounding_mode = - float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR, + float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL, .support_fp16_denorm_preserve = float_control.shaderDenormPreserveFloat16 != VK_FALSE, .support_fp32_denorm_preserve = float_control.shaderDenormPreserveFloat32 != VK_FALSE, .support_fp16_denorm_flush = float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE, @@ -308,24 +321,36 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), .support_derivative_control = true, .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), + .support_native_ndc = device.IsExtDepthClipControlSupported(), .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), .lower_left_origin_mode = false, .need_declared_frag_colors = false, - .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR, + .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, .has_broken_unsigned_image_offsets = false, .has_broken_signed_operations = false, - .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR, + .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY, .ignore_nan_fp_comparisons = false, }; host_info = Shader::HostTranslateInfo{ .support_float16 = device.IsFloat16Supported(), .support_int64 = device.IsShaderInt64Supported(), - .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR || - driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, + .needs_demote_reorder = + driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, + .support_snorm_render_buffer = true, + .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), }; + + if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) { + LOG_WARNING(Render_Vulkan, "maxVertexInputAttributes is too low: {} < {}", + device.GetMaxVertexInputAttributes(), Maxwell::NumVertexAttributes); + } + if (device.GetMaxVertexInputBindings() < Maxwell::NumVertexArrays) { + LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}", + device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays); + } } PipelineCache::~PipelineCache() = default; @@ -337,7 +362,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { current_pipeline = nullptr; return nullptr; } - graphics_key.state.Refresh(maxwell3d, device.IsExtExtendedDynamicStateSupported(), + graphics_key.state.Refresh(*maxwell3d, device.IsExtExtendedDynamicStateSupported(), device.IsExtVertexInputDynamicStateSupported()); if (current_pipeline) { @@ -357,7 +382,7 @@ ComputePipeline* PipelineCache::CurrentComputePipeline() { if (!shader) { return nullptr; } - const auto& qmd{kepler_compute.launch_description}; + const auto& qmd{kepler_compute->launch_description}; const ComputePipelineCacheKey key{ .unique_hash = shader->unique_hash, .shared_memory_size = qmd.shared_alloc, @@ -393,7 +418,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading std::unique_ptr statistics; } state; - if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { + if (device.IsKhrPipelineExecutablePropertiesEnabled()) { state.statistics = std::make_unique(device); } const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { @@ -486,13 +511,14 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const } // If something is using depth, we can assume that games are not rendering anything which // will be used one time. - if (maxwell3d.regs.zeta_enable) { + if (maxwell3d->regs.zeta_enable) { return nullptr; } // If games are using a small index count, we can assume these are full screen quads. // Usually these shaders are only used once for building textures so we can assume they // can't be built async - if (maxwell3d.regs.index_array.count <= 6 || maxwell3d.regs.vertex_buffer.count <= 6) { + const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + if (draw_state.index_buffer.count <= 6 || draw_state.vertex_buffer.count <= 6) { return pipeline; } return nullptr; @@ -507,7 +533,19 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( std::array programs; const bool uses_vertex_a{key.unique_hashes[0] != 0}; const bool uses_vertex_b{key.unique_hashes[1] != 0}; + + // Layer passthrough generation for devices without VK_EXT_shader_viewport_index_layer + Shader::IR::Program* layer_source_program{}; + for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && is_emulated_stage) { + auto topology = MaxwellToOutputTopology(key.state.topology); + programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology); + continue; + } if (key.unique_hashes[index] == 0) { continue; } @@ -528,6 +566,10 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } + + if (programs[index].info.requires_layer_emulation) { + layer_source_program = &programs[index]; + } } std::array infos{}; std::array modules; @@ -536,7 +578,9 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( Shader::Backend::Bindings binding; for (size_t index = uses_vertex_a && uses_vertex_b ? 1 : 0; index < Maxwell::MaxShaderProgram; ++index) { - if (key.unique_hashes[index] == 0) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && !is_emulated_stage) { continue; } UNIMPLEMENTED_IF(index == 0); @@ -557,10 +601,10 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( previous_stage = &program; } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; - return std::make_unique( - maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device, - descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key, - std::move(modules), infos); + return std::make_unique(scheduler, buffer_cache, texture_cache, + &shader_notify, device, descriptor_pool, + update_descriptor_queue, thread_worker, statistics, + render_pass_cache, key, std::move(modules), infos); } catch (const Shader::Exception& exception) { LOG_ERROR(Render_Vulkan, "{}", exception.what()); @@ -592,9 +636,9 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline() { std::unique_ptr PipelineCache::CreateComputePipeline( const ComputePipelineCacheKey& key, const ShaderInfo* shader) { - const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()}; - const auto& qmd{kepler_compute.launch_description}; - ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start}; + const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()}; + const auto& qmd{kepler_compute->launch_description}; + ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start}; env.SetCachedSize(shader->size_bytes); main_pools.ReleaseContents(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 127957d..61f9e93 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -100,10 +100,8 @@ struct ShaderPools { class PipelineCache : public VideoCommon::ShaderCache { public: - explicit PipelineCache(RasterizerVulkan& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d, - Tegra::Engines::KeplerCompute& kepler_compute, - Tegra::MemoryManager& gpu_memory, const Device& device, - Scheduler& scheduler, DescriptorPool& descriptor_pool, + explicit PipelineCache(RasterizerVulkan& rasterizer, const Device& device, Scheduler& scheduler, + DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue, RenderPassCache& render_pass_cache, BufferCache& buffer_cache, TextureCache& texture_cache, VideoCore::ShaderNotify& shader_notify_); diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 2b859c6..929c8ec 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -59,16 +59,16 @@ void QueryPool::Reserve(std::pair query) { std::find_if(pools.begin(), pools.end(), [query_pool = query.first](vk::QueryPool& pool) { return query_pool == *pool; }); - ASSERT(it != std::end(pools)); - const std::ptrdiff_t pool_index = std::distance(std::begin(pools), it); - usage[pool_index * GROW_STEP + static_cast(query.second)] = false; + if (it != std::end(pools)) { + const std::ptrdiff_t pool_index = std::distance(std::begin(pools), it); + usage[pool_index * GROW_STEP + static_cast(query.second)] = false; + } } -QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, - const Device& device_, Scheduler& scheduler_) - : QueryCacheBase{rasterizer_, maxwell3d_, gpu_memory_}, device{device_}, scheduler{scheduler_}, +QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, + Scheduler& scheduler_) + : QueryCacheBase{rasterizer_}, device{device_}, scheduler{scheduler_}, query_pools{ QueryPool{device_, scheduler_, QueryType::SamplesPassed}, } {} @@ -98,7 +98,7 @@ HostCounter::HostCounter(QueryCache& cache_, std::shared_ptr depend query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} { const vk::Device* logical = &cache.GetDevice().GetLogical(); cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { - logical->ResetQueryPoolEXT(query.first, query.second, 1); + logical->ResetQueryPool(query.first, query.second, 1); cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT); }); } diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h index b0d86c4..26762ee 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.h +++ b/src/video_core/renderer_vulkan/vk_query_cache.h @@ -52,9 +52,8 @@ private: class QueryCache final : public VideoCommon::QueryCacheBase { public: - explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, - const Device& device_, Scheduler& scheduler_); + explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, + Scheduler& scheduler_); ~QueryCache(); std::pair AllocateQuery(VideoCore::QueryType type); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 7e40c2d..4b7126c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -11,6 +11,8 @@ #include "common/microprofile.h" #include "common/scope_exit.h" #include "common/settings.h" +#include "video_core/control/channel_state.h" +#include "video_core/engines/draw_manager.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_vulkan/blit_image.h" @@ -35,6 +37,7 @@ namespace Vulkan { using Maxwell = Tegra::Engines::Maxwell3D::Regs; +using MaxwellDrawState = Tegra::Engines::DrawManager::State; using VideoCommon::ImageViewId; using VideoCommon::ImageViewType; @@ -69,7 +72,7 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in const float width = conv(src.scale_x * 2.0f); float y = conv(src.translate_y - src.scale_y); float height = conv(src.scale_y * 2.0f); - bool y_negate = regs.screen_y_control.y_negate; + bool y_negate = regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft; if (!device.IsNvViewportSwizzleSupported()) { y_negate = y_negate != (src.swizzle.y == Maxwell::ViewportSwizzle::NegativeY); @@ -126,17 +129,16 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3 return scissor; } -DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_instanced, - bool is_indexed) { +DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, bool is_indexed) { DrawParams params{ - .base_instance = regs.vb_base_instance, - .num_instances = is_instanced ? num_instances : 1, - .base_vertex = is_indexed ? regs.vb_element_base : regs.vertex_buffer.first, - .num_vertices = is_indexed ? regs.index_array.count : regs.vertex_buffer.count, - .first_index = is_indexed ? regs.index_array.first : 0, + .base_instance = draw_state.base_instance, + .num_instances = num_instances, + .base_vertex = is_indexed ? draw_state.base_index : draw_state.vertex_buffer.first, + .num_vertices = is_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count, + .first_index = is_indexed ? draw_state.index_buffer.first : 0, .is_indexed = is_indexed, }; - if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) { + if (draw_state.topology == Maxwell::PrimitiveTopology::Quads) { // 6 triangle vertices per quad, base vertex is part of the index // See BindQuadArrayIndexBuffer for more details params.num_vertices = (params.num_vertices / 4) * 6; @@ -148,31 +150,25 @@ DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_instan } // Anonymous namespace RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, - Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, ScreenInfo& screen_info_, const Device& device_, MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, Scheduler& scheduler_) - : RasterizerAccelerated{cpu_memory_}, gpu{gpu_}, - gpu_memory{gpu_memory_}, maxwell3d{gpu.Maxwell3D()}, kepler_compute{gpu.KeplerCompute()}, - screen_info{screen_info_}, device{device_}, memory_allocator{memory_allocator_}, - state_tracker{state_tracker_}, scheduler{scheduler_}, + : RasterizerAccelerated{cpu_memory_}, gpu{gpu_}, screen_info{screen_info_}, device{device_}, + memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_}, staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), update_descriptor_queue(device, scheduler), blit_image(device, scheduler, state_tracker, descriptor_pool), - astc_decoder_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue, - memory_allocator), render_pass_cache(device), texture_cache_runtime{device, scheduler, memory_allocator, staging_pool, - blit_image, astc_decoder_pass, - render_pass_cache}, - texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), + blit_image, render_pass_cache, + descriptor_pool, update_descriptor_queue}, + texture_cache(texture_cache_runtime, *this), buffer_cache_runtime(device, memory_allocator, scheduler, staging_pool, update_descriptor_queue, descriptor_pool), - buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), - pipeline_cache(*this, maxwell3d, kepler_compute, gpu_memory, device, scheduler, - descriptor_pool, update_descriptor_queue, render_pass_cache, buffer_cache, - texture_cache, gpu.ShaderNotify()), - query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, accelerate_dma{buffer_cache}, + buffer_cache(*this, cpu_memory_, buffer_cache_runtime), + pipeline_cache(*this, device, scheduler, descriptor_pool, update_descriptor_queue, + render_pass_cache, buffer_cache, texture_cache, gpu.ShaderNotify()), + query_cache{*this, device, scheduler}, accelerate_dma{buffer_cache}, fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), wfi_event(device.GetLogical().CreateEvent()) { scheduler.SetQueryCache(query_cache); @@ -180,7 +176,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra RasterizerVulkan::~RasterizerVulkan() = default; -void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { +void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { MICROPROFILE_SCOPE(Vulkan_Drawing); SCOPE_EXIT({ gpu.TickWork(); }); @@ -193,15 +189,17 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { return; } std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + // update engine as channel may be different. + pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->Configure(is_indexed); BeginTransformFeedback(); UpdateDynamicStates(); - const auto& regs{maxwell3d.regs}; - const u32 num_instances{maxwell3d.mme_draw.instance_count}; - const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_instanced, is_indexed)}; + const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + const u32 num_instances{instance_count}; + const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)}; scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { if (draw_params.is_indexed) { cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, @@ -215,21 +213,18 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { EndTransformFeedback(); } -void RasterizerVulkan::Clear() { +void RasterizerVulkan::Clear(u32 layer_count) { MICROPROFILE_SCOPE(Vulkan_Clearing); - if (!maxwell3d.ShouldExecute()) { - return; - } FlushWork(); query_cache.UpdateCounters(); - auto& regs = maxwell3d.regs; - const bool use_color = regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || - regs.clear_buffers.A; - const bool use_depth = regs.clear_buffers.Z; - const bool use_stencil = regs.clear_buffers.S; + auto& regs = maxwell3d->regs; + const bool use_color = regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B || + regs.clear_surface.A; + const bool use_depth = regs.clear_surface.Z; + const bool use_stencil = regs.clear_surface.S; if (!use_color && !use_depth && !use_stencil) { return; } @@ -248,10 +243,17 @@ void RasterizerVulkan::Clear() { } UpdateViewportsState(regs); + VkRect2D default_scissor; + default_scissor.offset.x = 0; + default_scissor.offset.y = 0; + default_scissor.extent.width = std::numeric_limits::max(); + default_scissor.extent.height = std::numeric_limits::max(); + VkClearRect clear_rect{ - .rect = GetScissorState(regs, 0, up_scale, down_shift), - .baseArrayLayer = regs.clear_buffers.layer, - .layerCount = 1, + .rect = regs.clear_control.use_scissor ? GetScissorState(regs, 0, up_scale, down_shift) + : default_scissor, + .baseArrayLayer = regs.clear_surface.layer, + .layerCount = layer_count, }; if (clear_rect.rect.extent.width == 0 || clear_rect.rect.extent.height == 0) { return; @@ -261,7 +263,7 @@ void RasterizerVulkan::Clear() { .height = std::min(clear_rect.rect.extent.height, render_area.height), }; - const u32 color_attachment = regs.clear_buffers.RT; + const u32 color_attachment = regs.clear_surface.RT; if (use_color && framebuffer->HasAspectColorBit(color_attachment)) { VkClearValue clear_value; bool is_integer = false; @@ -283,7 +285,8 @@ void RasterizerVulkan::Clear() { break; } if (!is_integer) { - std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color)); + std::memcpy(clear_value.color.float32, regs.clear_color.data(), + regs.clear_color.size() * sizeof(f32)); } else if (!is_signed) { for (size_t i = 0; i < 4; i++) { clear_value.color.uint32[i] = static_cast( @@ -297,14 +300,19 @@ void RasterizerVulkan::Clear() { } } - scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) { - const VkClearAttachment attachment{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .colorAttachment = color_attachment, - .clearValue = clear_value, - }; - cmdbuf.ClearAttachments(attachment, clear_rect); - }); + if (regs.clear_surface.R && regs.clear_surface.G && regs.clear_surface.B && + regs.clear_surface.A) { + scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) { + const VkClearAttachment attachment{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .colorAttachment = color_attachment, + .clearValue = clear_value, + }; + cmdbuf.ClearAttachments(attachment, clear_rect); + }); + } else { + UNIMPLEMENTED_MSG("Unimplemented Clear only the specified channel"); + } } if (!use_depth && !use_stencil) { @@ -339,9 +347,9 @@ void RasterizerVulkan::DispatchCompute() { return; } std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex}; - pipeline->Configure(kepler_compute, gpu_memory, scheduler, buffer_cache, texture_cache); + pipeline->Configure(*kepler_compute, *gpu_memory, scheduler, buffer_cache, texture_cache); - const auto& qmd{kepler_compute.launch_description}; + const auto& qmd{kepler_compute->launch_description}; const std::array dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z}; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); @@ -422,7 +430,7 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { } } -void RasterizerVulkan::SyncGuestHost() { +void RasterizerVulkan::InvalidateGPUCache() { pipeline_cache.SyncGuestHost(); { std::scoped_lock lock{buffer_cache.mutex}; @@ -442,40 +450,30 @@ void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) { pipeline_cache.OnCPUWrite(addr, size); } -void RasterizerVulkan::ModifyGPUMemory(GPUVAddr addr, u64 size) { +void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { { std::scoped_lock lock{texture_cache.mutex}; - texture_cache.UnmapGPUMemory(addr, size); + texture_cache.UnmapGPUMemory(as_id, addr, size); } } -void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) { - if (!gpu.IsAsync()) { - gpu_memory.Write(addr, value); - return; - } - fence_manager.SignalSemaphore(addr, value); +void RasterizerVulkan::SignalFence(std::function&& func) { + fence_manager.SignalFence(std::move(func)); +} + +void RasterizerVulkan::SyncOperation(std::function&& func) { + fence_manager.SyncOperation(std::move(func)); } void RasterizerVulkan::SignalSyncPoint(u32 value) { - if (!gpu.IsAsync()) { - gpu.IncrementSyncPoint(value); - return; - } fence_manager.SignalSyncPoint(value); } void RasterizerVulkan::SignalReference() { - if (!gpu.IsAsync()) { - return; - } fence_manager.SignalOrdering(); } void RasterizerVulkan::ReleaseFences() { - if (!gpu.IsAsync()) { - return; - } fence_manager.WaitPendingFences(); } @@ -543,8 +541,7 @@ bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) { std::scoped_lock lock{texture_cache.mutex}; - texture_cache.BlitImage(dst, src, copy_config); - return true; + return texture_cache.BlitImage(dst, src, copy_config); } Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA() { @@ -552,13 +549,13 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA() } void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, - std::span memory) { - auto cpu_addr = gpu_memory.GpuToCpuAddress(address); + std::span memory) { + auto cpu_addr = gpu_memory->GpuToCpuAddress(address); if (!cpu_addr) [[unlikely]] { - gpu_memory.WriteBlock(address, memory.data(), copy_size); + gpu_memory->WriteBlock(address, memory.data(), copy_size); return; } - gpu_memory.WriteBlockUnsafe(address, memory.data(), copy_size); + gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); { std::unique_lock lock{buffer_cache.mutex}; if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { @@ -583,6 +580,7 @@ bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config, if (!image_view) { return false; } + screen_info.image = image_view->ImageHandle(); screen_info.image_view = image_view->Handle(Shader::TextureType::Color2D); screen_info.width = image_view->size.width; screen_info.height = image_view->size.height; @@ -627,7 +625,7 @@ bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 } void RasterizerVulkan::UpdateDynamicStates() { - auto& regs = maxwell3d.regs; + auto& regs = maxwell3d->regs; UpdateViewportsState(regs); UpdateScissorsState(regs); UpdateDepthBias(regs); @@ -651,24 +649,23 @@ void RasterizerVulkan::UpdateDynamicStates() { } void RasterizerVulkan::BeginTransformFeedback() { - const auto& regs = maxwell3d.regs; - if (regs.tfb_enabled == 0) { + const auto& regs = maxwell3d->regs; + if (regs.transform_feedback_enabled == 0) { return; } if (!device.IsExtTransformFeedbackSupported()) { LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); return; } - UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || - regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || - regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); + UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) || + regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation)); scheduler.Record( [](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); }); } void RasterizerVulkan::EndTransformFeedback() { - const auto& regs = maxwell3d.regs; - if (regs.tfb_enabled == 0) { + const auto& regs = maxwell3d->regs; + if (regs.transform_feedback_enabled == 0) { return; } if (!device.IsExtTransformFeedbackSupported()) { @@ -682,6 +679,22 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg if (!state_tracker.TouchViewports()) { return; } + if (!regs.viewport_scale_offset_enabled) { + const auto x = static_cast(regs.surface_clip.x); + const auto y = static_cast(regs.surface_clip.y); + const auto width = static_cast(regs.surface_clip.width); + const auto height = static_cast(regs.surface_clip.height); + VkViewport viewport{ + .x = x, + .y = y, + .width = width != 0.0f ? width : 1.0f, + .height = height != 0.0f ? height : 1.0f, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewport); }); + return; + } const bool is_rescaling{texture_cache.IsRescaling()}; const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; const std::array viewports{ @@ -732,11 +745,11 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchDepthBias()) { return; } - float units = regs.polygon_offset_units / 2.0f; - const bool is_d24 = regs.zeta.format == Tegra::DepthFormat::S8_UINT_Z24_UNORM || - regs.zeta.format == Tegra::DepthFormat::D24X8_UNORM || - regs.zeta.format == Tegra::DepthFormat::D24S8_UNORM || - regs.zeta.format == Tegra::DepthFormat::D24C8_UNORM; + float units = regs.depth_bias / 2.0f; + const bool is_d24 = regs.zeta.format == Tegra::DepthFormat::Z24_UNORM_S8_UINT || + regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM || + regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM || + regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM; if (is_d24 && !device.SupportsD24DepthBuffer()) { // the base formulas can be obtained from here: // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias @@ -744,8 +757,8 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) { static_cast(1ULL << (32 - 24)) / (static_cast(0x1.ep+127)); units = static_cast(static_cast(units) * rescale_factor); } - scheduler.Record([constant = units, clamp = regs.polygon_offset_clamp, - factor = regs.polygon_offset_factor](vk::CommandBuffer cmdbuf) { + scheduler.Record([constant = units, clamp = regs.depth_bias_clamp, + factor = regs.slope_scale_depth_bias](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBias(constant, clamp, factor); }); } @@ -775,8 +788,8 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) if (regs.stencil_two_side_enable) { // Separate values per face scheduler.Record( - [front_ref = regs.stencil_front_func_ref, front_write_mask = regs.stencil_front_mask, - front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_func_ref, + [front_ref = regs.stencil_front_ref, front_write_mask = regs.stencil_front_mask, + front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_ref, back_write_mask = regs.stencil_back_mask, back_test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) { // Front face @@ -791,7 +804,7 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) }); } else { // Front face defines both faces - scheduler.Record([ref = regs.stencil_front_func_ref, write_mask = regs.stencil_front_mask, + scheduler.Record([ref = regs.stencil_front_ref, write_mask = regs.stencil_front_mask, test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) { cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref); cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask); @@ -804,7 +817,8 @@ void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchLineWidth()) { return; } - const float width = regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased; + const float width = + regs.line_anti_alias_enable ? regs.line_width_smooth : regs.line_width_aliased; scheduler.Record([width](vk::CommandBuffer cmdbuf) { cmdbuf.SetLineWidth(width); }); } @@ -812,10 +826,10 @@ void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchCullMode()) { return; } - scheduler.Record( - [enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) { - cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE); - }); + scheduler.Record([enabled = regs.gl_cull_test_enabled, + cull_face = regs.gl_cull_face](vk::CommandBuffer cmdbuf) { + cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE); + }); } void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { @@ -864,8 +878,8 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) { return; } - VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face); - if (regs.screen_y_control.triangle_rast_flip != 0) { + VkFrontFace front_face = MaxwellToVK::FrontFace(regs.gl_front_face); + if (regs.window_origin.flip_y != 0) { front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE; } @@ -877,16 +891,16 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchStencilOp()) { return; } - const Maxwell::StencilOp fail = regs.stencil_front_op_fail; - const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail; - const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass; - const Maxwell::ComparisonOp compare = regs.stencil_front_func_func; + const Maxwell::StencilOp::Op fail = regs.stencil_front_op.fail; + const Maxwell::StencilOp::Op zfail = regs.stencil_front_op.zfail; + const Maxwell::StencilOp::Op zpass = regs.stencil_front_op.zpass; + const Maxwell::ComparisonOp compare = regs.stencil_front_op.func; if (regs.stencil_two_side_enable) { // Separate stencil op per face - const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail; - const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail; - const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass; - const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func; + const Maxwell::StencilOp::Op back_fail = regs.stencil_back_op.fail; + const Maxwell::StencilOp::Op back_zfail = regs.stencil_back_op.zfail; + const Maxwell::StencilOp::Op back_zpass = regs.stencil_back_op.zpass; + const Maxwell::ComparisonOp back_compare = regs.stencil_back_op.func; scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass, back_compare](vk::CommandBuffer cmdbuf) { cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail), @@ -917,7 +931,7 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& } void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { - auto& dirty{maxwell3d.dirty.flags}; + auto& dirty{maxwell3d->dirty.flags}; if (!dirty[Dirty::VertexInput]) { return; } @@ -958,15 +972,15 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) dirty[Dirty::VertexBinding0 + index] = false; const u32 binding{static_cast(index)}; - const auto& input_binding{regs.vertex_array[binding]}; - const bool is_instanced{regs.instanced_arrays.IsInstancingEnabled(binding)}; + const auto& input_binding{regs.vertex_streams[binding]}; + const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)}; bindings.push_back({ .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT, .pNext = nullptr, .binding = binding, .stride = input_binding.stride, .inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX, - .divisor = is_instanced ? input_binding.divisor : 1, + .divisor = is_instanced ? input_binding.frequency : 1, }); } scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) { @@ -974,4 +988,41 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) }); } +void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) { + CreateChannel(channel); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.CreateChannel(channel); + buffer_cache.CreateChannel(channel); + } + pipeline_cache.CreateChannel(channel); + query_cache.CreateChannel(channel); + state_tracker.SetupTables(channel); +} + +void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) { + const s32 channel_id = channel.bind_id; + BindToChannel(channel_id); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.BindToChannel(channel_id); + buffer_cache.BindToChannel(channel_id); + } + pipeline_cache.BindToChannel(channel_id); + query_cache.BindToChannel(channel_id); + state_tracker.ChangeChannel(channel); + state_tracker.InvalidateState(); +} + +void RasterizerVulkan::ReleaseChannel(s32 channel_id) { + EraseChannel(channel_id); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + texture_cache.EraseChannel(channel_id); + buffer_cache.EraseChannel(channel_id); + } + pipeline_cache.EraseChannel(channel_id); + query_cache.EraseChannel(channel_id); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 0370ea3..ee483cf 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -8,6 +8,7 @@ #include #include "common/common_types.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/engines/maxwell_dma.h" #include "video_core/rasterizer_accelerated.h" #include "video_core/rasterizer_interface.h" @@ -54,17 +55,17 @@ private: BufferCache& buffer_cache; }; -class RasterizerVulkan final : public VideoCore::RasterizerAccelerated { +class RasterizerVulkan final : public VideoCore::RasterizerAccelerated, + protected VideoCommon::ChannelSetupCaches { public: explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, - Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, - ScreenInfo& screen_info_, const Device& device_, - MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, - Scheduler& scheduler_); + Core::Memory::Memory& cpu_memory_, ScreenInfo& screen_info_, + const Device& device_, MemoryAllocator& memory_allocator_, + StateTracker& state_tracker_, Scheduler& scheduler_); ~RasterizerVulkan() override; - void Draw(bool is_indexed, bool is_instanced) override; - void Clear() override; + void Draw(bool is_indexed, u32 instance_count) override; + void Clear(u32 layer_count) override; void DispatchCompute() override; void ResetCounter(VideoCore::QueryType type) override; void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional timestamp) override; @@ -75,10 +76,11 @@ public: bool MustFlushRegion(VAddr addr, u64 size) override; void InvalidateRegion(VAddr addr, u64 size) override; void OnCPUWrite(VAddr addr, u64 size) override; - void SyncGuestHost() override; + void InvalidateGPUCache() override; void UnmapMemory(VAddr addr, u64 size) override; - void ModifyGPUMemory(GPUVAddr addr, u64 size) override; - void SignalSemaphore(GPUVAddr addr, u32 value) override; + void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; + void SignalFence(std::function&& func) override; + void SyncOperation(std::function&& func) override; void SignalSyncPoint(u32 value) override; void SignalReference() override; void ReleaseFences() override; @@ -93,12 +95,18 @@ public: const Tegra::Engines::Fermi2D::Config& copy_config) override; Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, - std::span memory) override; + std::span memory) override; bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) override; void LoadDiskResources(u64 title_id, std::stop_token stop_loading, const VideoCore::DiskResourceLoadCallback& callback) override; + void InitializeChannel(Tegra::Control::ChannelState& channel) override; + + void BindChannel(Tegra::Control::ChannelState& channel) override; + + void ReleaseChannel(s32 channel_id) override; + private: static constexpr size_t MAX_TEXTURES = 192; static constexpr size_t MAX_IMAGES = 48; @@ -134,9 +142,6 @@ private: void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); Tegra::GPU& gpu; - Tegra::MemoryManager& gpu_memory; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; ScreenInfo& screen_info; const Device& device; @@ -148,7 +153,6 @@ private: DescriptorPool descriptor_pool; UpdateDescriptorQueue update_descriptor_queue; BlitImageHelper blit_image; - ASTCDecoderPass astc_decoder_pass; RenderPassCache render_pass_cache; TextureCacheRuntime texture_cache_runtime; diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.h b/src/video_core/renderer_vulkan/vk_render_pass_cache.h index dc21b7e..91ad4bf 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h @@ -12,7 +12,7 @@ namespace Vulkan { struct RenderPassKey { - auto operator<=>(const RenderPassKey&) const noexcept = default; + bool operator==(const RenderPassKey&) const noexcept = default; std::array color_formats; VideoCore::Surface::PixelFormat depth_format; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index a331ff3..c2e53a5 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -136,23 +136,25 @@ bool Scheduler::UpdateRescaling(bool is_rescaling) { } void Scheduler::WorkerThread(std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:VulkanWorker"); + Common::SetCurrentThreadName("VulkanWorker"); do { std::unique_ptr work; + bool has_submit{false}; { std::unique_lock lock{work_mutex}; if (work_queue.empty()) { wait_cv.notify_all(); } - work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); }); + Common::CondvarWait(work_cv, lock, stop_token, [&] { return !work_queue.empty(); }); if (stop_token.stop_requested()) { continue; } work = std::move(work_queue.front()); work_queue.pop(); + + has_submit = work->HasSubmit(); + work->ExecuteAll(current_cmdbuf); } - const bool has_submit = work->HasSubmit(); - work->ExecuteAll(current_cmdbuf); if (has_submit) { AllocateWorkerCommandBuffer(); } @@ -192,8 +194,8 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, }; - const VkTimelineSemaphoreSubmitInfoKHR timeline_si{ - .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR, + const VkTimelineSemaphoreSubmitInfo timeline_si{ + .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, .pNext = nullptr, .waitSemaphoreValueCount = num_wait_semaphores, .pWaitSemaphoreValues = wait_values.data(), @@ -219,6 +221,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s [[fallthrough]]; default: vk::Check(result); + break; } }); chunk->MarkSubmit(); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index c04aad0..3858c50 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -12,6 +12,7 @@ #include "common/alignment.h" #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "video_core/renderer_vulkan/vk_master_semaphore.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -144,7 +145,6 @@ private: using FuncType = TypedCommand; static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); - recorded_counts++; command_offset = Common::AlignUp(command_offset, alignof(FuncType)); if (command_offset > sizeof(data) - sizeof(FuncType)) { return false; @@ -166,7 +166,7 @@ private: } bool Empty() const { - return recorded_counts == 0; + return command_offset == 0; } bool HasSubmit() const { @@ -177,7 +177,6 @@ private: Command* first = nullptr; Command* last = nullptr; - size_t recorded_counts = 0; size_t command_offset = 0; bool submit = false; alignas(std::max_align_t) std::array data{}; diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/vk_smaa.cpp new file mode 100644 index 0000000..8eb7354 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_smaa.cpp @@ -0,0 +1,761 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "common/assert.h" +#include "common/polyfill_ranges.h" + +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/renderer_vulkan/vk_smaa.h" +#include "video_core/smaa_area_tex.h" +#include "video_core/smaa_search_tex.h" +#include "video_core/vulkan_common/vulkan_device.h" + +#include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h" +#include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h" +#include "video_core/host_shaders/smaa_edge_detection_frag_spv.h" +#include "video_core/host_shaders/smaa_edge_detection_vert_spv.h" +#include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h" +#include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h" + +namespace Vulkan { +namespace { + +#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) + +std::pair CreateWrappedImage(const Device& device, + MemoryAllocator& allocator, + VkExtent2D dimensions, VkFormat format) { + const VkImageCreateInfo image_ci{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + .format = format, + .extent = {.width = dimensions.width, .height = dimensions.height, .depth = 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + + auto image = device.GetLogical().CreateImage(image_ci); + auto commit = allocator.Commit(image, Vulkan::MemoryUsage::DeviceLocal); + + return std::make_pair(std::move(image), std::move(commit)); +} + +void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, + VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) { + constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = flags, + .dstAccessMask = flags, + .oldLayout = source_layout, + .newLayout = target_layout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + 0, barrier); +} + +void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, + vk::Image& image, VkExtent2D dimensions, VkFormat format, + std::span initial_contents = {}) { + auto upload_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = initial_contents.size_bytes(), + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }); + auto upload_commit = allocator.Commit(upload_buffer, MemoryUsage::Upload); + std::ranges::copy(initial_contents, upload_commit.Map().begin()); + + const std::array regions{{{ + .bufferOffset = 0, + .bufferRowLength = dimensions.width, + .bufferImageHeight = dimensions.height, + .imageSubresource{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1}, + .imageOffset{}, + .imageExtent{.width = dimensions.width, .height = dimensions.height, .depth = 1}, + }}}; + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + TransitionImageLayout(cmdbuf, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_UNDEFINED); + cmdbuf.CopyBufferToImage(*upload_buffer, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + regions); + TransitionImageLayout(cmdbuf, *image, VK_IMAGE_LAYOUT_GENERAL, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + }); + scheduler.Finish(); + + // This should go out of scope before the commit + auto upload_buffer2 = std::move(upload_buffer); +} + +vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { + return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .image = *image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format, + .components{}, + .subresourceRange{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1}, + }); +} + +vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format) { + const VkAttachmentDescription attachment{ + .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, + .format = format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, + .initialLayout = VK_IMAGE_LAYOUT_GENERAL, + .finalLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + + constexpr VkAttachmentReference color_attachment_ref{ + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_GENERAL, + }; + + const VkSubpassDescription subpass_description{ + .flags = 0, + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .inputAttachmentCount = 0, + .pInputAttachments = nullptr, + .colorAttachmentCount = 1, + .pColorAttachments = &color_attachment_ref, + .pResolveAttachments = nullptr, + .pDepthStencilAttachment = nullptr, + .preserveAttachmentCount = 0, + .pPreserveAttachments = nullptr, + }; + + constexpr VkSubpassDependency dependency{ + .srcSubpass = VK_SUBPASS_EXTERNAL, + .dstSubpass = 0, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dependencyFlags = 0, + }; + + return device.GetLogical().CreateRenderPass(VkRenderPassCreateInfo{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .attachmentCount = 1, + .pAttachments = &attachment, + .subpassCount = 1, + .pSubpasses = &subpass_description, + .dependencyCount = 1, + .pDependencies = &dependency, + }); +} + +vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& render_pass, + vk::ImageView& dest_image, VkExtent2D extent) { + return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .renderPass = *render_pass, + .attachmentCount = 1, + .pAttachments = dest_image.address(), + .width = extent.width, + .height = extent.height, + .layers = 1, + }); +} + +vk::Sampler CreateWrappedSampler(const Device& device) { + return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .mipLodBias = 0.0f, + .anisotropyEnable = VK_FALSE, + .maxAnisotropy = 0.0f, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_NEVER, + .minLod = 0.0f, + .maxLod = 0.0f, + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE, + }); +} + +vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span code) { + return device.GetLogical().CreateShaderModule(VkShaderModuleCreateInfo{ + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .codeSize = code.size_bytes(), + .pCode = code.data(), + }); +} + +vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, u32 max_descriptors, + u32 max_sets) { + const VkDescriptorPoolSize pool_size{ + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = static_cast(max_descriptors), + }; + + return device.GetLogical().CreateDescriptorPool(VkDescriptorPoolCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .maxSets = max_sets, + .poolSizeCount = 1, + .pPoolSizes = &pool_size, + }); +} + +vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout(const Device& device, + u32 max_sampler_bindings) { + std::vector bindings(max_sampler_bindings); + for (u32 i = 0; i < max_sampler_bindings; i++) { + bindings[i] = { + .binding = i, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = nullptr, + }; + } + + return device.GetLogical().CreateDescriptorSetLayout(VkDescriptorSetLayoutCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .bindingCount = static_cast(bindings.size()), + .pBindings = bindings.data(), + }); +} + +vk::DescriptorSets CreateWrappedDescriptorSets(vk::DescriptorPool& pool, + vk::Span layouts) { + return pool.Allocate(VkDescriptorSetAllocateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pNext = nullptr, + .descriptorPool = *pool, + .descriptorSetCount = layouts.size(), + .pSetLayouts = layouts.data(), + }); +} + +vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, + vk::DescriptorSetLayout& layout) { + return device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .setLayoutCount = 1, + .pSetLayouts = layout.address(), + .pushConstantRangeCount = 0, + .pPushConstantRanges = nullptr, + }); +} + +vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, + vk::PipelineLayout& layout, + std::tuple shaders) { + const std::array shader_stages{{ + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = *std::get<0>(shaders), + .pName = "main", + .pSpecializationInfo = nullptr, + }, + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = *std::get<1>(shaders), + .pName = "main", + .pSpecializationInfo = nullptr, + }, + }}; + + constexpr VkPipelineVertexInputStateCreateInfo vertex_input_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .vertexBindingDescriptionCount = 0, + .pVertexBindingDescriptions = nullptr, + .vertexAttributeDescriptionCount = 0, + .pVertexAttributeDescriptions = nullptr, + }; + + constexpr VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + .primitiveRestartEnable = VK_FALSE, + }; + + constexpr VkPipelineViewportStateCreateInfo viewport_state_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .viewportCount = 1, + .pViewports = nullptr, + .scissorCount = 1, + .pScissors = nullptr, + }; + + constexpr VkPipelineRasterizationStateCreateInfo rasterization_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = VK_POLYGON_MODE_FILL, + .cullMode = VK_CULL_MODE_NONE, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + .depthBiasEnable = VK_FALSE, + .depthBiasConstantFactor = 0.0f, + .depthBiasClamp = 0.0f, + .depthBiasSlopeFactor = 0.0f, + .lineWidth = 1.0f, + }; + + constexpr VkPipelineMultisampleStateCreateInfo multisampling_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + .sampleShadingEnable = VK_FALSE, + .minSampleShading = 0.0f, + .pSampleMask = nullptr, + .alphaToCoverageEnable = VK_FALSE, + .alphaToOneEnable = VK_FALSE, + }; + + constexpr VkPipelineColorBlendAttachmentState color_blend_attachment{ + .blendEnable = VK_FALSE, + .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, + .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + }; + + const VkPipelineColorBlendStateCreateInfo color_blend_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_COPY, + .attachmentCount = 1, + .pAttachments = &color_blend_attachment, + .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, + }; + + constexpr std::array dynamic_states{ + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + + const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .dynamicStateCount = static_cast(dynamic_states.size()), + .pDynamicStates = dynamic_states.data(), + }; + + return device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stageCount = static_cast(shader_stages.size()), + .pStages = shader_stages.data(), + .pVertexInputState = &vertex_input_ci, + .pInputAssemblyState = &input_assembly_ci, + .pTessellationState = nullptr, + .pViewportState = &viewport_state_ci, + .pRasterizationState = &rasterization_ci, + .pMultisampleState = &multisampling_ci, + .pDepthStencilState = nullptr, + .pColorBlendState = &color_blend_ci, + .pDynamicState = &dynamic_state_ci, + .layout = *layout, + .renderPass = *renderpass, + .subpass = 0, + .basePipelineHandle = 0, + .basePipelineIndex = 0, + }); +} + +VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector& images, + VkSampler sampler, VkImageView view, + VkDescriptorSet set, u32 binding) { + ASSERT(images.capacity() > images.size()); + auto& image_info = images.emplace_back(VkDescriptorImageInfo{ + .sampler = sampler, + .imageView = view, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }); + + return VkWriteDescriptorSet{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = set, + .dstBinding = binding, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &image_info, + .pBufferInfo = nullptr, + .pTexelBufferView = nullptr, + }; +} + +void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { + constexpr std::array subresources{{{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }}}; + TransitionImageLayout(cmdbuf, image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_UNDEFINED); + cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources); +} + +void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass, + VkFramebuffer framebuffer, VkExtent2D extent) { + const VkRenderPassBeginInfo renderpass_bi{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = nullptr, + .renderPass = *render_pass, + .framebuffer = framebuffer, + .renderArea{ + .offset{}, + .extent = extent, + }, + .clearValueCount = 0, + .pClearValues = nullptr, + }; + cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); + + const VkViewport viewport{ + .x = 0.0f, + .y = 0.0f, + .width = static_cast(extent.width), + .height = static_cast(extent.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + const VkRect2D scissor{ + .offset = {0, 0}, + .extent = extent, + }; + cmdbuf.SetViewport(0, viewport); + cmdbuf.SetScissor(0, scissor); +} + +} // Anonymous namespace + +SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) + : m_device(device), m_allocator(allocator), m_extent(extent), + m_image_count(static_cast(image_count)) { + CreateImages(); + CreateRenderPasses(); + CreateSampler(); + CreateShaders(); + CreateDescriptorPool(); + CreateDescriptorSetLayouts(); + CreateDescriptorSets(); + CreatePipelineLayouts(); + CreatePipelines(); +} + +void SMAA::CreateImages() { + constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; + constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; + + std::tie(m_static_images[Area], m_static_buffer_commits[Area]) = + CreateWrappedImage(m_device, m_allocator, area_extent, VK_FORMAT_R8G8_UNORM); + std::tie(m_static_images[Search], m_static_buffer_commits[Search]) = + CreateWrappedImage(m_device, m_allocator, search_extent, VK_FORMAT_R8_UNORM); + + m_static_image_views[Area] = + CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM); + m_static_image_views[Search] = + CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM); + + for (u32 i = 0; i < m_image_count; i++) { + Images& images = m_dynamic_images.emplace_back(); + + std::tie(images.images[Blend], images.buffer_commits[Blend]) = + CreateWrappedImage(m_device, m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + std::tie(images.images[Edges], images.buffer_commits[Edges]) = + CreateWrappedImage(m_device, m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT); + std::tie(images.images[Output], images.buffer_commits[Output]) = + CreateWrappedImage(m_device, m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + + images.image_views[Blend] = + CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Edges] = + CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT); + images.image_views[Output] = + CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT); + } +} + +void SMAA::CreateRenderPasses() { + m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT); + m_renderpasses[BlendingWeightCalculation] = + CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + m_renderpasses[NeighborhoodBlending] = + CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + + for (auto& images : m_dynamic_images) { + images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer( + m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent); + + images.framebuffers[BlendingWeightCalculation] = + CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation], + images.image_views[Blend], m_extent); + + images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer( + m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent); + } +} + +void SMAA::CreateSampler() { + m_sampler = CreateWrappedSampler(m_device); +} + +void SMAA::CreateShaders() { + // These match the order of the SMAAStage enum + constexpr std::array vert_shader_sources{ + ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV), + ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV), + ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV), + }; + constexpr std::array frag_shader_sources{ + ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV), + ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV), + ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV), + }; + + for (size_t i = 0; i < MaxSMAAStage; i++) { + m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]); + m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]); + } +} + +void SMAA::CreateDescriptorPool() { + // Edge detection: 1 descriptor + // Blending weight calculation: 3 descriptors + // Neighborhood blending: 2 descriptors + + // 6 descriptors, 3 descriptor sets per image + m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count); +} + +void SMAA::CreateDescriptorSetLayouts() { + m_descriptor_set_layouts[EdgeDetection] = CreateWrappedDescriptorSetLayout(m_device, 1); + m_descriptor_set_layouts[BlendingWeightCalculation] = + CreateWrappedDescriptorSetLayout(m_device, 3); + m_descriptor_set_layouts[NeighborhoodBlending] = CreateWrappedDescriptorSetLayout(m_device, 2); +} + +void SMAA::CreateDescriptorSets() { + std::vector layouts(m_descriptor_set_layouts.size()); + std::ranges::transform(m_descriptor_set_layouts, layouts.begin(), + [](auto& layout) { return *layout; }); + + for (auto& images : m_dynamic_images) { + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); + } +} + +void SMAA::CreatePipelineLayouts() { + for (size_t i = 0; i < MaxSMAAStage; i++) { + m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]); + } +} + +void SMAA::CreatePipelines() { + for (size_t i = 0; i < MaxSMAAStage; i++) { + m_pipelines[i] = + CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i], + std::tie(m_vertex_shaders[i], m_fragment_shaders[i])); + } +} + +void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { + Images& images = m_dynamic_images[image_index]; + std::vector image_infos; + std::vector updates; + image_infos.reserve(6); + + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, + images.descriptor_sets[EdgeDetection], 0)); + + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges], + images.descriptor_sets[BlendingWeightCalculation], + 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area], + images.descriptor_sets[BlendingWeightCalculation], + 1)); + updates.push_back( + CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search], + images.descriptor_sets[BlendingWeightCalculation], 2)); + + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, + images.descriptor_sets[NeighborhoodBlending], 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend], + images.descriptor_sets[NeighborhoodBlending], 1)); + + m_device.GetLogical().UpdateDescriptorSets(updates, {}); +} + +void SMAA::UploadImages(Scheduler& scheduler) { + if (m_images_ready) { + return; + } + + constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; + constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; + + UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent, + VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes)); + UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, + VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); + + scheduler.Record([&](vk::CommandBuffer& cmdbuf) { + for (auto& images : m_dynamic_images) { + for (size_t i = 0; i < MaxDynamicImage; i++) { + ClearColorImage(cmdbuf, *images.images[i]); + } + } + }); + scheduler.Finish(); + + m_images_ready = true; +} + +VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, + VkImageView source_image_view) { + Images& images = m_dynamic_images[image_index]; + + VkImage output_image = *images.images[Output]; + VkImage edges_image = *images.images[Edges]; + VkImage blend_image = *images.images[Blend]; + + VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection]; + VkDescriptorSet blending_weight_calculation_descriptor_set = + images.descriptor_sets[BlendingWeightCalculation]; + VkDescriptorSet neighborhood_blending_descriptor_set = + images.descriptor_sets[NeighborhoodBlending]; + + VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection]; + VkFramebuffer blending_weight_calculation_framebuffer = + *images.framebuffers[BlendingWeightCalculation]; + VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending]; + + UploadImages(scheduler); + UpdateDescriptorSets(source_image_view, image_index); + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([=, this](vk::CommandBuffer& cmdbuf) { + TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer, + m_extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipeline_layouts[EdgeDetection], 0, + edge_detection_descriptor_set, {}); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + + TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, m_renderpasses[BlendingWeightCalculation], + blending_weight_calculation_framebuffer, m_extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipelines[BlendingWeightCalculation]); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipeline_layouts[BlendingWeightCalculation], 0, + blending_weight_calculation_descriptor_set, {}); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + + TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, m_renderpasses[NeighborhoodBlending], + neighborhood_blending_framebuffer, m_extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipeline_layouts[NeighborhoodBlending], 0, + neighborhood_blending_descriptor_set, {}); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + }); + + return *images.image_views[Output]; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_smaa.h b/src/video_core/renderer_vulkan/vk_smaa.h new file mode 100644 index 0000000..99a3691 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_smaa.h @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "video_core/vulkan_common/vulkan_memory_allocator.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +class Device; +class Scheduler; +class StagingBufferPool; + +class SMAA { +public: + explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, + VkExtent2D extent); + VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, + VkImageView source_image_view); + +private: + enum SMAAStage { + EdgeDetection = 0, + BlendingWeightCalculation = 1, + NeighborhoodBlending = 2, + MaxSMAAStage = 3, + }; + + enum StaticImageType { + Area = 0, + Search = 1, + MaxStaticImage = 2, + }; + + enum DynamicImageType { + Blend = 0, + Edges = 1, + Output = 2, + MaxDynamicImage = 3, + }; + + void CreateImages(); + void CreateRenderPasses(); + void CreateSampler(); + void CreateShaders(); + void CreateDescriptorPool(); + void CreateDescriptorSetLayouts(); + void CreateDescriptorSets(); + void CreatePipelineLayouts(); + void CreatePipelines(); + void UpdateDescriptorSets(VkImageView image_view, size_t image_index); + void UploadImages(Scheduler& scheduler); + + const Device& m_device; + MemoryAllocator& m_allocator; + const VkExtent2D m_extent; + const u32 m_image_count; + + vk::DescriptorPool m_descriptor_pool{}; + std::array m_descriptor_set_layouts{}; + std::array m_pipeline_layouts{}; + std::array m_vertex_shaders{}; + std::array m_fragment_shaders{}; + std::array m_pipelines{}; + std::array m_renderpasses{}; + + std::array m_static_buffer_commits; + std::array m_static_images{}; + std::array m_static_image_views{}; + + struct Images { + vk::DescriptorSets descriptor_sets{}; + std::array buffer_commits; + std::array images{}; + std::array image_views{}; + std::array framebuffers{}; + }; + std::vector m_dynamic_images{}; + bool m_images_ready{}; + + vk::Sampler m_sampler{}; +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 9ad0964..edb41b1 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -7,9 +7,9 @@ #include "common/common_types.h" #include "core/core.h" +#include "video_core/control/channel_state.h" #include "video_core/dirty_flags.h" #include "video_core/engines/maxwell_3d.h" -#include "video_core/gpu.h" #include "video_core/renderer_vulkan/vk_state_tracker.h" #define OFF(field_name) MAXWELL3D_REG_INDEX(field_name) @@ -51,8 +51,8 @@ Flags MakeInvalidationFlags() { void SetupDirtyViewports(Tables& tables) { FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); - tables[0][OFF(viewport_transform_enabled)] = Viewports; - tables[1][OFF(screen_y_control)] = Viewports; + tables[0][OFF(viewport_scale_offset_enabled)] = Viewports; + tables[1][OFF(window_origin)] = Viewports; } void SetupDirtyScissors(Tables& tables) { @@ -61,9 +61,9 @@ void SetupDirtyScissors(Tables& tables) { void SetupDirtyDepthBias(Tables& tables) { auto& table = tables[0]; - table[OFF(polygon_offset_units)] = DepthBias; - table[OFF(polygon_offset_clamp)] = DepthBias; - table[OFF(polygon_offset_factor)] = DepthBias; + table[OFF(depth_bias)] = DepthBias; + table[OFF(depth_bias_clamp)] = DepthBias; + table[OFF(slope_scale_depth_bias)] = DepthBias; } void SetupDirtyBlendConstants(Tables& tables) { @@ -77,10 +77,10 @@ void SetupDirtyDepthBounds(Tables& tables) { void SetupDirtyStencilProperties(Tables& tables) { auto& table = tables[0]; table[OFF(stencil_two_side_enable)] = StencilProperties; - table[OFF(stencil_front_func_ref)] = StencilProperties; + table[OFF(stencil_front_ref)] = StencilProperties; table[OFF(stencil_front_mask)] = StencilProperties; table[OFF(stencil_front_func_mask)] = StencilProperties; - table[OFF(stencil_back_func_ref)] = StencilProperties; + table[OFF(stencil_back_ref)] = StencilProperties; table[OFF(stencil_back_mask)] = StencilProperties; table[OFF(stencil_back_func_mask)] = StencilProperties; } @@ -92,8 +92,8 @@ void SetupDirtyLineWidth(Tables& tables) { void SetupDirtyCullMode(Tables& tables) { auto& table = tables[0]; - table[OFF(cull_face)] = CullMode; - table[OFF(cull_test_enabled)] = CullMode; + table[OFF(gl_cull_face)] = CullMode; + table[OFF(gl_cull_test_enabled)] = CullMode; } void SetupDirtyDepthBoundsEnable(Tables& tables) { @@ -114,20 +114,20 @@ void SetupDirtyDepthCompareOp(Tables& tables) { void SetupDirtyFrontFace(Tables& tables) { auto& table = tables[0]; - table[OFF(front_face)] = FrontFace; - table[OFF(screen_y_control)] = FrontFace; + table[OFF(gl_front_face)] = FrontFace; + table[OFF(window_origin)] = FrontFace; } void SetupDirtyStencilOp(Tables& tables) { auto& table = tables[0]; - table[OFF(stencil_front_op_fail)] = StencilOp; - table[OFF(stencil_front_op_zfail)] = StencilOp; - table[OFF(stencil_front_op_zpass)] = StencilOp; - table[OFF(stencil_front_func_func)] = StencilOp; - table[OFF(stencil_back_op_fail)] = StencilOp; - table[OFF(stencil_back_op_zfail)] = StencilOp; - table[OFF(stencil_back_op_zpass)] = StencilOp; - table[OFF(stencil_back_func_func)] = StencilOp; + table[OFF(stencil_front_op.fail)] = StencilOp; + table[OFF(stencil_front_op.zfail)] = StencilOp; + table[OFF(stencil_front_op.zpass)] = StencilOp; + table[OFF(stencil_front_op.func)] = StencilOp; + table[OFF(stencil_back_op.fail)] = StencilOp; + table[OFF(stencil_back_op.zfail)] = StencilOp; + table[OFF(stencil_back_op.zpass)] = StencilOp; + table[OFF(stencil_back_op.func)] = StencilOp; // Table 0 is used by StencilProperties tables[1][OFF(stencil_two_side_enable)] = StencilOp; @@ -139,10 +139,10 @@ void SetupDirtyStencilTestEnable(Tables& tables) { void SetupDirtyBlending(Tables& tables) { tables[0][OFF(color_mask_common)] = Blending; - tables[0][OFF(independent_blend_enable)] = Blending; + tables[0][OFF(blend_per_target_enabled)] = Blending; FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending); FillBlock(tables[0], OFF(blend), NUM(blend), Blending); - FillBlock(tables[0], OFF(independent_blend), NUM(independent_blend), Blending); + FillBlock(tables[0], OFF(blend_per_target), NUM(blend_per_target), Blending); } void SetupDirtyViewportSwizzles(Tables& tables) { @@ -166,17 +166,16 @@ void SetupDirtyVertexBindings(Tables& tables) { static constexpr size_t divisor_offset = 3; for (size_t i = 0; i < Regs::NumVertexArrays; ++i) { const u8 flag = static_cast(VertexBinding0 + i); - tables[0][OFF(instanced_arrays) + i] = VertexInput; - tables[1][OFF(instanced_arrays) + i] = flag; - tables[0][OFF(vertex_array) + i * NUM(vertex_array[0]) + divisor_offset] = VertexInput; - tables[1][OFF(vertex_array) + i * NUM(vertex_array[0]) + divisor_offset] = flag; + tables[0][OFF(vertex_stream_instances) + i] = VertexInput; + tables[1][OFF(vertex_stream_instances) + i] = flag; + tables[0][OFF(vertex_streams) + i * NUM(vertex_streams[0]) + divisor_offset] = VertexInput; + tables[1][OFF(vertex_streams) + i * NUM(vertex_streams[0]) + divisor_offset] = flag; } } } // Anonymous namespace -StateTracker::StateTracker(Tegra::GPU& gpu) - : flags{gpu.Maxwell3D().dirty.flags}, invalidation_flags{MakeInvalidationFlags()} { - auto& tables{gpu.Maxwell3D().dirty.tables}; +void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { + auto& tables{channel_state.maxwell_3d->dirty.tables}; SetupDirtyFlags(tables); SetupDirtyViewports(tables); SetupDirtyScissors(tables); @@ -199,4 +198,15 @@ StateTracker::StateTracker(Tegra::GPU& gpu) SetupDirtyVertexBindings(tables); } +void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { + flags = &channel_state.maxwell_3d->dirty.flags; +} + +void StateTracker::InvalidateState() { + flags->set(); +} + +StateTracker::StateTracker() + : flags{&default_flags}, default_flags{}, invalidation_flags{MakeInvalidationFlags()} {} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index a85bc1c..2296dea 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -10,6 +10,12 @@ #include "video_core/dirty_flags.h" #include "video_core/engines/maxwell_3d.h" +namespace Tegra { +namespace Control { +struct ChannelState; +} +} // namespace Tegra + namespace Vulkan { namespace Dirty { @@ -53,19 +59,19 @@ class StateTracker { using Maxwell = Tegra::Engines::Maxwell3D::Regs; public: - explicit StateTracker(Tegra::GPU& gpu); + explicit StateTracker(); void InvalidateCommandBufferState() { - flags |= invalidation_flags; + (*flags) |= invalidation_flags; current_topology = INVALID_TOPOLOGY; } void InvalidateViewports() { - flags[Dirty::Viewports] = true; + (*flags)[Dirty::Viewports] = true; } void InvalidateScissors() { - flags[Dirty::Scissors] = true; + (*flags)[Dirty::Scissors] = true; } bool TouchViewports() { @@ -139,16 +145,23 @@ public: return has_changed; } + void SetupTables(Tegra::Control::ChannelState& channel_state); + + void ChangeChannel(Tegra::Control::ChannelState& channel_state); + + void InvalidateState(); + private: static constexpr auto INVALID_TOPOLOGY = static_cast(~0u); bool Exchange(std::size_t id, bool new_value) const noexcept { - const bool is_dirty = flags[id]; - flags[id] = new_value; + const bool is_dirty = (*flags)[id]; + (*flags)[id] = new_value; return is_dirty; } - Tegra::Engines::Maxwell3D::DirtyState::Flags& flags; + Tegra::Engines::Maxwell3D::DirtyState::Flags* flags; + Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags; Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY; }; diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index a69ae77..b6810ee 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -7,6 +7,7 @@ #include #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "core/core.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -36,7 +37,8 @@ VkPresentModeKHR ChooseSwapPresentMode(vk::Span modes) { // Mailbox (triple buffering) doesn't lock the application like fifo (vsync), // prefer it if vsync option is not selected const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR); - if (found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) { + if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Borderless && + found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) { return VK_PRESENT_MODE_MAILBOX_KHR; } if (!Settings::values.use_speed_limit.GetValue()) { @@ -65,17 +67,19 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi } // Anonymous namespace -Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, u32 width, - u32 height, bool srgb) +Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, + u32 width_, u32 height_, bool srgb) : surface{surface_}, device{device_}, scheduler{scheduler_} { - Create(width, height, srgb); + Create(width_, height_, srgb); } Swapchain::~Swapchain() = default; -void Swapchain::Create(u32 width, u32 height, bool srgb) { +void Swapchain::Create(u32 width_, u32 height_, bool srgb) { is_outdated = false; is_suboptimal = false; + width = width_; + height = height_; const auto physical_device = device.GetPhysical(); const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)}; @@ -86,7 +90,7 @@ void Swapchain::Create(u32 width, u32 height, bool srgb) { device.GetLogical().WaitIdle(); Destroy(); - CreateSwapchain(capabilities, width, height, srgb); + CreateSwapchain(capabilities, srgb); CreateSemaphores(); CreateImageViews(); @@ -146,8 +150,7 @@ void Swapchain::Present(VkSemaphore render_semaphore) { } } -void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, - bool srgb) { +void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) { const auto physical_device{device.GetPhysical()}; const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; @@ -156,8 +159,16 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u3 present_mode = ChooseSwapPresentMode(present_modes); u32 requested_image_count{capabilities.minImageCount + 1}; - if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) { - requested_image_count = capabilities.maxImageCount; + // Ensure Tripple buffering if possible. + if (capabilities.maxImageCount > 0) { + if (requested_image_count > capabilities.maxImageCount) { + requested_image_count = capabilities.maxImageCount; + } else { + requested_image_count = + std::max(requested_image_count, std::min(3U, capabilities.maxImageCount)); + } + } else { + requested_image_count = std::max(requested_image_count, 3U); } VkSwapchainCreateInfoKHR swapchain_ci{ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 111b390..caf1ff3 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -80,9 +80,16 @@ public: return *present_semaphores[frame_index]; } + u32 GetWidth() const { + return width; + } + + u32 GetHeight() const { + return height; + } + private: - void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, - bool srgb); + void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb); void CreateSemaphores(); void CreateImageViews(); @@ -105,6 +112,9 @@ private: std::vector resource_ticks; std::vector present_semaphores; + u32 width; + u32 height; + u32 image_index{}; u32 frame_index{}; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index caca79d..a65bbeb 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -108,6 +108,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { break; default: ASSERT_MSG(false, "Invalid surface type"); + break; } } if (info.storage) { @@ -592,7 +593,7 @@ void TryTransformSwizzleIfNeeded(PixelFormat format, std::array(*runtime, view_ptr, nullptr, extent); + blit_framebuffer = + std::make_unique(*runtime, view_ptr, nullptr, extent, scale_up); } const auto color_view = blit_view->Handle(Shader::TextureType::Color2D); @@ -1488,7 +1495,8 @@ bool Image::BlitScaleHelper(bool scale_up) { src_region, operation, BLIT_OPERATION); } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { if (!blit_framebuffer) { - blit_framebuffer = std::make_unique(*runtime, nullptr, view_ptr, extent); + blit_framebuffer = + std::make_unique(*runtime, nullptr, view_ptr, extent, scale_up); } runtime->blit_image_helper.BlitDepthStencil(blit_framebuffer.get(), blit_view->DepthView(), blit_view->StencilView(), dst_region, @@ -1756,34 +1764,42 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span color_buffers{color_buffer}; - CreateFramebuffer(runtime, color_buffers, depth_buffer); + CreateFramebuffer(runtime, color_buffers, depth_buffer, is_rescaled); } Framebuffer::~Framebuffer() = default; void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, std::span color_buffers, - ImageView* depth_buffer) { + ImageView* depth_buffer, bool is_rescaled) { std::vector attachments; RenderPassKey renderpass_key{}; s32 num_layers = 1; + const auto& resolution = runtime.resolution; + + u32 width = std::numeric_limits::max(); + u32 height = std::numeric_limits::max(); for (size_t index = 0; index < NUM_RT; ++index) { const ImageView* const color_buffer = color_buffers[index]; if (!color_buffer) { renderpass_key.color_formats[index] = PixelFormat::Invalid; continue; } + width = std::min(width, is_rescaled ? resolution.ScaleUp(color_buffer->size.width) + : color_buffer->size.width); + height = std::min(height, is_rescaled ? resolution.ScaleUp(color_buffer->size.height) + : color_buffer->size.height); attachments.push_back(color_buffer->RenderTarget()); renderpass_key.color_formats[index] = color_buffer->format; num_layers = std::max(num_layers, color_buffer->range.extent.layers); @@ -1794,6 +1810,10 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, } const size_t num_colors = attachments.size(); if (depth_buffer) { + width = std::min(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width) + : depth_buffer->size.width); + height = std::min(height, is_rescaled ? resolution.ScaleUp(depth_buffer->size.height) + : depth_buffer->size.height); attachments.push_back(depth_buffer->RenderTarget()); renderpass_key.depth_format = depth_buffer->format; num_layers = std::max(num_layers, depth_buffer->range.extent.layers); @@ -1810,6 +1830,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, renderpass_key.samples = samples; renderpass = runtime.render_pass_cache.Get(renderpass_key); + render_area.width = std::min(render_area.width, width); + render_area.height = std::min(render_area.height, height); num_color_buffers = static_cast(num_colors); framebuffer = runtime.device.GetLogical().CreateFramebuffer({ @@ -1829,7 +1851,7 @@ void TextureCacheRuntime::AccelerateImageUpload( Image& image, const StagingBufferRef& map, std::span swizzles) { if (IsPixelFormatASTC(image.info.format)) { - return astc_decoder_pass.Assemble(image, map, swizzles); + return astc_decoder_pass->Assemble(image, map, swizzles); } ASSERT(false); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 69f06ee..7ec0df1 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -6,6 +6,7 @@ #include #include "shader_recompiler/shader_info.h" +#include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" #include "video_core/texture_cache/image_view_base.h" #include "video_core/texture_cache/texture_cache_base.h" @@ -25,14 +26,15 @@ using VideoCommon::RenderTargets; using VideoCommon::SlotVector; using VideoCore::Surface::PixelFormat; -class ASTCDecoderPass; class BlitImageHelper; +class DescriptorPool; class Device; class Image; class ImageView; class Framebuffer; class RenderPassCache; class StagingBufferPool; +class UpdateDescriptorQueue; class Scheduler; class TextureCacheRuntime { @@ -41,8 +43,9 @@ public: MemoryAllocator& memory_allocator_, StagingBufferPool& staging_buffer_pool_, BlitImageHelper& blit_image_helper_, - ASTCDecoderPass& astc_decoder_pass_, - RenderPassCache& render_pass_cache_); + RenderPassCache& render_pass_cache_, + DescriptorPool& descriptor_pool, + UpdateDescriptorQueue& update_descriptor_queue); void Finish(); @@ -97,8 +100,8 @@ public: MemoryAllocator& memory_allocator; StagingBufferPool& staging_buffer_pool; BlitImageHelper& blit_image_helper; - ASTCDecoderPass& astc_decoder_pass; RenderPassCache& render_pass_cache; + std::optional astc_decoder_pass; const Settings::ResolutionScalingInfo& resolution; constexpr static size_t indexing_slots = 8 * sizeof(size_t); @@ -268,7 +271,7 @@ public: ImageView* depth_buffer, const VideoCommon::RenderTargets& key); explicit Framebuffer(TextureCacheRuntime& runtime, ImageView* color_buffer, - ImageView* depth_buffer, VkExtent2D extent); + ImageView* depth_buffer, VkExtent2D extent, bool is_rescaled); ~Framebuffer(); @@ -279,7 +282,8 @@ public: Framebuffer& operator=(Framebuffer&&) = default; void CreateFramebuffer(TextureCacheRuntime& runtime, - std::span color_buffers, ImageView* depth_buffer); + std::span color_buffers, ImageView* depth_buffer, + bool is_rescaled = false); [[nodiscard]] VkFramebuffer Handle() const noexcept { return *framebuffer; diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index 164e4ee..d948237 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp @@ -8,6 +8,7 @@ #include "common/assert.h" #include "shader_recompiler/frontend/maxwell/control_flow.h" #include "shader_recompiler/object_pool.h" +#include "video_core/control/channel_state.h" #include "video_core/dirty_flags.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" @@ -33,29 +34,25 @@ void ShaderCache::SyncGuestHost() { RemovePendingShaders(); } -ShaderCache::ShaderCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::MemoryManager& gpu_memory_, Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_) - : gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_}, kepler_compute{kepler_compute_}, - rasterizer{rasterizer_} {} +ShaderCache::ShaderCache(VideoCore::RasterizerInterface& rasterizer_) : rasterizer{rasterizer_} {} bool ShaderCache::RefreshStages(std::array& unique_hashes) { - auto& dirty{maxwell3d.dirty.flags}; + auto& dirty{maxwell3d->dirty.flags}; if (!dirty[VideoCommon::Dirty::Shaders]) { return last_shaders_valid; } dirty[VideoCommon::Dirty::Shaders] = false; - const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()}; + const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()}; for (size_t index = 0; index < Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; ++index) { - if (!maxwell3d.regs.IsShaderConfigEnabled(index)) { + if (!maxwell3d->regs.IsShaderConfigEnabled(index)) { unique_hashes[index] = 0; continue; } - const auto& shader_config{maxwell3d.regs.shader_config[index]}; - const auto program{static_cast(index)}; + const auto& shader_config{maxwell3d->regs.pipelines[index]}; + const auto program{static_cast(index)}; const GPUVAddr shader_addr{base_addr + shader_config.offset}; - const std::optional cpu_shader_addr{gpu_memory.GpuToCpuAddress(shader_addr)}; + const std::optional cpu_shader_addr{gpu_memory->GpuToCpuAddress(shader_addr)}; if (!cpu_shader_addr) { LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr); last_shaders_valid = false; @@ -64,7 +61,7 @@ bool ShaderCache::RefreshStages(std::array& unique_hashes) { const ShaderInfo* shader_info{TryGet(*cpu_shader_addr)}; if (!shader_info) { const u32 start_address{shader_config.offset}; - GraphicsEnvironment env{maxwell3d, gpu_memory, program, base_addr, start_address}; + GraphicsEnvironment env{*maxwell3d, *gpu_memory, program, base_addr, start_address}; shader_info = MakeShaderInfo(env, *cpu_shader_addr); } shader_infos[index] = shader_info; @@ -75,10 +72,10 @@ bool ShaderCache::RefreshStages(std::array& unique_hashes) { } const ShaderInfo* ShaderCache::ComputeShader() { - const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()}; - const auto& qmd{kepler_compute.launch_description}; + const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()}; + const auto& qmd{kepler_compute->launch_description}; const GPUVAddr shader_addr{program_base + qmd.program_start}; - const std::optional cpu_shader_addr{gpu_memory.GpuToCpuAddress(shader_addr)}; + const std::optional cpu_shader_addr{gpu_memory->GpuToCpuAddress(shader_addr)}; if (!cpu_shader_addr) { LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr); return nullptr; @@ -86,22 +83,22 @@ const ShaderInfo* ShaderCache::ComputeShader() { if (const ShaderInfo* const shader = TryGet(*cpu_shader_addr)) { return shader; } - ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start}; + ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start}; return MakeShaderInfo(env, *cpu_shader_addr); } void ShaderCache::GetGraphicsEnvironments(GraphicsEnvironments& result, const std::array& unique_hashes) { size_t env_index{}; - const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()}; + const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()}; for (size_t index = 0; index < NUM_PROGRAMS; ++index) { if (unique_hashes[index] == 0) { continue; } - const auto program{static_cast(index)}; + const auto program{static_cast(index)}; auto& env{result.envs[index]}; - const u32 start_address{maxwell3d.regs.shader_config[index].offset}; - env = GraphicsEnvironment{maxwell3d, gpu_memory, program, base_addr, start_address}; + const u32 start_address{maxwell3d->regs.pipelines[index].offset}; + env = GraphicsEnvironment{*maxwell3d, *gpu_memory, program, base_addr, start_address}; env.SetCachedSize(shader_infos[index]->size_bytes); result.env_ptrs[env_index++] = &env; } diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index f67cea8..f3cc4c7 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h @@ -12,6 +12,8 @@ #include #include "common/common_types.h" +#include "common/polyfill_ranges.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/rasterizer_interface.h" #include "video_core/shader_environment.h" @@ -19,6 +21,10 @@ namespace Tegra { class MemoryManager; } +namespace Tegra::Control { +struct ChannelState; +} + namespace VideoCommon { class GenericEnvironment; @@ -28,7 +34,7 @@ struct ShaderInfo { size_t size_bytes{}; }; -class ShaderCache { +class ShaderCache : public VideoCommon::ChannelSetupCaches { static constexpr u64 YUZU_PAGEBITS = 14; static constexpr u64 YUZU_PAGESIZE = u64(1) << YUZU_PAGEBITS; @@ -71,9 +77,7 @@ protected: } }; - explicit ShaderCache(VideoCore::RasterizerInterface& rasterizer_, - Tegra::MemoryManager& gpu_memory_, Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_); + explicit ShaderCache(VideoCore::RasterizerInterface& rasterizer_); /// @brief Update the hashes and information of shader stages /// @param unique_hashes Shader hashes to store into when a stage is enabled @@ -88,10 +92,6 @@ protected: void GetGraphicsEnvironments(GraphicsEnvironments& result, const std::array& unique_hashes); - Tegra::MemoryManager& gpu_memory; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; - std::array shader_infos{}; bool last_shaders_valid = false; diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 5f76259..9588107 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -15,10 +15,12 @@ #include "common/fs/fs.h" #include "common/fs/path_util.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "shader_recompiler/environment.h" #include "video_core/engines/kepler_compute.h" #include "video_core/memory_manager.h" #include "video_core/shader_environment.h" +#include "video_core/texture_cache/format_lookup_table.h" #include "video_core/textures/texture.h" namespace VideoCommon { @@ -33,7 +35,7 @@ static u64 MakeCbufKey(u32 index, u32 offset) { return (static_cast(index) << 32) | offset; } -static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) { +static Shader::TextureType ConvertTextureType(const Tegra::Texture::TICEntry& entry) { switch (entry.texture_type) { case Tegra::Texture::TextureType::Texture1D: return Shader::TextureType::Color1D; @@ -59,6 +61,26 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) { } } +static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture::TICEntry& entry) { + switch (PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type, + entry.a_type, entry.srgb_conversion)) { + case VideoCore::Surface::PixelFormat::A8B8G8R8_SNORM: + return Shader::TexturePixelFormat::A8B8G8R8_SNORM; + case VideoCore::Surface::PixelFormat::R8_SNORM: + return Shader::TexturePixelFormat::R8_SNORM; + case VideoCore::Surface::PixelFormat::R8G8_SNORM: + return Shader::TexturePixelFormat::R8G8_SNORM; + case VideoCore::Surface::PixelFormat::R16G16B16A16_SNORM: + return Shader::TexturePixelFormat::R16G16B16A16_SNORM; + case VideoCore::Surface::PixelFormat::R16G16_SNORM: + return Shader::TexturePixelFormat::R16G16_SNORM; + case VideoCore::Surface::PixelFormat::R16_SNORM: + return Shader::TexturePixelFormat::R16_SNORM; + default: + return Shader::TexturePixelFormat::OTHER; + } +} + static std::string_view StageToPrefix(Shader::Stage stage) { switch (stage) { case Shader::Stage::VertexB: @@ -178,22 +200,31 @@ void GenericEnvironment::Dump(u64 hash) { void GenericEnvironment::Serialize(std::ofstream& file) const { const u64 code_size{static_cast(CachedSize())}; const u64 num_texture_types{static_cast(texture_types.size())}; + const u64 num_texture_pixel_formats{static_cast(texture_pixel_formats.size())}; const u64 num_cbuf_values{static_cast(cbuf_values.size())}; file.write(reinterpret_cast(&code_size), sizeof(code_size)) .write(reinterpret_cast(&num_texture_types), sizeof(num_texture_types)) + .write(reinterpret_cast(&num_texture_pixel_formats), + sizeof(num_texture_pixel_formats)) .write(reinterpret_cast(&num_cbuf_values), sizeof(num_cbuf_values)) .write(reinterpret_cast(&local_memory_size), sizeof(local_memory_size)) .write(reinterpret_cast(&texture_bound), sizeof(texture_bound)) .write(reinterpret_cast(&start_address), sizeof(start_address)) .write(reinterpret_cast(&cached_lowest), sizeof(cached_lowest)) .write(reinterpret_cast(&cached_highest), sizeof(cached_highest)) + .write(reinterpret_cast(&viewport_transform_state), + sizeof(viewport_transform_state)) .write(reinterpret_cast(&stage), sizeof(stage)) .write(reinterpret_cast(code.data()), code_size); for (const auto& [key, type] : texture_types) { file.write(reinterpret_cast(&key), sizeof(key)) .write(reinterpret_cast(&type), sizeof(type)); } + for (const auto& [key, format] : texture_pixel_formats) { + file.write(reinterpret_cast(&key), sizeof(key)) + .write(reinterpret_cast(&format), sizeof(format)); + } for (const auto& [key, type] : cbuf_values) { file.write(reinterpret_cast(&key), sizeof(key)) .write(reinterpret_cast(&type), sizeof(type)); @@ -237,47 +268,45 @@ std::optional GenericEnvironment::TryFindSize() { return std::nullopt; } -Shader::TextureType GenericEnvironment::ReadTextureTypeImpl(GPUVAddr tic_addr, u32 tic_limit, - bool via_header_index, u32 raw) { +Tegra::Texture::TICEntry GenericEnvironment::ReadTextureInfo(GPUVAddr tic_addr, u32 tic_limit, + bool via_header_index, u32 raw) { const auto handle{Tegra::Texture::TexturePair(raw, via_header_index)}; const GPUVAddr descriptor_addr{tic_addr + handle.first * sizeof(Tegra::Texture::TICEntry)}; Tegra::Texture::TICEntry entry; gpu_memory->ReadBlock(descriptor_addr, &entry, sizeof(entry)); - const Shader::TextureType result{ConvertType(entry)}; - texture_types.emplace(raw, result); - return result; + return entry; } GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, - Maxwell::ShaderProgram program, GPUVAddr program_base_, + Maxwell::ShaderType program, GPUVAddr program_base_, u32 start_address_) : GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} { gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph)); initial_offset = sizeof(sph); - gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask; + gp_passthrough_mask = maxwell3d->regs.post_vtg_shader_attrib_skip_mask; switch (program) { - case Maxwell::ShaderProgram::VertexA: + case Maxwell::ShaderType::VertexA: stage = Shader::Stage::VertexA; stage_index = 0; break; - case Maxwell::ShaderProgram::VertexB: + case Maxwell::ShaderType::VertexB: stage = Shader::Stage::VertexB; stage_index = 0; break; - case Maxwell::ShaderProgram::TesselationControl: + case Maxwell::ShaderType::TessellationInit: stage = Shader::Stage::TessellationControl; stage_index = 1; break; - case Maxwell::ShaderProgram::TesselationEval: + case Maxwell::ShaderType::Tessellation: stage = Shader::Stage::TessellationEval; stage_index = 2; break; - case Maxwell::ShaderProgram::Geometry: + case Maxwell::ShaderType::Geometry: stage = Shader::Stage::Geometry; stage_index = 3; break; - case Maxwell::ShaderProgram::Fragment: + case Maxwell::ShaderType::Pixel: stage = Shader::Stage::Fragment; stage_index = 4; break; @@ -288,7 +317,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, const u64 local_size{sph.LocalMemorySize()}; ASSERT(local_size <= std::numeric_limits::max()); local_memory_size = static_cast(local_size) + sph.common3.shader_local_memory_crs_size; - texture_bound = maxwell3d->regs.tex_cb_index; + texture_bound = maxwell3d->regs.bindless_texture_const_buffer_slot; } u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { @@ -304,8 +333,28 @@ u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { const auto& regs{maxwell3d->regs}; - const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex}; - return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, via_header_index, handle); + const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; + auto entry = + ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle); + const Shader::TextureType result{ConvertTextureType(entry)}; + texture_types.emplace(handle, result); + return result; +} + +Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handle) { + const auto& regs{maxwell3d->regs}; + const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; + auto entry = + ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle); + const Shader::TexturePixelFormat result(ConvertTexturePixelFormat(entry)); + texture_pixel_formats.emplace(handle, result); + return result; +} + +u32 GraphicsEnvironment::ReadViewportTransformState() { + const auto& regs{maxwell3d->regs}; + viewport_transform_state = regs.viewport_scale_offset_enabled; + return viewport_transform_state; } ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_, @@ -336,21 +385,41 @@ u32 ComputeEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) { const auto& regs{kepler_compute->regs}; const auto& qmd{kepler_compute->launch_description}; - return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); + auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); + const Shader::TextureType result{ConvertTextureType(entry)}; + texture_types.emplace(handle, result); + return result; +} + +Shader::TexturePixelFormat ComputeEnvironment::ReadTexturePixelFormat(u32 handle) { + const auto& regs{kepler_compute->regs}; + const auto& qmd{kepler_compute->launch_description}; + auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); + const Shader::TexturePixelFormat result(ConvertTexturePixelFormat(entry)); + texture_pixel_formats.emplace(handle, result); + return result; +} + +u32 ComputeEnvironment::ReadViewportTransformState() { + return viewport_transform_state; } void FileEnvironment::Deserialize(std::ifstream& file) { u64 code_size{}; u64 num_texture_types{}; + u64 num_texture_pixel_formats{}; u64 num_cbuf_values{}; file.read(reinterpret_cast(&code_size), sizeof(code_size)) .read(reinterpret_cast(&num_texture_types), sizeof(num_texture_types)) + .read(reinterpret_cast(&num_texture_pixel_formats), + sizeof(num_texture_pixel_formats)) .read(reinterpret_cast(&num_cbuf_values), sizeof(num_cbuf_values)) .read(reinterpret_cast(&local_memory_size), sizeof(local_memory_size)) .read(reinterpret_cast(&texture_bound), sizeof(texture_bound)) .read(reinterpret_cast(&start_address), sizeof(start_address)) .read(reinterpret_cast(&read_lowest), sizeof(read_lowest)) .read(reinterpret_cast(&read_highest), sizeof(read_highest)) + .read(reinterpret_cast(&viewport_transform_state), sizeof(viewport_transform_state)) .read(reinterpret_cast(&stage), sizeof(stage)); code = std::make_unique(Common::DivCeil(code_size, sizeof(u64))); file.read(reinterpret_cast(code.get()), code_size); @@ -361,6 +430,13 @@ void FileEnvironment::Deserialize(std::ifstream& file) { .read(reinterpret_cast(&type), sizeof(type)); texture_types.emplace(key, type); } + for (size_t i = 0; i < num_texture_pixel_formats; ++i) { + u32 key; + Shader::TexturePixelFormat format; + file.read(reinterpret_cast(&key), sizeof(key)) + .read(reinterpret_cast(&format), sizeof(format)); + texture_pixel_formats.emplace(key, format); + } for (size_t i = 0; i < num_cbuf_values; ++i) { u64 key; u32 value; @@ -408,6 +484,18 @@ Shader::TextureType FileEnvironment::ReadTextureType(u32 handle) { return it->second; } +Shader::TexturePixelFormat FileEnvironment::ReadTexturePixelFormat(u32 handle) { + const auto it{texture_pixel_formats.find(handle)}; + if (it == texture_pixel_formats.end()) { + throw Shader::LogicError("Uncached read texture pixel format"); + } + return it->second; +} + +u32 FileEnvironment::ReadViewportTransformState() { + return viewport_transform_state; +} + u32 FileEnvironment::LocalMemorySize() const { return local_memory_size; } diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index 5a145f3..1342fab 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -10,12 +10,12 @@ #include #include #include -#include #include #include #include #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "common/unique_function.h" #include "shader_recompiler/environment.h" #include "video_core/engines/maxwell_3d.h" @@ -63,14 +63,15 @@ public: protected: std::optional TryFindSize(); - Shader::TextureType ReadTextureTypeImpl(GPUVAddr tic_addr, u32 tic_limit, bool via_header_index, - u32 raw); + Tegra::Texture::TICEntry ReadTextureInfo(GPUVAddr tic_addr, u32 tic_limit, + bool via_header_index, u32 raw); Tegra::MemoryManager* gpu_memory{}; GPUVAddr program_base{}; std::vector code; std::unordered_map texture_types; + std::unordered_map texture_pixel_formats; std::unordered_map cbuf_values; u32 local_memory_size{}; @@ -85,6 +86,8 @@ protected: u32 cached_highest = 0; u32 initial_offset = 0; + u32 viewport_transform_state = 1; + bool has_unbound_instructions = false; }; @@ -93,7 +96,7 @@ public: explicit GraphicsEnvironment() = default; explicit GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, - Tegra::Engines::Maxwell3D::Regs::ShaderProgram program, + Tegra::Engines::Maxwell3D::Regs::ShaderType program, GPUVAddr program_base_, u32 start_address_); ~GraphicsEnvironment() override = default; @@ -102,6 +105,10 @@ public: Shader::TextureType ReadTextureType(u32 handle) override; + Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; + + u32 ReadViewportTransformState() override; + private: Tegra::Engines::Maxwell3D* maxwell3d{}; size_t stage_index{}; @@ -120,6 +127,10 @@ public: Shader::TextureType ReadTextureType(u32 handle) override; + Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; + + u32 ReadViewportTransformState() override; + private: Tegra::Engines::KeplerCompute* kepler_compute{}; }; @@ -143,6 +154,10 @@ public: [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override; + [[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; + + [[nodiscard]] u32 ReadViewportTransformState() override; + [[nodiscard]] u32 LocalMemorySize() const override; [[nodiscard]] u32 SharedMemorySize() const override; @@ -156,6 +171,7 @@ public: private: std::unique_ptr code; std::unordered_map texture_types; + std::unordered_map texture_pixel_formats; std::unordered_map cbuf_values; std::array workgroup_size{}; u32 local_memory_size{}; @@ -164,6 +180,7 @@ private: u32 read_lowest{}; u32 read_highest{}; u32 initial_offset{}; + u32 viewport_transform_state = 1; }; void SerializePipeline(std::span key, std::span envs, diff --git a/src/video_core/smaa_area_tex.h b/src/video_core/smaa_area_tex.h new file mode 100644 index 0000000..40d0941 --- /dev/null +++ b/src/video_core/smaa_area_tex.h @@ -0,0 +1,11223 @@ +// SPDX-FileCopyrightText: 2013 Jorge Jimenez (jorge@iryoku.com) +// SPDX-FileCopyrightText: 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) +// SPDX-FileCopyrightText: 2013 Belen Masia (bmasia@unizar.es) +// SPDX-FileCopyrightText: 2013 Fernando Navarro (fernandn@microsoft.com) +// SPDX-FileCopyrightText: 2013 Diego Gutierrez (diegog@unizar.es) +// SPDX-License-Identifier: MIT + +#ifndef AREATEX_H +#define AREATEX_H + +#define AREATEX_WIDTH 160 +#define AREATEX_HEIGHT 560 +#define AREATEX_PITCH (AREATEX_WIDTH * 2) +#define AREATEX_SIZE (AREATEX_HEIGHT * AREATEX_PITCH) + +/** + * Stored in R8G8 format. Load it in the following format: + * - DX9: D3DFMT_A8L8 + * - DX10: DXGI_FORMAT_R8G8_UNORM + */ +static const unsigned char areaTexBytes[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x44, 0x7b, 0x41, 0x5d, 0x42, 0x54, 0x41, 0x4f, + 0x42, 0x4b, 0x42, 0x49, 0x42, 0x48, 0x41, 0x47, 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x44, + 0x42, 0x44, 0x42, 0x44, 0x42, 0x43, 0x41, 0x43, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, + 0x04, 0x7f, 0x22, 0x3d, 0x2b, 0x3d, 0x30, 0x3d, 0x33, 0x3d, 0x35, 0x3d, 0x37, 0x3d, 0x38, 0x3d, + 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, + 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x40, 0x7f, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x62, 0x20, 0x4d, 0x2a, 0x48, 0x30, 0x44, 0x33, + 0x44, 0x35, 0x44, 0x36, 0x43, 0x37, 0x42, 0x38, 0x43, 0x39, 0x42, 0x39, 0x42, 0x3a, 0x42, 0x3a, + 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x41, 0x3b, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, + 0x00, 0x5d, 0x0c, 0x49, 0x16, 0x43, 0x1d, 0x41, 0x22, 0x40, 0x26, 0x3f, 0x29, 0x3f, 0x2c, 0x3f, + 0x2e, 0x3e, 0x2f, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, + 0x35, 0x3e, 0x36, 0x3e, 0x37, 0x3e, 0x37, 0x3e, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x00, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x6d, 0x0b, 0x57, 0x16, 0x4f, 0x1d, 0x4a, 0x22, + 0x48, 0x26, 0x47, 0x29, 0x46, 0x2b, 0x44, 0x2d, 0x44, 0x2f, 0x44, 0x30, 0x44, 0x31, 0x44, 0x32, + 0x43, 0x33, 0x43, 0x34, 0x43, 0x34, 0x42, 0x35, 0x43, 0x36, 0x42, 0x36, 0x42, 0x37, 0x42, 0x37, + 0x00, 0x68, 0x06, 0x54, 0x0d, 0x4b, 0x14, 0x47, 0x19, 0x44, 0x1d, 0x43, 0x20, 0x41, 0x23, 0x41, + 0x26, 0x40, 0x27, 0x40, 0x29, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x2f, 0x3f, + 0x30, 0x3f, 0x30, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x72, 0x06, 0x5f, 0x0d, 0x56, 0x14, 0x4f, 0x19, + 0x4d, 0x1d, 0x4a, 0x20, 0x49, 0x23, 0x47, 0x25, 0x46, 0x27, 0x46, 0x29, 0x45, 0x2a, 0x45, 0x2c, + 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2f, 0x43, 0x30, 0x44, 0x30, 0x44, 0x31, 0x43, 0x32, 0x43, 0x33, + 0x00, 0x6d, 0x04, 0x5b, 0x09, 0x51, 0x0e, 0x4c, 0x13, 0x48, 0x17, 0x46, 0x1a, 0x44, 0x1d, 0x43, + 0x1f, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, + 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x75, 0x03, 0x64, 0x09, 0x5a, 0x0e, 0x54, 0x13, + 0x51, 0x17, 0x4e, 0x1a, 0x4c, 0x1d, 0x49, 0x1f, 0x49, 0x22, 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, + 0x46, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, 0x44, 0x2c, 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2e, + 0x00, 0x70, 0x02, 0x60, 0x07, 0x56, 0x0b, 0x50, 0x0f, 0x4c, 0x12, 0x49, 0x16, 0x47, 0x18, 0x46, + 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x43, 0x21, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, 0x26, 0x40, + 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x77, 0x02, 0x68, 0x06, 0x5f, 0x0b, 0x58, 0x0f, + 0x54, 0x12, 0x51, 0x15, 0x4f, 0x18, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1f, 0x49, 0x21, 0x48, 0x22, + 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x45, 0x27, 0x45, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, + 0x00, 0x72, 0x02, 0x64, 0x05, 0x5b, 0x08, 0x54, 0x0c, 0x50, 0x0f, 0x4d, 0x12, 0x4a, 0x14, 0x48, + 0x17, 0x47, 0x19, 0x46, 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x44, 0x20, 0x42, 0x21, 0x42, 0x22, 0x42, + 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, 0x27, 0x40, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x78, 0x02, 0x6b, 0x05, 0x62, 0x08, 0x5b, 0x0c, + 0x57, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4e, 0x17, 0x4d, 0x19, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, + 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, 0x46, 0x24, 0x46, 0x25, 0x46, 0x26, 0x46, 0x27, 0x45, 0x27, + 0x00, 0x74, 0x01, 0x66, 0x04, 0x5e, 0x07, 0x57, 0x0a, 0x53, 0x0d, 0x4f, 0x10, 0x4d, 0x12, 0x4b, + 0x14, 0x49, 0x16, 0x48, 0x18, 0x46, 0x1a, 0x45, 0x1b, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x20, 0x43, + 0x21, 0x42, 0x22, 0x42, 0x23, 0x42, 0x24, 0x41, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x79, 0x01, 0x6d, 0x04, 0x65, 0x07, 0x5e, 0x0a, + 0x5a, 0x0d, 0x56, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4f, 0x16, 0x4e, 0x18, 0x4c, 0x1a, 0x4b, 0x1b, + 0x4b, 0x1d, 0x4a, 0x1e, 0x49, 0x1f, 0x48, 0x21, 0x48, 0x22, 0x48, 0x23, 0x47, 0x23, 0x46, 0x25, + 0x00, 0x75, 0x01, 0x69, 0x03, 0x61, 0x06, 0x5a, 0x08, 0x56, 0x0b, 0x52, 0x0d, 0x4f, 0x10, 0x4d, + 0x12, 0x4b, 0x14, 0x4a, 0x16, 0x48, 0x17, 0x47, 0x19, 0x46, 0x1a, 0x45, 0x1c, 0x45, 0x1d, 0x44, + 0x1e, 0x44, 0x1f, 0x44, 0x20, 0x43, 0x21, 0x42, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7a, 0x01, 0x6f, 0x03, 0x67, 0x06, 0x60, 0x08, + 0x5d, 0x0b, 0x59, 0x0d, 0x56, 0x10, 0x53, 0x11, 0x51, 0x14, 0x50, 0x15, 0x4e, 0x17, 0x4d, 0x19, + 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x48, 0x1e, 0x49, 0x1f, 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, + 0x00, 0x76, 0x01, 0x6b, 0x03, 0x63, 0x05, 0x5d, 0x07, 0x58, 0x09, 0x54, 0x0c, 0x52, 0x0e, 0x4f, + 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, 0x15, 0x49, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x45, + 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x01, 0x71, 0x02, 0x69, 0x05, 0x63, 0x07, + 0x5f, 0x09, 0x5b, 0x0c, 0x58, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4e, 0x16, + 0x4e, 0x18, 0x4c, 0x19, 0x4c, 0x1a, 0x4a, 0x1c, 0x4b, 0x1d, 0x49, 0x1e, 0x49, 0x1f, 0x49, 0x20, + 0x00, 0x77, 0x00, 0x6c, 0x02, 0x65, 0x04, 0x5f, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x54, 0x0c, 0x51, + 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, 0x15, 0x49, 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, + 0x1a, 0x46, 0x1b, 0x45, 0x1c, 0x45, 0x1d, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, + 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, + 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x00, 0x72, 0x02, 0x6b, 0x04, 0x64, 0x06, + 0x61, 0x08, 0x5d, 0x0a, 0x5a, 0x0c, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, + 0x4f, 0x16, 0x4e, 0x17, 0x4d, 0x18, 0x4b, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, 0x4b, 0x1d, 0x49, 0x1d, + 0x00, 0x77, 0x00, 0x6e, 0x02, 0x66, 0x04, 0x61, 0x05, 0x5c, 0x07, 0x59, 0x09, 0x56, 0x0b, 0x53, + 0x0d, 0x51, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x48, 0x17, 0x48, + 0x18, 0x47, 0x19, 0x46, 0x1b, 0x46, 0x1b, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, + 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, + 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x73, 0x02, 0x6c, 0x03, 0x66, 0x05, + 0x63, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, + 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x17, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, + 0x00, 0x77, 0x00, 0x6f, 0x02, 0x68, 0x03, 0x63, 0x05, 0x5e, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x55, + 0x0b, 0x53, 0x0d, 0x50, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x49, + 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x19, 0x46, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, + 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, + 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x74, 0x02, 0x6e, 0x03, 0x68, 0x05, + 0x64, 0x06, 0x60, 0x08, 0x5d, 0x0a, 0x5a, 0x0b, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, + 0x52, 0x13, 0x50, 0x14, 0x50, 0x15, 0x4d, 0x16, 0x4e, 0x18, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, + 0x00, 0x78, 0x00, 0x70, 0x01, 0x69, 0x03, 0x64, 0x04, 0x60, 0x06, 0x5c, 0x07, 0x59, 0x09, 0x56, + 0x0b, 0x54, 0x0c, 0x52, 0x0d, 0x50, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x13, 0x4a, + 0x15, 0x4a, 0x15, 0x48, 0x17, 0x48, 0x18, 0x47, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, + 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, + 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x75, 0x01, 0x6f, 0x02, 0x69, 0x04, + 0x65, 0x06, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0b, 0x5a, 0x0c, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, + 0x53, 0x11, 0x52, 0x13, 0x50, 0x13, 0x4f, 0x15, 0x4e, 0x15, 0x4e, 0x17, 0x4d, 0x18, 0x4c, 0x18, + 0x00, 0x78, 0x00, 0x71, 0x01, 0x6a, 0x03, 0x66, 0x04, 0x61, 0x05, 0x5d, 0x06, 0x5a, 0x08, 0x58, + 0x09, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0e, 0x50, 0x0f, 0x4e, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4c, + 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x49, 0x16, 0x48, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, + 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, 0x70, 0x02, 0x6a, 0x03, + 0x67, 0x05, 0x63, 0x06, 0x60, 0x08, 0x5d, 0x09, 0x5b, 0x0b, 0x59, 0x0c, 0x57, 0x0d, 0x55, 0x0e, + 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x4f, 0x13, 0x50, 0x15, 0x4f, 0x15, 0x4e, 0x16, 0x4e, 0x18, + 0x00, 0x79, 0x00, 0x71, 0x01, 0x6c, 0x02, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5c, 0x07, 0x59, + 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, + 0x13, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, + 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, + 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, 0x71, 0x02, 0x6c, 0x03, + 0x68, 0x05, 0x64, 0x06, 0x61, 0x07, 0x5e, 0x09, 0x5c, 0x0a, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0e, + 0x55, 0x0e, 0x55, 0x11, 0x52, 0x11, 0x51, 0x12, 0x51, 0x13, 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x15, + 0x00, 0x79, 0x00, 0x72, 0x01, 0x6d, 0x02, 0x68, 0x03, 0x63, 0x04, 0x60, 0x06, 0x5d, 0x07, 0x5b, + 0x08, 0x58, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, + 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, + 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x00, 0x7d, 0x00, 0x58, 0x00, 0x70, 0x00, 0x77, 0x00, 0x79, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, + 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, + 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x1f, 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, + 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x77, 0x01, 0x72, 0x02, 0x6d, 0x03, + 0x69, 0x04, 0x66, 0x06, 0x63, 0x07, 0x5f, 0x08, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0c, 0x58, 0x0c, + 0x57, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x51, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x14, 0x50, 0x15, + 0x00, 0x79, 0x00, 0x73, 0x01, 0x6d, 0x02, 0x69, 0x03, 0x65, 0x04, 0x61, 0x05, 0x5e, 0x06, 0x5c, + 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0d, 0x53, 0x0d, 0x51, 0x0f, 0x50, 0x0f, 0x4e, + 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, + 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, + 0x00, 0x58, 0x00, 0x44, 0x00, 0x55, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x75, 0x00, 0x78, + 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, + 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, + 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, + 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x78, 0x01, 0x72, 0x02, 0x6e, 0x02, + 0x6a, 0x03, 0x67, 0x05, 0x64, 0x06, 0x60, 0x07, 0x5f, 0x09, 0x5c, 0x0a, 0x5b, 0x0b, 0x5a, 0x0c, + 0x57, 0x0c, 0x56, 0x0e, 0x55, 0x0f, 0x53, 0x11, 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x13, + 0x00, 0x79, 0x00, 0x73, 0x01, 0x6e, 0x02, 0x69, 0x03, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5d, + 0x07, 0x5b, 0x08, 0x58, 0x09, 0x56, 0x0b, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, + 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x12, 0x4c, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, + 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x70, 0x00, 0x55, 0x00, 0x20, 0x00, 0x3e, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x63, 0x00, 0x6a, + 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x3f, 0x00, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, + 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, + 0x66, 0x00, 0x3f, 0x00, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, + 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x01, 0x73, 0x02, 0x6e, 0x02, + 0x6b, 0x03, 0x68, 0x05, 0x65, 0x06, 0x61, 0x07, 0x5f, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0b, + 0x59, 0x0c, 0x57, 0x0d, 0x56, 0x0e, 0x54, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, + 0x00, 0x79, 0x00, 0x74, 0x00, 0x6f, 0x02, 0x6a, 0x03, 0x66, 0x04, 0x63, 0x05, 0x60, 0x05, 0x5e, + 0x06, 0x5b, 0x07, 0x59, 0x09, 0x58, 0x09, 0x55, 0x0b, 0x55, 0x0c, 0x53, 0x0d, 0x52, 0x0d, 0x50, + 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, + 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, + 0x00, 0x77, 0x00, 0x67, 0x00, 0x3e, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, + 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, + 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, + 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, + 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x00, 0x74, 0x01, 0x6f, 0x02, + 0x6c, 0x03, 0x68, 0x04, 0x65, 0x05, 0x62, 0x06, 0x61, 0x07, 0x5f, 0x09, 0x5c, 0x09, 0x5b, 0x0b, + 0x5a, 0x0b, 0x58, 0x0c, 0x57, 0x0d, 0x55, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, + 0x00, 0x79, 0x00, 0x74, 0x00, 0x70, 0x01, 0x6b, 0x02, 0x67, 0x03, 0x64, 0x04, 0x61, 0x05, 0x5f, + 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0c, 0x53, 0x0d, 0x51, + 0x0e, 0x50, 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, + 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, + 0x00, 0x79, 0x00, 0x6e, 0x00, 0x50, 0x00, 0x28, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, + 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, + 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, + 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, + 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, + 0x21, 0x5d, 0x0c, 0x68, 0x06, 0x6d, 0x04, 0x71, 0x02, 0x72, 0x02, 0x74, 0x01, 0x75, 0x01, 0x76, + 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, + 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, 0x42, 0x40, 0x17, 0x55, 0x0c, 0x60, 0x08, 0x66, + 0x05, 0x6a, 0x04, 0x6d, 0x03, 0x6f, 0x02, 0x71, 0x02, 0x73, 0x01, 0x73, 0x01, 0x74, 0x01, 0x75, + 0x01, 0x76, 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x21, 0x5d, 0x0c, 0x68, 0x06, 0x6d, 0x04, 0x71, + 0x02, 0x72, 0x02, 0x74, 0x01, 0x75, 0x01, 0x76, 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, + 0x00, 0x78, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, + 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, + 0x00, 0x7b, 0x00, 0x72, 0x00, 0x5a, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x16, 0x00, 0x28, + 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, + 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, + 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, + 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, + 0x2b, 0x49, 0x16, 0x53, 0x0d, 0x5b, 0x09, 0x60, 0x07, 0x64, 0x05, 0x67, 0x04, 0x69, 0x03, 0x6b, + 0x03, 0x6c, 0x02, 0x6e, 0x02, 0x6f, 0x02, 0x70, 0x01, 0x71, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, + 0x01, 0x73, 0x01, 0x74, 0x00, 0x74, 0x00, 0x75, 0x57, 0x16, 0x2d, 0x2c, 0x1b, 0x3b, 0x12, 0x45, + 0x0d, 0x4d, 0x0b, 0x53, 0x08, 0x57, 0x07, 0x5b, 0x05, 0x5e, 0x05, 0x61, 0x04, 0x63, 0x04, 0x65, + 0x03, 0x67, 0x02, 0x68, 0x02, 0x69, 0x02, 0x6b, 0x02, 0x6c, 0x01, 0x6d, 0x01, 0x6e, 0x01, 0x6f, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x2b, 0x49, 0x16, 0x53, 0x0d, 0x5b, 0x09, 0x60, + 0x07, 0x64, 0x05, 0x67, 0x04, 0x69, 0x03, 0x6b, 0x03, 0x6c, 0x02, 0x6e, 0x02, 0x6f, 0x02, 0x70, + 0x01, 0x71, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, 0x01, 0x73, 0x01, 0x74, 0x00, 0x74, 0x00, 0x75, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, + 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, + 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, + 0x31, 0x43, 0x1d, 0x4b, 0x14, 0x51, 0x0e, 0x56, 0x0b, 0x5b, 0x08, 0x5e, 0x07, 0x61, 0x06, 0x63, + 0x05, 0x65, 0x04, 0x67, 0x04, 0x68, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6c, 0x02, 0x6d, 0x02, 0x6d, + 0x02, 0x6e, 0x02, 0x6f, 0x02, 0x70, 0x01, 0x70, 0x61, 0x0c, 0x3b, 0x1b, 0x28, 0x28, 0x1d, 0x32, + 0x16, 0x3a, 0x11, 0x41, 0x0e, 0x47, 0x0c, 0x4b, 0x0a, 0x4f, 0x09, 0x53, 0x07, 0x55, 0x06, 0x58, + 0x05, 0x5a, 0x05, 0x5c, 0x05, 0x5e, 0x04, 0x60, 0x04, 0x61, 0x04, 0x63, 0x03, 0x64, 0x02, 0x66, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x31, 0x43, 0x1d, 0x4b, 0x14, 0x51, 0x0e, 0x56, + 0x0b, 0x5b, 0x08, 0x5e, 0x07, 0x61, 0x06, 0x63, 0x05, 0x65, 0x04, 0x67, 0x04, 0x68, 0x03, 0x69, + 0x03, 0x6a, 0x03, 0x6c, 0x02, 0x6d, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x6f, 0x02, 0x70, 0x01, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, + 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, + 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, + 0x33, 0x41, 0x22, 0x46, 0x19, 0x4c, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5d, + 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04, 0x68, 0x03, 0x69, + 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x02, 0x6c, 0x67, 0x07, 0x45, 0x12, 0x32, 0x1d, 0x26, 0x26, + 0x1e, 0x2e, 0x18, 0x34, 0x14, 0x3a, 0x11, 0x3f, 0x0f, 0x44, 0x0c, 0x47, 0x0b, 0x4b, 0x0a, 0x4d, + 0x09, 0x51, 0x07, 0x52, 0x07, 0x55, 0x06, 0x57, 0x05, 0x58, 0x05, 0x5a, 0x05, 0x5c, 0x04, 0x5d, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x33, 0x41, 0x22, 0x46, 0x19, 0x4c, 0x13, 0x50, + 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5d, 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, + 0x04, 0x66, 0x04, 0x66, 0x04, 0x68, 0x03, 0x69, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x02, 0x6c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, + 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, + 0x35, 0x40, 0x27, 0x44, 0x1d, 0x48, 0x17, 0x4c, 0x12, 0x50, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, + 0x09, 0x5a, 0x08, 0x5c, 0x07, 0x5e, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, 0x04, 0x65, + 0x04, 0x66, 0x04, 0x66, 0x04, 0x67, 0x03, 0x68, 0x6b, 0x05, 0x4d, 0x0d, 0x3b, 0x16, 0x2e, 0x1e, + 0x25, 0x25, 0x1f, 0x2b, 0x1a, 0x31, 0x16, 0x36, 0x13, 0x3a, 0x10, 0x3e, 0x0f, 0x42, 0x0d, 0x45, + 0x0c, 0x47, 0x0a, 0x4a, 0x0a, 0x4c, 0x09, 0x4f, 0x07, 0x51, 0x07, 0x52, 0x07, 0x54, 0x06, 0x56, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x35, 0x40, 0x27, 0x44, 0x1d, 0x48, 0x17, 0x4c, + 0x12, 0x50, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5a, 0x08, 0x5c, 0x07, 0x5e, 0x06, 0x60, + 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, 0x04, 0x65, 0x04, 0x66, 0x04, 0x66, 0x04, 0x67, 0x03, 0x68, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, + 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, + 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, + 0x37, 0x3f, 0x29, 0x43, 0x21, 0x46, 0x1a, 0x49, 0x16, 0x4d, 0x12, 0x50, 0x10, 0x52, 0x0d, 0x54, + 0x0c, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, 0x07, 0x5d, 0x06, 0x5f, 0x06, 0x60, 0x06, 0x61, + 0x05, 0x62, 0x05, 0x63, 0x04, 0x64, 0x04, 0x65, 0x6e, 0x04, 0x53, 0x0a, 0x41, 0x11, 0x34, 0x18, + 0x2b, 0x1f, 0x24, 0x24, 0x1f, 0x29, 0x1b, 0x2e, 0x17, 0x33, 0x15, 0x37, 0x12, 0x3a, 0x10, 0x3d, + 0x0f, 0x40, 0x0d, 0x43, 0x0c, 0x45, 0x0c, 0x48, 0x0a, 0x4a, 0x0a, 0x4c, 0x09, 0x4e, 0x07, 0x4f, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x37, 0x3f, 0x29, 0x43, 0x21, 0x46, 0x1a, 0x49, + 0x16, 0x4d, 0x12, 0x50, 0x10, 0x52, 0x0d, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5d, 0x06, 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, 0x04, 0x64, 0x04, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, + 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, + 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, + 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, + 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, + 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, + 0x38, 0x3f, 0x2c, 0x41, 0x23, 0x44, 0x1d, 0x47, 0x19, 0x4a, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x52, + 0x0e, 0x54, 0x0c, 0x56, 0x0b, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, 0x07, 0x5d, 0x07, 0x5e, + 0x06, 0x5f, 0x06, 0x61, 0x05, 0x61, 0x05, 0x62, 0x70, 0x03, 0x58, 0x08, 0x47, 0x0e, 0x3a, 0x14, + 0x31, 0x1a, 0x29, 0x1f, 0x24, 0x24, 0x20, 0x28, 0x1c, 0x2d, 0x19, 0x31, 0x16, 0x34, 0x14, 0x37, + 0x12, 0x3a, 0x10, 0x3d, 0x0f, 0x3f, 0x0e, 0x42, 0x0c, 0x44, 0x0c, 0x46, 0x0b, 0x47, 0x0a, 0x4a, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x38, 0x3f, 0x2c, 0x41, 0x23, 0x44, 0x1d, 0x47, + 0x19, 0x4a, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x52, 0x0e, 0x54, 0x0c, 0x56, 0x0b, 0x57, 0x0a, 0x59, + 0x09, 0x5a, 0x08, 0x5c, 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x61, 0x05, 0x61, 0x05, 0x62, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, + 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, + 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, + 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, + 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, + 0x39, 0x3f, 0x2e, 0x41, 0x26, 0x43, 0x20, 0x46, 0x1b, 0x48, 0x17, 0x4b, 0x14, 0x4d, 0x12, 0x4f, + 0x10, 0x51, 0x0e, 0x53, 0x0d, 0x55, 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x60, 0x71, 0x02, 0x5b, 0x07, 0x4c, 0x0c, 0x3f, 0x11, + 0x36, 0x16, 0x2e, 0x1b, 0x29, 0x20, 0x24, 0x23, 0x20, 0x28, 0x1d, 0x2b, 0x19, 0x2f, 0x17, 0x32, + 0x16, 0x35, 0x12, 0x37, 0x12, 0x3a, 0x10, 0x3c, 0x0f, 0x3f, 0x0e, 0x41, 0x0c, 0x42, 0x0c, 0x45, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x39, 0x3f, 0x2e, 0x41, 0x26, 0x43, 0x20, 0x46, + 0x1b, 0x48, 0x17, 0x4b, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x51, 0x0e, 0x53, 0x0d, 0x55, 0x0b, 0x56, + 0x0b, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, + 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, + 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, + 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, + 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, + 0x39, 0x3e, 0x2f, 0x40, 0x28, 0x42, 0x22, 0x45, 0x1d, 0x47, 0x19, 0x49, 0x16, 0x4b, 0x14, 0x4d, + 0x11, 0x4f, 0x10, 0x51, 0x0f, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, + 0x09, 0x5b, 0x08, 0x5b, 0x07, 0x5c, 0x07, 0x5e, 0x73, 0x02, 0x5e, 0x05, 0x4f, 0x0a, 0x44, 0x0f, + 0x3a, 0x13, 0x33, 0x18, 0x2d, 0x1c, 0x27, 0x20, 0x23, 0x23, 0x20, 0x27, 0x1d, 0x2a, 0x1a, 0x2d, + 0x18, 0x30, 0x16, 0x33, 0x14, 0x35, 0x12, 0x38, 0x12, 0x3a, 0x10, 0x3c, 0x0f, 0x3e, 0x0f, 0x41, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x39, 0x3e, 0x2f, 0x40, 0x28, 0x42, 0x22, 0x45, + 0x1d, 0x47, 0x19, 0x49, 0x16, 0x4b, 0x14, 0x4d, 0x11, 0x4f, 0x10, 0x51, 0x0f, 0x53, 0x0d, 0x54, + 0x0c, 0x55, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, 0x07, 0x5c, 0x07, 0x5e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, + 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, + 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, + 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, + 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, + 0x3a, 0x3e, 0x31, 0x40, 0x29, 0x42, 0x23, 0x44, 0x1f, 0x46, 0x1b, 0x48, 0x18, 0x4a, 0x15, 0x4c, + 0x13, 0x4d, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x56, 0x0b, 0x57, + 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, 0x74, 0x01, 0x62, 0x05, 0x53, 0x08, 0x47, 0x0c, + 0x3e, 0x11, 0x37, 0x15, 0x31, 0x19, 0x2b, 0x1d, 0x27, 0x20, 0x23, 0x23, 0x20, 0x26, 0x1d, 0x2a, + 0x1b, 0x2c, 0x19, 0x2f, 0x16, 0x31, 0x16, 0x34, 0x13, 0x35, 0x12, 0x38, 0x12, 0x3b, 0x0f, 0x3b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3a, 0x3e, 0x31, 0x40, 0x29, 0x42, 0x23, 0x44, + 0x1f, 0x46, 0x1b, 0x48, 0x18, 0x4a, 0x15, 0x4c, 0x13, 0x4d, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, + 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x56, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, + 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, + 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x3a, 0x3e, 0x32, 0x3f, 0x2b, 0x41, 0x25, 0x43, 0x21, 0x45, 0x1d, 0x46, 0x1a, 0x48, 0x17, 0x4a, + 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, + 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x58, 0x09, 0x59, 0x75, 0x01, 0x64, 0x04, 0x56, 0x07, 0x4b, 0x0b, + 0x42, 0x0f, 0x3a, 0x12, 0x34, 0x16, 0x2f, 0x19, 0x2a, 0x1d, 0x26, 0x20, 0x23, 0x23, 0x21, 0x26, + 0x1d, 0x29, 0x1b, 0x2b, 0x19, 0x2e, 0x18, 0x30, 0x16, 0x32, 0x15, 0x35, 0x12, 0x35, 0x12, 0x38, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3a, 0x3e, 0x32, 0x3f, 0x2b, 0x41, 0x25, 0x43, + 0x21, 0x45, 0x1d, 0x46, 0x1a, 0x48, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, 0x10, 0x50, + 0x0f, 0x52, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x58, 0x09, 0x59, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, + 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, + 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, + 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x3b, 0x3e, 0x33, 0x3f, 0x2c, 0x41, 0x27, 0x42, 0x22, 0x44, 0x1f, 0x45, 0x1c, 0x47, 0x19, 0x49, + 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x51, 0x0e, 0x53, 0x0d, 0x54, + 0x0c, 0x55, 0x0b, 0x55, 0x0b, 0x57, 0x0a, 0x58, 0x76, 0x01, 0x66, 0x04, 0x58, 0x06, 0x4d, 0x0a, + 0x45, 0x0d, 0x3e, 0x10, 0x37, 0x14, 0x32, 0x17, 0x2d, 0x1a, 0x2a, 0x1d, 0x26, 0x21, 0x22, 0x22, + 0x21, 0x26, 0x1d, 0x28, 0x1c, 0x2b, 0x19, 0x2c, 0x19, 0x30, 0x16, 0x30, 0x16, 0x33, 0x14, 0x35, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3b, 0x3e, 0x33, 0x3f, 0x2c, 0x41, 0x27, 0x42, + 0x22, 0x44, 0x1f, 0x45, 0x1c, 0x47, 0x19, 0x49, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, + 0x10, 0x50, 0x0f, 0x51, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x0b, 0x55, 0x0b, 0x57, 0x0a, 0x58, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x3e, 0x33, 0x3f, 0x2d, 0x40, 0x28, 0x42, 0x24, 0x44, 0x20, 0x45, 0x1d, 0x46, 0x1a, 0x48, + 0x18, 0x49, 0x16, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x10, 0x50, 0x0f, 0x51, 0x0f, 0x53, + 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x55, 0x0b, 0x56, 0x77, 0x01, 0x67, 0x03, 0x5a, 0x05, 0x51, 0x09, + 0x48, 0x0c, 0x40, 0x0f, 0x3a, 0x12, 0x35, 0x16, 0x30, 0x18, 0x2c, 0x1b, 0x29, 0x1d, 0x26, 0x21, + 0x22, 0x22, 0x21, 0x26, 0x1d, 0x27, 0x1d, 0x2b, 0x1a, 0x2b, 0x19, 0x2e, 0x17, 0x30, 0x16, 0x31, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3b, 0x3e, 0x33, 0x3f, 0x2d, 0x40, 0x28, 0x42, + 0x24, 0x44, 0x20, 0x45, 0x1d, 0x46, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4b, 0x14, 0x4c, 0x13, 0x4e, + 0x11, 0x4e, 0x10, 0x50, 0x0f, 0x51, 0x0f, 0x53, 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x55, 0x0b, 0x56, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x3e, 0x34, 0x3f, 0x2e, 0x40, 0x29, 0x41, 0x25, 0x43, 0x21, 0x44, 0x1e, 0x45, 0x1c, 0x47, + 0x19, 0x48, 0x18, 0x4a, 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, + 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x77, 0x01, 0x68, 0x02, 0x5d, 0x05, 0x52, 0x07, + 0x4a, 0x0a, 0x43, 0x0d, 0x3d, 0x10, 0x38, 0x12, 0x33, 0x16, 0x2f, 0x19, 0x2b, 0x1b, 0x28, 0x1d, + 0x26, 0x21, 0x22, 0x22, 0x21, 0x26, 0x1e, 0x26, 0x1d, 0x2a, 0x1a, 0x2b, 0x19, 0x2d, 0x18, 0x30, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3b, 0x3e, 0x34, 0x3f, 0x2e, 0x40, 0x29, 0x41, + 0x25, 0x43, 0x21, 0x44, 0x1e, 0x45, 0x1c, 0x47, 0x19, 0x48, 0x18, 0x4a, 0x15, 0x4b, 0x14, 0x4c, + 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x54, 0x0c, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x35, 0x3f, 0x2f, 0x40, 0x2a, 0x41, 0x26, 0x42, 0x22, 0x44, 0x20, 0x45, 0x1d, 0x46, + 0x1b, 0x48, 0x18, 0x48, 0x17, 0x4a, 0x15, 0x4b, 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, + 0x0f, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x53, 0x77, 0x00, 0x6a, 0x02, 0x5e, 0x04, 0x55, 0x07, + 0x4c, 0x0a, 0x45, 0x0c, 0x40, 0x0f, 0x3a, 0x12, 0x35, 0x14, 0x31, 0x16, 0x2e, 0x19, 0x2b, 0x1c, + 0x27, 0x1d, 0x26, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1e, 0x26, 0x1d, 0x29, 0x1b, 0x2b, 0x19, 0x2b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x35, 0x3f, 0x2f, 0x40, 0x2a, 0x41, + 0x26, 0x42, 0x22, 0x44, 0x20, 0x45, 0x1d, 0x46, 0x1b, 0x48, 0x18, 0x48, 0x17, 0x4a, 0x15, 0x4b, + 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x35, 0x3f, 0x30, 0x3f, 0x2b, 0x40, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, 0x1e, 0x45, + 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, + 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x0e, 0x53, 0x78, 0x00, 0x6b, 0x02, 0x60, 0x04, 0x57, 0x06, + 0x4f, 0x09, 0x48, 0x0c, 0x42, 0x0e, 0x3c, 0x10, 0x38, 0x12, 0x34, 0x16, 0x30, 0x18, 0x2d, 0x19, + 0x2b, 0x1d, 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1e, 0x26, 0x1d, 0x28, 0x1c, 0x2b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x35, 0x3f, 0x30, 0x3f, 0x2b, 0x40, + 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, 0x1e, 0x45, 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, + 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x0e, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x36, 0x3f, 0x30, 0x3f, 0x2c, 0x40, 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x1f, 0x45, + 0x1d, 0x46, 0x1b, 0x47, 0x19, 0x48, 0x18, 0x4a, 0x15, 0x4a, 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, + 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x78, 0x00, 0x6d, 0x02, 0x61, 0x04, 0x58, 0x05, + 0x51, 0x07, 0x4a, 0x0a, 0x44, 0x0c, 0x3f, 0x0f, 0x3a, 0x12, 0x35, 0x14, 0x32, 0x16, 0x30, 0x19, + 0x2b, 0x19, 0x2a, 0x1d, 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1e, 0x26, 0x1d, 0x27, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x36, 0x3f, 0x30, 0x3f, 0x2c, 0x40, + 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, 0x19, 0x48, 0x18, 0x4a, + 0x15, 0x4a, 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x37, 0x3e, 0x31, 0x3f, 0x2d, 0x40, 0x29, 0x41, 0x26, 0x42, 0x23, 0x44, 0x20, 0x45, + 0x1e, 0x45, 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x48, 0x17, 0x4a, 0x15, 0x4a, 0x14, 0x4c, 0x13, 0x4c, + 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x78, 0x00, 0x6d, 0x02, 0x63, 0x04, 0x5a, 0x05, + 0x53, 0x07, 0x4c, 0x0a, 0x46, 0x0c, 0x41, 0x0f, 0x3c, 0x0f, 0x38, 0x12, 0x35, 0x16, 0x30, 0x16, + 0x2e, 0x19, 0x2b, 0x1a, 0x29, 0x1d, 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1f, 0x26, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x37, 0x3e, 0x31, 0x3f, 0x2d, 0x40, + 0x29, 0x41, 0x26, 0x42, 0x23, 0x44, 0x20, 0x45, 0x1e, 0x45, 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x48, + 0x17, 0x4a, 0x15, 0x4a, 0x14, 0x4c, 0x13, 0x4c, 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x37, 0x3e, 0x32, 0x3f, 0x2e, 0x40, 0x2a, 0x41, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, + 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, 0x15, 0x4b, 0x14, 0x4c, + 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x78, 0x00, 0x6e, 0x01, 0x65, 0x03, 0x5c, 0x05, + 0x54, 0x07, 0x4e, 0x09, 0x48, 0x0b, 0x43, 0x0c, 0x3e, 0x0f, 0x3b, 0x12, 0x36, 0x12, 0x33, 0x16, + 0x30, 0x17, 0x2d, 0x19, 0x2b, 0x1b, 0x28, 0x1d, 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x24, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x37, 0x3e, 0x32, 0x3f, 0x2e, 0x40, + 0x2a, 0x41, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, 0x1a, 0x48, + 0x18, 0x49, 0x16, 0x4a, 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x37, 0x3e, 0x33, 0x3f, 0x2e, 0x3f, 0x2b, 0x40, 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, + 0x20, 0x45, 0x1e, 0x45, 0x1c, 0x46, 0x1b, 0x48, 0x19, 0x48, 0x18, 0x4a, 0x16, 0x4a, 0x15, 0x4b, + 0x13, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x79, 0x00, 0x6f, 0x01, 0x66, 0x02, 0x5d, 0x04, + 0x56, 0x06, 0x4f, 0x07, 0x4a, 0x0a, 0x45, 0x0c, 0x41, 0x0f, 0x3b, 0x0f, 0x38, 0x12, 0x35, 0x14, + 0x31, 0x16, 0x30, 0x18, 0x2b, 0x19, 0x2b, 0x1c, 0x27, 0x1d, 0x26, 0x1f, 0x24, 0x22, 0x21, 0x22, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x37, 0x3e, 0x33, 0x3f, 0x2e, 0x3f, + 0x2b, 0x40, 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x20, 0x45, 0x1e, 0x45, 0x1c, 0x46, 0x1b, 0x48, + 0x19, 0x48, 0x18, 0x4a, 0x16, 0x4a, 0x15, 0x4b, 0x13, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x74, 0x00, 0x77, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x7b, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7c, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x08, 0x00, 0x44, 0x00, 0x56, 0x00, 0x61, 0x00, 0x67, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x70, 0x00, + 0x71, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x76, 0x00, 0x77, 0x00, 0x77, 0x00, + 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x44, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x71, 0x00, + 0x75, 0x00, 0x77, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7c, 0x00, + 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x0b, 0x57, 0x06, 0x5f, 0x03, 0x63, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6d, 0x01, 0x6e, 0x01, + 0x71, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x76, 0x00, 0x76, 0x00, + 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, 0x00, 0x82, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x40, 0x17, 0x17, 0x2c, 0x0c, 0x3b, 0x07, 0x45, 0x05, 0x4d, 0x04, 0x53, 0x03, 0x57, 0x02, + 0x5c, 0x02, 0x5e, 0x01, 0x61, 0x01, 0x63, 0x01, 0x66, 0x01, 0x67, 0x01, 0x68, 0x00, 0x69, 0x00, + 0x6b, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x41, 0x20, 0x4d, 0x0b, 0x58, 0x06, 0x5e, 0x03, + 0x64, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6c, 0x01, 0x6f, 0x01, 0x71, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x75, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x15, 0x4f, 0x0d, 0x56, 0x09, 0x59, 0x06, 0x5f, 0x05, 0x62, 0x04, 0x65, 0x03, 0x67, 0x02, + 0x69, 0x02, 0x6b, 0x02, 0x6c, 0x02, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x70, 0x01, + 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x74, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x55, 0x0c, 0x2c, 0x1b, 0x1b, 0x27, 0x12, 0x32, 0x0d, 0x3a, 0x0b, 0x41, 0x08, 0x47, 0x07, + 0x4c, 0x05, 0x4f, 0x05, 0x53, 0x04, 0x56, 0x04, 0x58, 0x03, 0x5a, 0x02, 0x5d, 0x02, 0x5e, 0x02, + 0x60, 0x02, 0x61, 0x01, 0x63, 0x01, 0x64, 0x01, 0x41, 0x2a, 0x47, 0x16, 0x4f, 0x0d, 0x54, 0x09, + 0x5b, 0x06, 0x5f, 0x05, 0x62, 0x04, 0x64, 0x03, 0x67, 0x02, 0x69, 0x02, 0x6b, 0x02, 0x6c, 0x02, + 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x70, 0x01, 0x71, 0x01, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x1d, 0x4a, 0x14, 0x50, 0x0e, 0x54, 0x0b, 0x59, 0x08, 0x5c, 0x07, 0x5f, 0x06, 0x60, 0x05, + 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, 0x68, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, 0x6c, 0x02, + 0x6e, 0x02, 0x6f, 0x01, 0x70, 0x01, 0x70, 0x01, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x60, 0x08, 0x3b, 0x12, 0x28, 0x1d, 0x1d, 0x26, 0x16, 0x2e, 0x11, 0x34, 0x0e, 0x3a, 0x0c, + 0x3f, 0x0a, 0x44, 0x08, 0x47, 0x07, 0x4b, 0x06, 0x4d, 0x05, 0x51, 0x05, 0x52, 0x04, 0x55, 0x04, + 0x57, 0x04, 0x58, 0x04, 0x5a, 0x03, 0x5c, 0x02, 0x42, 0x30, 0x45, 0x1d, 0x4b, 0x14, 0x4f, 0x0e, + 0x55, 0x0b, 0x59, 0x08, 0x5c, 0x07, 0x5e, 0x06, 0x61, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, + 0x68, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6b, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x6f, 0x01, 0x70, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x22, 0x48, 0x19, 0x4d, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5c, 0x07, + 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, 0x68, 0x03, 0x68, 0x02, + 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, 0x6d, 0x02, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x66, 0x05, 0x45, 0x0d, 0x32, 0x16, 0x26, 0x1e, 0x1e, 0x25, 0x18, 0x2b, 0x14, 0x31, 0x11, + 0x36, 0x0f, 0x3a, 0x0c, 0x3e, 0x0b, 0x42, 0x0a, 0x45, 0x09, 0x47, 0x07, 0x4a, 0x07, 0x4c, 0x06, + 0x4f, 0x05, 0x51, 0x05, 0x52, 0x05, 0x54, 0x04, 0x41, 0x33, 0x44, 0x22, 0x48, 0x19, 0x4c, 0x13, + 0x51, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x08, 0x5d, 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, + 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, 0x67, 0x03, 0x69, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x26, 0x47, 0x1d, 0x4a, 0x17, 0x4d, 0x12, 0x51, 0x0f, 0x54, 0x0d, 0x56, 0x0b, 0x58, 0x09, + 0x5b, 0x08, 0x5d, 0x07, 0x5f, 0x06, 0x60, 0x06, 0x62, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, + 0x67, 0x03, 0x67, 0x03, 0x68, 0x03, 0x69, 0x02, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x6a, 0x04, 0x4d, 0x0b, 0x3a, 0x11, 0x2e, 0x18, 0x25, 0x1f, 0x1f, 0x24, 0x1a, 0x29, 0x16, + 0x2e, 0x13, 0x33, 0x11, 0x37, 0x0f, 0x3a, 0x0d, 0x3d, 0x0c, 0x40, 0x0a, 0x43, 0x0a, 0x45, 0x09, + 0x48, 0x07, 0x4a, 0x07, 0x4c, 0x07, 0x4e, 0x06, 0x41, 0x35, 0x43, 0x26, 0x47, 0x1d, 0x49, 0x17, + 0x4e, 0x12, 0x51, 0x0f, 0x54, 0x0d, 0x55, 0x0b, 0x59, 0x09, 0x5b, 0x08, 0x5d, 0x07, 0x5f, 0x06, + 0x60, 0x06, 0x62, 0x05, 0x63, 0x05, 0x63, 0x04, 0x65, 0x03, 0x67, 0x03, 0x67, 0x03, 0x68, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x29, 0x45, 0x20, 0x49, 0x1a, 0x4b, 0x15, 0x4f, 0x12, 0x51, 0x0f, 0x54, 0x0d, 0x55, 0x0b, + 0x58, 0x0a, 0x5a, 0x09, 0x5b, 0x08, 0x5d, 0x07, 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, + 0x64, 0x05, 0x65, 0x04, 0x65, 0x03, 0x66, 0x03, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x6d, 0x03, 0x52, 0x08, 0x41, 0x0e, 0x34, 0x14, 0x2b, 0x1a, 0x24, 0x1f, 0x1f, 0x24, 0x1b, + 0x28, 0x17, 0x2d, 0x15, 0x31, 0x12, 0x34, 0x10, 0x37, 0x0f, 0x3a, 0x0d, 0x3d, 0x0c, 0x40, 0x0b, + 0x42, 0x0a, 0x44, 0x0a, 0x46, 0x09, 0x47, 0x07, 0x41, 0x36, 0x43, 0x29, 0x46, 0x20, 0x48, 0x1a, + 0x4c, 0x15, 0x4f, 0x12, 0x51, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x0a, 0x5a, 0x09, 0x5b, 0x08, + 0x5d, 0x07, 0x5f, 0x06, 0x60, 0x06, 0x60, 0x05, 0x62, 0x05, 0x64, 0x05, 0x65, 0x04, 0x65, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x2b, 0x45, 0x23, 0x48, 0x1d, 0x49, 0x18, 0x4d, 0x14, 0x4f, 0x12, 0x51, 0x10, 0x53, 0x0e, + 0x55, 0x0c, 0x57, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5c, 0x08, 0x5d, 0x07, 0x5f, 0x07, 0x5f, 0x06, + 0x61, 0x06, 0x62, 0x05, 0x63, 0x05, 0x64, 0x05, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x6f, 0x02, 0x57, 0x07, 0x47, 0x0c, 0x3a, 0x11, 0x31, 0x16, 0x29, 0x1b, 0x24, 0x20, 0x20, + 0x23, 0x1c, 0x28, 0x19, 0x2b, 0x16, 0x2f, 0x14, 0x32, 0x12, 0x35, 0x10, 0x38, 0x0f, 0x3a, 0x0e, + 0x3c, 0x0c, 0x3f, 0x0c, 0x41, 0x0b, 0x42, 0x0a, 0x41, 0x37, 0x43, 0x2b, 0x45, 0x23, 0x47, 0x1d, + 0x4a, 0x18, 0x4d, 0x14, 0x4f, 0x12, 0x51, 0x10, 0x53, 0x0e, 0x55, 0x0c, 0x57, 0x0b, 0x59, 0x0a, + 0x5a, 0x09, 0x5c, 0x08, 0x5d, 0x07, 0x5e, 0x07, 0x60, 0x06, 0x61, 0x06, 0x62, 0x05, 0x63, 0x05, + 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x1f, 0x1f, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x58, 0x00, 0x70, 0x00, 0x77, 0x00, 0x79, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x1f, 0x1f, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x43, 0x2d, 0x44, 0x25, 0x46, 0x1f, 0x48, 0x1b, 0x4b, 0x17, 0x4d, 0x14, 0x50, 0x12, 0x51, 0x10, + 0x53, 0x0e, 0x55, 0x0c, 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x08, 0x5c, 0x07, + 0x5f, 0x07, 0x60, 0x06, 0x61, 0x06, 0x62, 0x06, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x71, 0x02, 0x5b, 0x05, 0x4b, 0x0a, 0x3f, 0x0f, 0x36, 0x13, 0x2e, 0x18, 0x29, 0x1c, 0x23, + 0x20, 0x20, 0x23, 0x1d, 0x27, 0x19, 0x2a, 0x17, 0x2d, 0x16, 0x30, 0x12, 0x33, 0x12, 0x35, 0x10, + 0x38, 0x0f, 0x3b, 0x0e, 0x3c, 0x0c, 0x3e, 0x0c, 0x41, 0x38, 0x43, 0x2d, 0x44, 0x25, 0x45, 0x1f, + 0x49, 0x1b, 0x4b, 0x17, 0x4d, 0x14, 0x4f, 0x11, 0x51, 0x10, 0x53, 0x0e, 0x55, 0x0c, 0x57, 0x0b, + 0x58, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5b, 0x08, 0x5e, 0x07, 0x5f, 0x07, 0x60, 0x06, 0x61, 0x06, + 0x00, 0x00, 0x0a, 0x00, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, + 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x44, 0x00, 0x55, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x75, 0x00, 0x78, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, + 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, + 0x42, 0x2f, 0x44, 0x27, 0x46, 0x22, 0x47, 0x1d, 0x4a, 0x19, 0x4c, 0x16, 0x4e, 0x13, 0x4f, 0x11, + 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x58, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5b, 0x09, + 0x5c, 0x07, 0x5e, 0x07, 0x5f, 0x07, 0x5f, 0x06, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x73, 0x01, 0x5e, 0x05, 0x4f, 0x08, 0x44, 0x0c, 0x3a, 0x10, 0x33, 0x15, 0x2d, 0x19, 0x28, + 0x1d, 0x23, 0x20, 0x20, 0x23, 0x1d, 0x26, 0x1a, 0x2a, 0x18, 0x2c, 0x16, 0x2f, 0x14, 0x31, 0x12, + 0x34, 0x12, 0x35, 0x0f, 0x38, 0x0f, 0x3b, 0x0f, 0x41, 0x39, 0x42, 0x2f, 0x44, 0x27, 0x45, 0x22, + 0x48, 0x1d, 0x4a, 0x19, 0x4c, 0x16, 0x4d, 0x14, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, + 0x56, 0x0c, 0x58, 0x0b, 0x59, 0x0a, 0x59, 0x09, 0x5c, 0x09, 0x5c, 0x07, 0x5e, 0x07, 0x5f, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, + 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x00, 0x66, 0x00, 0x3f, 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, + 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x55, 0x00, 0x20, 0x00, 0x3e, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x63, 0x00, 0x6a, 0x00, + 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x00, 0x66, 0x00, 0x3f, 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, + 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x42, 0x30, 0x44, 0x29, 0x45, 0x23, 0x46, 0x1f, 0x49, 0x1b, 0x4b, 0x18, 0x4c, 0x15, 0x4d, 0x13, + 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0b, 0x59, 0x0b, 0x59, 0x09, + 0x5b, 0x09, 0x5c, 0x09, 0x5c, 0x07, 0x5e, 0x07, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x73, 0x01, 0x61, 0x04, 0x52, 0x07, 0x47, 0x0b, 0x3e, 0x0f, 0x37, 0x12, 0x31, 0x16, 0x2b, + 0x19, 0x27, 0x1d, 0x23, 0x20, 0x20, 0x23, 0x1d, 0x26, 0x1b, 0x29, 0x19, 0x2b, 0x16, 0x2e, 0x16, + 0x30, 0x13, 0x32, 0x12, 0x35, 0x12, 0x36, 0x0f, 0x41, 0x39, 0x42, 0x30, 0x44, 0x29, 0x44, 0x23, + 0x47, 0x1f, 0x49, 0x1b, 0x4b, 0x18, 0x4c, 0x15, 0x4e, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, + 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0b, 0x57, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x5c, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, + 0x00, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x01, 0x01, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x67, 0x00, 0x3e, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, + 0x00, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x01, 0x01, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, + 0x42, 0x32, 0x44, 0x2a, 0x45, 0x25, 0x46, 0x21, 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x17, 0x4c, 0x15, + 0x4e, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x55, 0x0c, 0x57, 0x0c, 0x57, 0x0b, + 0x5a, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x74, 0x01, 0x64, 0x04, 0x55, 0x06, 0x4a, 0x0a, 0x42, 0x0d, 0x3a, 0x10, 0x34, 0x14, 0x2f, + 0x17, 0x2a, 0x1a, 0x26, 0x1d, 0x23, 0x21, 0x21, 0x22, 0x1d, 0x26, 0x1b, 0x28, 0x19, 0x2b, 0x18, + 0x2c, 0x16, 0x30, 0x15, 0x30, 0x12, 0x33, 0x12, 0x41, 0x3a, 0x42, 0x32, 0x44, 0x2a, 0x44, 0x25, + 0x46, 0x21, 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x17, 0x4d, 0x15, 0x4e, 0x13, 0x50, 0x11, 0x52, 0x10, + 0x53, 0x0e, 0x55, 0x0d, 0x55, 0x0c, 0x56, 0x0c, 0x58, 0x0b, 0x5a, 0x0a, 0x5a, 0x09, 0x5b, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, + 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x79, 0x00, 0x6e, 0x00, 0x50, 0x00, 0x28, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, + 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, + 0x42, 0x32, 0x43, 0x2c, 0x44, 0x26, 0x45, 0x22, 0x48, 0x1f, 0x49, 0x1b, 0x4b, 0x19, 0x4c, 0x16, + 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0e, 0x55, 0x0c, 0x56, 0x0c, + 0x57, 0x0b, 0x59, 0x0b, 0x5a, 0x0a, 0x5a, 0x09, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x75, 0x01, 0x65, 0x03, 0x58, 0x05, 0x4d, 0x09, 0x45, 0x0c, 0x3e, 0x0f, 0x37, 0x12, 0x32, + 0x16, 0x2d, 0x18, 0x2a, 0x1b, 0x26, 0x1d, 0x22, 0x21, 0x21, 0x22, 0x1d, 0x26, 0x1c, 0x27, 0x19, + 0x2b, 0x19, 0x2b, 0x16, 0x2e, 0x16, 0x30, 0x14, 0x41, 0x3a, 0x42, 0x32, 0x43, 0x2c, 0x44, 0x26, + 0x46, 0x22, 0x48, 0x1f, 0x49, 0x1b, 0x4a, 0x19, 0x4c, 0x16, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x11, + 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0e, 0x54, 0x0c, 0x57, 0x0c, 0x57, 0x0b, 0x59, 0x0b, 0x5a, 0x0a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, + 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, + 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7b, 0x00, 0x72, 0x00, 0x5a, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x16, 0x00, 0x28, 0x00, + 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, + 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x42, 0x33, 0x43, 0x2d, 0x44, 0x28, 0x45, 0x23, 0x47, 0x20, 0x48, 0x1d, 0x4a, 0x1a, 0x4a, 0x18, + 0x4c, 0x16, 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0e, 0x54, 0x0d, + 0x57, 0x0c, 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x0b, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x76, 0x01, 0x67, 0x02, 0x5a, 0x05, 0x50, 0x07, 0x47, 0x0a, 0x40, 0x0d, 0x3a, 0x10, 0x35, + 0x12, 0x30, 0x16, 0x2c, 0x19, 0x29, 0x1b, 0x26, 0x1d, 0x22, 0x21, 0x21, 0x22, 0x1d, 0x26, 0x1d, + 0x26, 0x1a, 0x2a, 0x19, 0x2b, 0x17, 0x2d, 0x16, 0x41, 0x3b, 0x42, 0x33, 0x43, 0x2d, 0x44, 0x28, + 0x45, 0x23, 0x47, 0x20, 0x48, 0x1d, 0x49, 0x1a, 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x14, 0x4f, 0x13, + 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x54, 0x0e, 0x55, 0x0d, 0x57, 0x0c, 0x57, 0x0b, 0x58, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, + 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, + 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, + 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, + 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, + 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, + 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, + 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, 0x45, 0x25, 0x46, 0x21, 0x48, 0x1e, 0x49, 0x1b, 0x4a, 0x19, + 0x4c, 0x17, 0x4d, 0x15, 0x4e, 0x14, 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x53, 0x0e, + 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0c, 0x57, 0x0b, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x77, 0x00, 0x68, 0x02, 0x5c, 0x05, 0x52, 0x07, 0x4a, 0x0a, 0x43, 0x0c, 0x3d, 0x0f, 0x37, + 0x12, 0x33, 0x14, 0x2f, 0x16, 0x2b, 0x19, 0x28, 0x1c, 0x26, 0x1d, 0x22, 0x21, 0x22, 0x21, 0x1e, + 0x25, 0x1d, 0x26, 0x1a, 0x29, 0x19, 0x2b, 0x18, 0x41, 0x3b, 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, + 0x45, 0x25, 0x46, 0x21, 0x48, 0x1e, 0x48, 0x1b, 0x4b, 0x19, 0x4c, 0x17, 0x4d, 0x15, 0x4e, 0x14, + 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x51, 0x0f, 0x54, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, + 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, + 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, + 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, + 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, + 0x42, 0x34, 0x43, 0x2f, 0x44, 0x2a, 0x44, 0x26, 0x46, 0x22, 0x47, 0x1f, 0x48, 0x1d, 0x49, 0x1a, + 0x4b, 0x18, 0x4c, 0x17, 0x4e, 0x15, 0x4e, 0x13, 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x51, 0x0f, + 0x54, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0c, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x77, 0x00, 0x69, 0x02, 0x5e, 0x04, 0x55, 0x06, 0x4c, 0x09, 0x45, 0x0c, 0x3f, 0x0e, 0x3a, + 0x10, 0x35, 0x12, 0x31, 0x16, 0x2e, 0x18, 0x2b, 0x19, 0x27, 0x1d, 0x26, 0x1e, 0x22, 0x21, 0x22, + 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1b, 0x28, 0x19, 0x41, 0x3b, 0x42, 0x34, 0x43, 0x2f, 0x43, 0x2a, + 0x45, 0x26, 0x46, 0x22, 0x47, 0x1f, 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x18, 0x4c, 0x17, 0x4e, 0x15, + 0x4e, 0x13, 0x50, 0x13, 0x50, 0x11, 0x51, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0d, 0x56, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, + 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, + 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, + 0x42, 0x35, 0x43, 0x30, 0x44, 0x2b, 0x44, 0x27, 0x45, 0x24, 0x46, 0x21, 0x48, 0x1e, 0x48, 0x1c, + 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x15, 0x4e, 0x13, 0x50, 0x12, 0x50, 0x11, 0x51, 0x11, + 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, 0x55, 0x0c, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x77, 0x00, 0x6b, 0x02, 0x60, 0x04, 0x57, 0x05, 0x4f, 0x07, 0x48, 0x0a, 0x42, 0x0c, 0x3c, + 0x0f, 0x38, 0x12, 0x34, 0x14, 0x30, 0x16, 0x2c, 0x19, 0x2b, 0x19, 0x26, 0x1d, 0x25, 0x1e, 0x22, + 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1c, 0x41, 0x3b, 0x42, 0x35, 0x43, 0x30, 0x43, 0x2b, + 0x44, 0x27, 0x45, 0x24, 0x46, 0x21, 0x47, 0x1e, 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x16, + 0x4e, 0x15, 0x4e, 0x13, 0x50, 0x12, 0x50, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x42, 0x36, 0x43, 0x30, 0x44, 0x2c, 0x44, 0x28, 0x45, 0x25, 0x46, 0x22, 0x48, 0x1f, 0x48, 0x1d, + 0x49, 0x1a, 0x4b, 0x19, 0x4c, 0x18, 0x4d, 0x15, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x12, 0x50, 0x11, + 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x77, 0x00, 0x6c, 0x02, 0x61, 0x04, 0x58, 0x05, 0x51, 0x07, 0x4a, 0x0a, 0x44, 0x0c, 0x3f, + 0x0f, 0x3a, 0x10, 0x35, 0x12, 0x32, 0x16, 0x30, 0x16, 0x2b, 0x19, 0x2a, 0x1a, 0x26, 0x1d, 0x25, + 0x1e, 0x22, 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x41, 0x3b, 0x42, 0x36, 0x43, 0x30, 0x43, 0x2c, + 0x44, 0x28, 0x45, 0x25, 0x46, 0x22, 0x47, 0x1f, 0x49, 0x1d, 0x49, 0x1a, 0x4b, 0x19, 0x4c, 0x18, + 0x4d, 0x15, 0x4e, 0x15, 0x4f, 0x13, 0x4f, 0x12, 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, + 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, + 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, + 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, + 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, + 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, + 0x42, 0x36, 0x42, 0x31, 0x43, 0x2d, 0x44, 0x29, 0x45, 0x26, 0x46, 0x23, 0x47, 0x20, 0x47, 0x1e, + 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x17, 0x4d, 0x15, 0x4e, 0x14, 0x4f, 0x13, 0x4f, 0x12, + 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x78, 0x00, 0x6d, 0x01, 0x62, 0x03, 0x5a, 0x05, 0x52, 0x07, 0x4c, 0x09, 0x46, 0x0b, 0x41, + 0x0c, 0x3c, 0x0f, 0x38, 0x12, 0x35, 0x12, 0x30, 0x16, 0x2e, 0x17, 0x2b, 0x19, 0x29, 0x1b, 0x26, + 0x1d, 0x25, 0x1e, 0x22, 0x21, 0x22, 0x21, 0x1f, 0x41, 0x3c, 0x42, 0x36, 0x42, 0x31, 0x43, 0x2d, + 0x44, 0x29, 0x45, 0x26, 0x46, 0x23, 0x46, 0x20, 0x48, 0x1e, 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, + 0x4c, 0x17, 0x4d, 0x15, 0x4e, 0x14, 0x4e, 0x13, 0x50, 0x12, 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, + 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, + 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, + 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, + 0x42, 0x37, 0x42, 0x32, 0x43, 0x2d, 0x44, 0x2a, 0x45, 0x27, 0x45, 0x23, 0x46, 0x21, 0x47, 0x1f, + 0x49, 0x1d, 0x49, 0x1a, 0x4b, 0x19, 0x4c, 0x18, 0x4c, 0x16, 0x4e, 0x15, 0x4e, 0x14, 0x4f, 0x13, + 0x50, 0x12, 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x78, 0x00, 0x6e, 0x01, 0x64, 0x02, 0x5b, 0x04, 0x54, 0x06, 0x4e, 0x07, 0x47, 0x0a, 0x42, + 0x0c, 0x3e, 0x0f, 0x3b, 0x0f, 0x35, 0x12, 0x33, 0x14, 0x30, 0x16, 0x2d, 0x19, 0x2b, 0x19, 0x28, + 0x1c, 0x26, 0x1d, 0x25, 0x1f, 0x22, 0x21, 0x22, 0x41, 0x3c, 0x42, 0x37, 0x42, 0x32, 0x43, 0x2d, + 0x44, 0x2a, 0x45, 0x27, 0x45, 0x23, 0x46, 0x21, 0x48, 0x1f, 0x49, 0x1d, 0x49, 0x1a, 0x4b, 0x19, + 0x4c, 0x18, 0x4c, 0x16, 0x4e, 0x15, 0x4d, 0x14, 0x50, 0x13, 0x50, 0x12, 0x51, 0x11, 0x52, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, + 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, + 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, + 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, + 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x61, 0x20, 0x4d, 0x2a, 0x48, 0x30, 0x44, 0x33, + 0x44, 0x35, 0x44, 0x36, 0x43, 0x37, 0x42, 0x38, 0x43, 0x39, 0x42, 0x39, 0x42, 0x3a, 0x42, 0x3a, + 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x41, 0x3b, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, + 0x04, 0x3d, 0x22, 0x3d, 0x2b, 0x3d, 0x30, 0x3d, 0x33, 0x3d, 0x35, 0x3d, 0x37, 0x3d, 0x38, 0x3d, + 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, + 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, + 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, + 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, + 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x6c, 0x0b, 0x57, 0x16, 0x4f, 0x1d, 0x4a, 0x22, + 0x48, 0x26, 0x47, 0x29, 0x46, 0x2b, 0x44, 0x2d, 0x44, 0x2f, 0x44, 0x30, 0x44, 0x31, 0x44, 0x32, + 0x43, 0x33, 0x43, 0x34, 0x43, 0x34, 0x42, 0x35, 0x43, 0x36, 0x42, 0x36, 0x42, 0x37, 0x42, 0x37, + 0x00, 0x5d, 0x0c, 0x49, 0x16, 0x43, 0x1d, 0x41, 0x22, 0x40, 0x26, 0x3f, 0x29, 0x3f, 0x2c, 0x3f, + 0x2e, 0x3e, 0x2f, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, + 0x35, 0x3e, 0x36, 0x3e, 0x37, 0x3e, 0x37, 0x3e, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, + 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x72, 0x06, 0x5f, 0x0d, 0x56, 0x14, 0x4f, 0x19, + 0x4d, 0x1d, 0x4a, 0x20, 0x49, 0x23, 0x47, 0x25, 0x46, 0x27, 0x46, 0x29, 0x45, 0x2a, 0x45, 0x2c, + 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2f, 0x43, 0x30, 0x44, 0x30, 0x44, 0x31, 0x43, 0x32, 0x43, 0x33, + 0x00, 0x68, 0x06, 0x54, 0x0d, 0x4b, 0x14, 0x47, 0x19, 0x44, 0x1d, 0x43, 0x20, 0x41, 0x23, 0x41, + 0x26, 0x40, 0x27, 0x40, 0x29, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x2f, 0x3f, + 0x30, 0x3f, 0x30, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, + 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, + 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, + 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x75, 0x03, 0x64, 0x09, 0x5a, 0x0e, 0x54, 0x13, + 0x51, 0x17, 0x4e, 0x1a, 0x4c, 0x1d, 0x49, 0x1f, 0x49, 0x22, 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, + 0x46, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, 0x44, 0x2c, 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2e, + 0x00, 0x6d, 0x04, 0x5b, 0x09, 0x51, 0x0e, 0x4c, 0x13, 0x48, 0x17, 0x46, 0x1a, 0x44, 0x1d, 0x43, + 0x1f, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, + 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1f, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, + 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x77, 0x02, 0x68, 0x06, 0x5f, 0x0b, 0x58, 0x0f, + 0x54, 0x12, 0x51, 0x15, 0x4f, 0x18, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1f, 0x49, 0x21, 0x48, 0x22, + 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x45, 0x27, 0x45, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, + 0x00, 0x70, 0x02, 0x60, 0x07, 0x56, 0x0b, 0x50, 0x0f, 0x4c, 0x12, 0x49, 0x16, 0x47, 0x18, 0x46, + 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x43, 0x21, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, 0x26, 0x40, + 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, + 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x78, 0x02, 0x6b, 0x05, 0x62, 0x08, 0x5b, 0x0c, + 0x57, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4e, 0x17, 0x4d, 0x19, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, + 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, 0x46, 0x24, 0x46, 0x25, 0x46, 0x26, 0x46, 0x27, 0x45, 0x27, + 0x00, 0x72, 0x02, 0x64, 0x05, 0x5b, 0x08, 0x54, 0x0c, 0x50, 0x0f, 0x4d, 0x12, 0x4a, 0x14, 0x48, + 0x17, 0x47, 0x19, 0x46, 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x44, 0x20, 0x42, 0x21, 0x42, 0x22, 0x42, + 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, 0x27, 0x40, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x00, 0x3f, 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, + 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x3f, 0x00, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, + 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x79, 0x01, 0x6d, 0x04, 0x65, 0x07, 0x5e, 0x0a, + 0x5a, 0x0d, 0x56, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4f, 0x16, 0x4e, 0x18, 0x4c, 0x1a, 0x4b, 0x1b, + 0x4b, 0x1d, 0x4a, 0x1e, 0x49, 0x1f, 0x48, 0x21, 0x48, 0x22, 0x48, 0x23, 0x47, 0x23, 0x46, 0x25, + 0x00, 0x74, 0x01, 0x66, 0x04, 0x5e, 0x07, 0x57, 0x0a, 0x53, 0x0d, 0x4f, 0x10, 0x4d, 0x12, 0x4b, + 0x14, 0x49, 0x16, 0x48, 0x18, 0x46, 0x1a, 0x45, 0x1b, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x20, 0x43, + 0x21, 0x42, 0x22, 0x42, 0x23, 0x42, 0x24, 0x41, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x01, 0x01, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, + 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7a, 0x01, 0x6f, 0x03, 0x67, 0x06, 0x60, 0x08, + 0x5d, 0x0b, 0x59, 0x0d, 0x56, 0x10, 0x53, 0x11, 0x51, 0x14, 0x50, 0x15, 0x4e, 0x17, 0x4d, 0x19, + 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x48, 0x1e, 0x49, 0x1f, 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, + 0x00, 0x75, 0x01, 0x69, 0x03, 0x61, 0x06, 0x5a, 0x08, 0x56, 0x0b, 0x52, 0x0d, 0x4f, 0x10, 0x4d, + 0x12, 0x4b, 0x14, 0x4a, 0x16, 0x48, 0x17, 0x47, 0x19, 0x46, 0x1a, 0x45, 0x1c, 0x45, 0x1d, 0x44, + 0x1e, 0x44, 0x1f, 0x44, 0x20, 0x43, 0x21, 0x42, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, + 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x01, 0x71, 0x02, 0x69, 0x05, 0x62, 0x07, + 0x5f, 0x09, 0x5b, 0x0c, 0x58, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4e, 0x16, + 0x4e, 0x18, 0x4c, 0x19, 0x4c, 0x1a, 0x4a, 0x1c, 0x4b, 0x1d, 0x49, 0x1e, 0x49, 0x1f, 0x49, 0x20, + 0x00, 0x76, 0x01, 0x6b, 0x03, 0x63, 0x05, 0x5d, 0x07, 0x58, 0x09, 0x54, 0x0c, 0x52, 0x0e, 0x4f, + 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, 0x15, 0x49, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x45, + 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, + 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, + 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x00, 0x72, 0x02, 0x6b, 0x04, 0x64, 0x06, + 0x61, 0x08, 0x5d, 0x0a, 0x5a, 0x0c, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, + 0x4f, 0x16, 0x4e, 0x17, 0x4d, 0x18, 0x4b, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, 0x4b, 0x1d, 0x49, 0x1d, + 0x00, 0x77, 0x00, 0x6c, 0x02, 0x65, 0x04, 0x5f, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x54, 0x0c, 0x51, + 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, 0x15, 0x49, 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, + 0x1a, 0x46, 0x1b, 0x45, 0x1c, 0x45, 0x1d, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, + 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x73, 0x02, 0x6c, 0x03, 0x66, 0x05, + 0x63, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, + 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x17, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, + 0x00, 0x77, 0x00, 0x6e, 0x02, 0x66, 0x04, 0x61, 0x05, 0x5c, 0x07, 0x59, 0x09, 0x56, 0x0b, 0x53, + 0x0d, 0x51, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x48, 0x17, 0x48, + 0x18, 0x47, 0x19, 0x46, 0x1b, 0x46, 0x1b, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, + 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x74, 0x02, 0x6e, 0x03, 0x68, 0x05, + 0x64, 0x06, 0x60, 0x08, 0x5d, 0x0a, 0x5a, 0x0b, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, + 0x52, 0x13, 0x50, 0x14, 0x50, 0x15, 0x4d, 0x16, 0x4e, 0x18, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, + 0x00, 0x77, 0x00, 0x6f, 0x02, 0x68, 0x03, 0x63, 0x05, 0x5e, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x55, + 0x0b, 0x53, 0x0d, 0x50, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x49, + 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x19, 0x46, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, + 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x75, 0x01, 0x6f, 0x02, 0x69, 0x04, + 0x65, 0x06, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0b, 0x5a, 0x0c, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, + 0x53, 0x11, 0x52, 0x13, 0x50, 0x13, 0x4f, 0x15, 0x4e, 0x15, 0x4e, 0x17, 0x4d, 0x18, 0x4c, 0x18, + 0x00, 0x78, 0x00, 0x70, 0x01, 0x69, 0x03, 0x64, 0x04, 0x60, 0x06, 0x5c, 0x07, 0x59, 0x09, 0x56, + 0x0b, 0x54, 0x0c, 0x52, 0x0d, 0x50, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x13, 0x4a, + 0x15, 0x4a, 0x15, 0x48, 0x17, 0x48, 0x18, 0x47, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, 0x70, 0x02, 0x6a, 0x03, + 0x67, 0x05, 0x63, 0x06, 0x60, 0x08, 0x5d, 0x09, 0x5b, 0x0b, 0x59, 0x0c, 0x57, 0x0d, 0x55, 0x0e, + 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x4f, 0x13, 0x50, 0x15, 0x4f, 0x15, 0x4e, 0x16, 0x4e, 0x18, + 0x00, 0x78, 0x00, 0x71, 0x01, 0x6a, 0x03, 0x66, 0x04, 0x61, 0x05, 0x5d, 0x06, 0x5a, 0x08, 0x58, + 0x09, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0e, 0x50, 0x0f, 0x4e, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4c, + 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x49, 0x16, 0x48, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, + 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, + 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, 0x71, 0x02, 0x6c, 0x03, + 0x68, 0x05, 0x64, 0x06, 0x61, 0x07, 0x5e, 0x09, 0x5c, 0x0a, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0e, + 0x55, 0x0e, 0x55, 0x11, 0x52, 0x11, 0x51, 0x12, 0x51, 0x13, 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x15, + 0x00, 0x79, 0x00, 0x71, 0x01, 0x6c, 0x02, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5c, 0x07, 0x59, + 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, + 0x13, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, + 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, + 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x77, 0x01, 0x72, 0x02, 0x6d, 0x03, + 0x69, 0x04, 0x66, 0x06, 0x63, 0x07, 0x5f, 0x08, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0c, 0x58, 0x0c, + 0x57, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x51, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x14, 0x50, 0x15, + 0x00, 0x79, 0x00, 0x72, 0x01, 0x6d, 0x02, 0x68, 0x03, 0x63, 0x04, 0x60, 0x06, 0x5d, 0x07, 0x5b, + 0x08, 0x58, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, + 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, + 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, + 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x78, 0x01, 0x72, 0x02, 0x6e, 0x02, + 0x6a, 0x03, 0x67, 0x05, 0x64, 0x06, 0x60, 0x07, 0x5f, 0x09, 0x5c, 0x0a, 0x5b, 0x0b, 0x5a, 0x0c, + 0x57, 0x0c, 0x56, 0x0e, 0x55, 0x0f, 0x53, 0x11, 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x13, + 0x00, 0x79, 0x00, 0x73, 0x01, 0x6d, 0x02, 0x69, 0x03, 0x65, 0x04, 0x61, 0x05, 0x5e, 0x06, 0x5c, + 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0d, 0x53, 0x0d, 0x51, 0x0f, 0x50, 0x0f, 0x4e, + 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, + 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, + 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x01, 0x73, 0x02, 0x6e, 0x02, + 0x6b, 0x03, 0x68, 0x05, 0x65, 0x06, 0x62, 0x07, 0x5f, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0b, + 0x59, 0x0c, 0x57, 0x0d, 0x56, 0x0e, 0x54, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, + 0x00, 0x79, 0x00, 0x73, 0x01, 0x6e, 0x02, 0x69, 0x03, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5d, + 0x07, 0x5b, 0x08, 0x58, 0x09, 0x56, 0x0b, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, + 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x12, 0x4c, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, + 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x00, 0x74, 0x01, 0x6f, 0x02, + 0x6c, 0x03, 0x68, 0x04, 0x65, 0x05, 0x62, 0x06, 0x61, 0x07, 0x5f, 0x09, 0x5c, 0x09, 0x5b, 0x0b, + 0x5a, 0x0b, 0x58, 0x0c, 0x57, 0x0d, 0x55, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, + 0x00, 0x79, 0x00, 0x74, 0x00, 0x6f, 0x02, 0x6a, 0x03, 0x66, 0x04, 0x63, 0x05, 0x60, 0x05, 0x5e, + 0x06, 0x5b, 0x07, 0x59, 0x09, 0x58, 0x09, 0x55, 0x0b, 0x55, 0x0c, 0x53, 0x0d, 0x52, 0x0d, 0x50, + 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, + 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, + 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x79, 0x00, 0x74, 0x01, 0x70, 0x02, + 0x6c, 0x03, 0x69, 0x03, 0x66, 0x05, 0x63, 0x06, 0x62, 0x07, 0x5f, 0x07, 0x5e, 0x09, 0x5c, 0x0a, + 0x5a, 0x0b, 0x5a, 0x0c, 0x57, 0x0c, 0x56, 0x0e, 0x55, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, + 0x00, 0x79, 0x00, 0x74, 0x00, 0x70, 0x01, 0x6b, 0x02, 0x67, 0x03, 0x64, 0x04, 0x61, 0x05, 0x5f, + 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0c, 0x53, 0x0d, 0x51, + 0x0e, 0x50, 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x66, 0x7f, 0x4d, 0x68, 0x49, 0x5c, 0x47, 0x55, + 0x45, 0x51, 0x45, 0x4e, 0x44, 0x4c, 0x43, 0x4a, 0x43, 0x49, 0x43, 0x48, 0x43, 0x47, 0x42, 0x47, + 0x42, 0x46, 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x45, 0x42, 0x44, 0x42, 0x44, 0x42, 0x44, + 0x57, 0x7f, 0x2e, 0x58, 0x34, 0x4d, 0x37, 0x49, 0x39, 0x47, 0x3a, 0x45, 0x3b, 0x44, 0x3b, 0x44, + 0x3c, 0x43, 0x3c, 0x43, 0x3c, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x41, + 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x99, 0x7f, 0x4d, 0x58, 0x49, 0x4d, 0x47, 0x49, + 0x45, 0x47, 0x45, 0x45, 0x44, 0x44, 0x43, 0x44, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa5, 0x16, 0x74, 0x23, 0x62, 0x29, 0x59, 0x2d, + 0x53, 0x30, 0x50, 0x32, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x36, 0x49, 0x37, 0x48, 0x38, 0x48, 0x38, + 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x3a, 0x45, 0x3a, 0x45, 0x3a, 0x45, 0x3a, 0x44, 0x3b, + 0x23, 0x2f, 0x25, 0x35, 0x2a, 0x37, 0x2e, 0x39, 0x31, 0x3a, 0x33, 0x3b, 0x34, 0x3b, 0x35, 0x3c, + 0x37, 0x3c, 0x37, 0x3c, 0x38, 0x3d, 0x38, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, + 0x3a, 0x3d, 0x3a, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x77, 0x27, 0x5f, 0x31, 0x56, 0x35, 0x52, 0x38, + 0x4f, 0x39, 0x4c, 0x3a, 0x4b, 0x3b, 0x49, 0x3b, 0x48, 0x3c, 0x48, 0x3c, 0x47, 0x3c, 0x47, 0x3c, + 0x46, 0x3d, 0x46, 0x3d, 0x45, 0x3d, 0x45, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x44, 0x3d, 0x44, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x33, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x5f, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xbb, 0x01, 0x91, 0x09, 0x78, 0x11, 0x6b, 0x17, + 0x62, 0x1c, 0x5c, 0x20, 0x58, 0x23, 0x55, 0x25, 0x52, 0x28, 0x51, 0x2a, 0x4f, 0x2b, 0x4e, 0x2c, + 0x4d, 0x2e, 0x4c, 0x2f, 0x4b, 0x30, 0x4a, 0x31, 0x49, 0x31, 0x49, 0x32, 0x48, 0x33, 0x47, 0x33, + 0x22, 0x25, 0x23, 0x2a, 0x26, 0x2e, 0x2a, 0x31, 0x2c, 0x33, 0x2e, 0x34, 0x30, 0x36, 0x31, 0x37, + 0x32, 0x37, 0x33, 0x38, 0x34, 0x38, 0x35, 0x39, 0x36, 0x39, 0x36, 0x3a, 0x37, 0x3a, 0x37, 0x3a, + 0x38, 0x3a, 0x38, 0x3b, 0x39, 0x3b, 0x39, 0x3b, 0x7a, 0x1a, 0x68, 0x24, 0x5f, 0x2a, 0x5a, 0x2e, + 0x55, 0x31, 0x53, 0x33, 0x51, 0x34, 0x4f, 0x35, 0x4d, 0x36, 0x4c, 0x37, 0x4b, 0x38, 0x4b, 0x38, + 0x4a, 0x39, 0x49, 0x39, 0x49, 0x39, 0x48, 0x3a, 0x47, 0x3a, 0x47, 0x3a, 0x46, 0x3b, 0x46, 0x3b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x39, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x8b, 0x00, 0x44, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xc6, 0x00, 0xa3, 0x02, 0x8a, 0x07, 0x7a, 0x0c, + 0x6f, 0x11, 0x68, 0x15, 0x62, 0x18, 0x5e, 0x1b, 0x5b, 0x1d, 0x58, 0x20, 0x56, 0x22, 0x54, 0x23, + 0x53, 0x25, 0x51, 0x26, 0x50, 0x28, 0x4e, 0x29, 0x4d, 0x2a, 0x4d, 0x2b, 0x4d, 0x2c, 0x4c, 0x2c, + 0x22, 0x23, 0x22, 0x27, 0x25, 0x2a, 0x27, 0x2c, 0x2a, 0x2e, 0x2b, 0x30, 0x2d, 0x32, 0x2e, 0x33, + 0x30, 0x34, 0x31, 0x34, 0x32, 0x35, 0x33, 0x36, 0x33, 0x36, 0x34, 0x37, 0x34, 0x37, 0x35, 0x38, + 0x36, 0x38, 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x7b, 0x16, 0x6d, 0x1f, 0x65, 0x24, 0x5f, 0x28, + 0x5b, 0x2b, 0x58, 0x2d, 0x55, 0x2f, 0x54, 0x31, 0x52, 0x32, 0x50, 0x33, 0x4f, 0x34, 0x4e, 0x35, + 0x4d, 0x35, 0x4c, 0x36, 0x4b, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x4a, 0x38, 0x4a, 0x39, 0x49, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x35, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb3, 0x00, 0x9f, 0x00, 0x6d, 0x00, 0x33, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xcc, 0x00, 0xaf, 0x00, 0x98, 0x03, 0x87, 0x06, + 0x7b, 0x0a, 0x73, 0x0e, 0x6c, 0x11, 0x67, 0x13, 0x63, 0x16, 0x5f, 0x18, 0x5c, 0x1a, 0x5a, 0x1c, + 0x58, 0x1e, 0x56, 0x20, 0x55, 0x21, 0x54, 0x22, 0x52, 0x24, 0x51, 0x25, 0x50, 0x26, 0x4f, 0x27, + 0x22, 0x22, 0x22, 0x25, 0x24, 0x27, 0x26, 0x2a, 0x28, 0x2b, 0x29, 0x2d, 0x2b, 0x2f, 0x2c, 0x30, + 0x2d, 0x31, 0x2f, 0x32, 0x30, 0x33, 0x30, 0x33, 0x31, 0x34, 0x32, 0x35, 0x33, 0x35, 0x33, 0x36, + 0x33, 0x36, 0x34, 0x36, 0x35, 0x36, 0x35, 0x37, 0x7c, 0x15, 0x71, 0x1b, 0x68, 0x20, 0x63, 0x24, + 0x5f, 0x27, 0x5c, 0x29, 0x59, 0x2c, 0x57, 0x2d, 0x55, 0x2f, 0x54, 0x30, 0x53, 0x31, 0x51, 0x32, + 0x50, 0x32, 0x4f, 0x33, 0x4f, 0x34, 0x4e, 0x35, 0x4d, 0x35, 0x4c, 0x35, 0x4b, 0x35, 0x4a, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb7, 0x00, 0xaa, 0x00, 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xcf, 0x00, 0xb7, 0x00, 0xa2, 0x00, 0x92, 0x03, + 0x85, 0x06, 0x7c, 0x09, 0x75, 0x0b, 0x6f, 0x0e, 0x6a, 0x11, 0x66, 0x13, 0x63, 0x15, 0x5f, 0x17, + 0x5e, 0x19, 0x5c, 0x1a, 0x5a, 0x1c, 0x57, 0x1d, 0x56, 0x1f, 0x55, 0x1f, 0x55, 0x21, 0x54, 0x22, + 0x22, 0x22, 0x22, 0x24, 0x23, 0x26, 0x25, 0x28, 0x26, 0x29, 0x28, 0x2b, 0x29, 0x2c, 0x2b, 0x2d, + 0x2c, 0x2f, 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x33, 0x32, 0x33, + 0x32, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, 0x7c, 0x14, 0x73, 0x19, 0x6c, 0x1e, 0x67, 0x22, + 0x63, 0x24, 0x5f, 0x26, 0x5c, 0x28, 0x5a, 0x2a, 0x58, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2f, + 0x53, 0x30, 0x52, 0x31, 0x51, 0x31, 0x4f, 0x32, 0x4f, 0x33, 0x4f, 0x33, 0x4f, 0x34, 0x4e, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, 0x48, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd2, 0x00, 0xbd, 0x00, 0xaa, 0x00, 0x9b, 0x01, + 0x8e, 0x03, 0x84, 0x05, 0x7c, 0x08, 0x76, 0x0a, 0x71, 0x0c, 0x6c, 0x0f, 0x69, 0x11, 0x66, 0x12, + 0x63, 0x14, 0x60, 0x16, 0x5e, 0x17, 0x5d, 0x19, 0x5b, 0x1a, 0x59, 0x1b, 0x57, 0x1c, 0x56, 0x1e, + 0x22, 0x22, 0x22, 0x23, 0x23, 0x25, 0x24, 0x27, 0x25, 0x28, 0x27, 0x29, 0x28, 0x2b, 0x29, 0x2c, + 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2f, 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x30, 0x30, 0x32, 0x30, 0x33, + 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x7d, 0x13, 0x75, 0x18, 0x6e, 0x1c, 0x69, 0x1f, + 0x65, 0x22, 0x62, 0x24, 0x60, 0x26, 0x5c, 0x28, 0x5b, 0x29, 0x59, 0x2a, 0x57, 0x2c, 0x57, 0x2d, + 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2f, 0x53, 0x31, 0x51, 0x31, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd3, 0x00, 0xc2, 0x00, 0xb1, 0x00, 0xa2, 0x00, + 0x96, 0x02, 0x8c, 0x03, 0x83, 0x05, 0x7d, 0x07, 0x77, 0x09, 0x73, 0x0b, 0x6f, 0x0d, 0x6a, 0x0f, + 0x68, 0x11, 0x66, 0x12, 0x63, 0x14, 0x60, 0x15, 0x5f, 0x16, 0x5e, 0x18, 0x5c, 0x19, 0x5a, 0x1a, + 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, 0x26, 0x29, 0x27, 0x29, 0x28, 0x2b, + 0x29, 0x2b, 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2e, 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, + 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x33, 0x7d, 0x13, 0x76, 0x17, 0x70, 0x1b, 0x6b, 0x1e, + 0x68, 0x20, 0x65, 0x22, 0x61, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x29, 0x5a, 0x2a, 0x58, 0x2b, + 0x57, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2f, 0x53, 0x30, 0x53, 0x31, 0x51, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0xb6, 0x00, 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd4, 0x00, 0xc5, 0x00, 0xb6, 0x00, 0xa8, 0x00, + 0x9c, 0x00, 0x92, 0x02, 0x8a, 0x03, 0x82, 0x05, 0x7d, 0x07, 0x78, 0x09, 0x74, 0x0a, 0x71, 0x0c, + 0x6c, 0x0e, 0x69, 0x0f, 0x68, 0x11, 0x65, 0x12, 0x63, 0x13, 0x60, 0x14, 0x5f, 0x16, 0x5e, 0x17, + 0x21, 0x22, 0x22, 0x23, 0x22, 0x24, 0x23, 0x25, 0x24, 0x26, 0x25, 0x27, 0x27, 0x28, 0x27, 0x29, + 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2e, 0x2d, 0x2f, 0x2d, 0x30, + 0x2e, 0x30, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x31, 0x7d, 0x12, 0x77, 0x16, 0x71, 0x19, 0x6d, 0x1c, + 0x69, 0x1f, 0x66, 0x20, 0x64, 0x23, 0x61, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x28, 0x5b, 0x2a, + 0x58, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd5, 0x00, 0xc7, 0x00, 0xba, 0x00, 0xad, 0x00, + 0xa2, 0x00, 0x98, 0x01, 0x8f, 0x02, 0x88, 0x03, 0x82, 0x05, 0x7e, 0x07, 0x78, 0x08, 0x74, 0x0a, + 0x72, 0x0b, 0x6e, 0x0c, 0x6b, 0x0e, 0x69, 0x0f, 0x67, 0x11, 0x65, 0x12, 0x63, 0x13, 0x60, 0x14, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, + 0x27, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, 0x2a, 0x2d, 0x2c, 0x2d, 0x2d, 0x2e, + 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x7d, 0x12, 0x77, 0x15, 0x72, 0x18, 0x6e, 0x1b, + 0x6b, 0x1d, 0x68, 0x20, 0x65, 0x21, 0x64, 0x23, 0x60, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x27, + 0x5c, 0x29, 0x59, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x54, 0x2e, 0x53, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, + 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, 0x59, 0x00, 0x41, 0x00, + 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd6, 0x00, 0xc9, 0x00, 0xbd, 0x00, 0xb2, 0x00, + 0xa7, 0x00, 0x9d, 0x00, 0x95, 0x01, 0x8e, 0x02, 0x87, 0x03, 0x82, 0x05, 0x7e, 0x06, 0x79, 0x08, + 0x75, 0x09, 0x73, 0x0b, 0x70, 0x0c, 0x6c, 0x0d, 0x6a, 0x0e, 0x68, 0x0f, 0x67, 0x11, 0x65, 0x12, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x26, 0x25, 0x27, 0x27, 0x28, + 0x27, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2d, 0x2b, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2e, 0x2d, 0x2f, 0x2d, 0x30, 0x7d, 0x12, 0x78, 0x15, 0x73, 0x18, 0x70, 0x1a, + 0x6d, 0x1d, 0x69, 0x1e, 0x67, 0x20, 0x65, 0x21, 0x63, 0x23, 0x60, 0x24, 0x60, 0x26, 0x5d, 0x27, + 0x5c, 0x27, 0x5c, 0x28, 0x5a, 0x2a, 0x58, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1a, + 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, + 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd7, 0x00, 0xcb, 0x00, 0xc0, 0x00, 0xb5, 0x00, + 0xab, 0x00, 0xa2, 0x00, 0x9a, 0x00, 0x92, 0x02, 0x8d, 0x02, 0x87, 0x03, 0x81, 0x05, 0x7e, 0x06, + 0x79, 0x07, 0x76, 0x09, 0x74, 0x0a, 0x71, 0x0b, 0x6e, 0x0c, 0x6b, 0x0d, 0x6a, 0x0e, 0x68, 0x10, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x25, 0x24, 0x25, 0x25, 0x27, 0x25, 0x27, + 0x27, 0x28, 0x27, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, + 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x7e, 0x12, 0x79, 0x15, 0x75, 0x17, 0x71, 0x1a, + 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1f, 0x65, 0x20, 0x65, 0x22, 0x62, 0x23, 0x60, 0x23, 0x60, 0x25, + 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x5c, 0x29, 0x59, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, + 0x00, 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, + 0x49, 0x00, 0x35, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd7, 0x00, 0xcc, 0x00, 0xc2, 0x00, 0xb9, 0x00, + 0xae, 0x00, 0xa6, 0x00, 0x9e, 0x00, 0x97, 0x01, 0x90, 0x02, 0x8b, 0x02, 0x85, 0x04, 0x81, 0x05, + 0x7e, 0x06, 0x7a, 0x07, 0x76, 0x08, 0x74, 0x09, 0x72, 0x0b, 0x6f, 0x0c, 0x6c, 0x0c, 0x6a, 0x0e, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, + 0x26, 0x27, 0x27, 0x28, 0x26, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x7e, 0x12, 0x79, 0x15, 0x75, 0x17, 0x72, 0x19, + 0x6e, 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x68, 0x20, 0x65, 0x20, 0x65, 0x22, 0x61, 0x23, 0x60, 0x23, + 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x5a, 0x2a, 0x58, 0x2a, 0x57, 0x2a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, + 0x00, 0x1c, 0x00, 0x16, 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, 0x7b, 0x00, 0x68, 0x00, + 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd8, 0x00, 0xce, 0x00, 0xc5, 0x00, 0xbb, 0x00, + 0xb2, 0x00, 0xaa, 0x00, 0xa1, 0x00, 0x9b, 0x00, 0x94, 0x01, 0x8f, 0x02, 0x8a, 0x03, 0x84, 0x04, + 0x81, 0x05, 0x7e, 0x06, 0x7b, 0x07, 0x77, 0x08, 0x75, 0x09, 0x73, 0x0a, 0x71, 0x0b, 0x6e, 0x0c, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, + 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x7e, 0x12, 0x7a, 0x14, 0x76, 0x16, 0x72, 0x18, + 0x6f, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, 0x64, 0x23, 0x61, 0x23, + 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x5b, 0x2a, 0x59, 0x2a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, 0x2b, 0x00, 0x26, + 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, + 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbd, 0x00, + 0xb5, 0x00, 0xad, 0x00, 0xa6, 0x00, 0x9e, 0x00, 0x99, 0x00, 0x92, 0x01, 0x8e, 0x02, 0x89, 0x03, + 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, 0x7b, 0x07, 0x77, 0x07, 0x75, 0x09, 0x73, 0x09, 0x72, 0x0b, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, + 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, + 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, 0x7e, 0x12, 0x7a, 0x14, 0x76, 0x15, 0x72, 0x18, + 0x71, 0x1a, 0x6d, 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x65, 0x21, 0x64, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x29, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, + 0x00, 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, + 0x6a, 0x00, 0x59, 0x00, 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbf, 0x00, + 0xb8, 0x00, 0xaf, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9c, 0x00, 0x96, 0x01, 0x90, 0x02, 0x8d, 0x02, + 0x88, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7b, 0x07, 0x77, 0x07, 0x76, 0x09, 0x74, 0x09, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, + 0x25, 0x26, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, 0x26, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, + 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x7e, 0x11, 0x7a, 0x13, 0x76, 0x15, 0x74, 0x18, + 0x72, 0x19, 0x6e, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1e, 0x67, 0x20, 0x65, 0x20, 0x65, 0x21, + 0x63, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x58, 0x00, 0x3d, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x42, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x0b, 0x07, 0x17, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, + 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x47, 0x07, 0x0f, 0x0f, 0x00, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd9, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc1, 0x00, + 0xba, 0x00, 0xb2, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x9a, 0x00, 0x93, 0x01, 0x8f, 0x02, + 0x8c, 0x02, 0x88, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7b, 0x06, 0x78, 0x07, 0x76, 0x08, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, + 0x25, 0x25, 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x7e, 0x11, 0x7b, 0x13, 0x77, 0x15, 0x75, 0x17, + 0x72, 0x18, 0x6f, 0x1a, 0x6d, 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x69, 0x1f, 0x65, 0x20, 0x65, 0x20, + 0x65, 0x22, 0x62, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5f, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x3d, 0x00, 0x30, 0x00, 0x39, 0x00, 0x42, 0x00, 0x41, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6f, 0x00, 0x27, 0x02, 0x02, 0x12, 0x00, 0x25, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, + 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, + 0x7f, 0x00, 0x3f, 0x00, 0x05, 0x05, 0x00, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, + 0x00, 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd9, 0x00, 0xd2, 0x00, 0xca, 0x00, 0xc3, 0x00, + 0xbb, 0x00, 0xb5, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x92, 0x01, + 0x8e, 0x02, 0x8b, 0x02, 0x87, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7c, 0x06, 0x78, 0x07, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, + 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, 0x7e, 0x11, 0x7b, 0x13, 0x77, 0x15, 0x76, 0x17, + 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x68, 0x20, 0x65, 0x20, + 0x65, 0x20, 0x65, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5f, 0x27, 0x5c, 0x27, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x31, 0x00, 0x35, + 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x4c, 0x00, 0x39, 0x00, 0x16, 0x00, 0x28, 0x00, 0x2f, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, + 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x6f, 0x00, 0x22, 0x00, 0x09, 0x0b, 0x00, 0x16, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, + 0x00, 0x33, 0x00, 0x35, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, + 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x00, 0x00, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, + 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd9, 0x00, 0xd2, 0x00, 0xca, 0x00, 0xc5, 0x00, + 0xbd, 0x00, 0xb7, 0x00, 0xb0, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9f, 0x00, 0x9b, 0x00, 0x95, 0x01, + 0x91, 0x01, 0x8e, 0x02, 0x8a, 0x02, 0x86, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7c, 0x06, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x24, + 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x7f, 0x11, 0x7b, 0x13, 0x77, 0x15, 0x76, 0x16, + 0x72, 0x18, 0x72, 0x19, 0x6d, 0x1a, 0x6d, 0x1b, 0x6b, 0x1d, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, + 0x65, 0x20, 0x65, 0x20, 0x64, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x24, 0x5f, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, + 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, + 0x00, 0x4c, 0x00, 0x42, 0x00, 0x28, 0x00, 0x09, 0x00, 0x16, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, + 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaf, 0x00, 0x93, 0x00, 0x58, 0x00, 0x21, 0x00, 0x0e, 0x08, 0x02, 0x0e, 0x00, 0x18, 0x00, 0x20, + 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00, 0x36, 0x00, 0x38, + 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x16, + 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xda, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc6, 0x00, + 0xbe, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xac, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x99, 0x00, + 0x93, 0x01, 0x90, 0x02, 0x8d, 0x02, 0x89, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, + 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x29, + 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x7f, 0x11, 0x7b, 0x13, 0x78, 0x15, 0x76, 0x15, + 0x73, 0x18, 0x72, 0x18, 0x6f, 0x1a, 0x6d, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1f, + 0x65, 0x20, 0x65, 0x20, 0x65, 0x21, 0x64, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x24, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, + 0x00, 0x25, 0x00, 0x2a, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, + 0x00, 0x48, 0x00, 0x41, 0x00, 0x2f, 0x00, 0x16, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, + 0x00, 0x25, 0x00, 0x2a, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb5, 0x00, 0xa4, 0x00, 0x7a, 0x00, 0x4a, 0x00, 0x20, 0x00, 0x12, 0x06, 0x07, 0x0c, 0x00, 0x10, + 0x00, 0x18, 0x00, 0x1f, 0x00, 0x24, 0x00, 0x28, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32, + 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, + 0x33, 0x22, 0x28, 0x22, 0x25, 0x22, 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x21, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x22, + 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x9d, 0x05, 0x6b, 0x0d, 0x51, 0x12, 0x45, 0x15, + 0x3d, 0x17, 0x38, 0x18, 0x35, 0x1a, 0x32, 0x1b, 0x30, 0x1b, 0x2f, 0x1c, 0x2d, 0x1c, 0x2d, 0x1c, + 0x2c, 0x1d, 0x2b, 0x1d, 0x2a, 0x1e, 0x2a, 0x1e, 0x29, 0x1e, 0x29, 0x1e, 0x28, 0x1e, 0x28, 0x1e, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x5f, 0x13, 0x46, 0x17, 0x39, 0x1a, 0x33, 0x1b, + 0x2f, 0x1c, 0x2d, 0x1d, 0x2b, 0x1e, 0x2a, 0x1e, 0x29, 0x1e, 0x28, 0x1f, 0x27, 0x1f, 0x27, 0x1f, + 0x27, 0x1f, 0x26, 0x1f, 0x26, 0x20, 0x26, 0x20, 0x25, 0x20, 0x25, 0x20, 0x25, 0x20, 0x25, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, + 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, + 0x00, 0x42, 0x00, 0x3d, 0x00, 0x2e, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, + 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb9, 0x00, 0xad, 0x00, 0x8f, 0x00, 0x68, 0x00, 0x42, 0x00, 0x20, 0x00, 0x14, 0x05, 0x0b, 0x0a, + 0x04, 0x0d, 0x00, 0x12, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x26, 0x00, 0x29, 0x00, 0x2c, + 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, 0x00, + 0x08, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, + 0x37, 0x28, 0x2d, 0x25, 0x28, 0x23, 0x26, 0x23, 0x25, 0x23, 0x24, 0x22, 0x24, 0x22, 0x23, 0x22, + 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xbc, 0x00, 0x8e, 0x01, 0x73, 0x04, 0x60, 0x08, + 0x55, 0x0b, 0x4c, 0x0d, 0x47, 0x0f, 0x42, 0x11, 0x3e, 0x12, 0x3b, 0x13, 0x39, 0x14, 0x37, 0x15, + 0x36, 0x16, 0x34, 0x16, 0x33, 0x17, 0x32, 0x18, 0x31, 0x18, 0x30, 0x19, 0x2f, 0x19, 0x2f, 0x19, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x6f, 0x11, 0x58, 0x11, 0x4a, 0x13, 0x41, 0x15, + 0x3b, 0x16, 0x37, 0x17, 0x34, 0x18, 0x32, 0x19, 0x30, 0x1a, 0x2e, 0x1a, 0x2d, 0x1b, 0x2c, 0x1b, + 0x2c, 0x1c, 0x2b, 0x1c, 0x2a, 0x1c, 0x2a, 0x1d, 0x29, 0x1d, 0x29, 0x1d, 0x28, 0x1d, 0x28, 0x1d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2d, + 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xba, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x7d, 0x00, 0x5c, 0x00, 0x3c, 0x00, 0x20, 0x00, 0x16, 0x04, + 0x0e, 0x08, 0x07, 0x0c, 0x02, 0x0e, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, + 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, 0x00, + 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, + 0x38, 0x2c, 0x30, 0x28, 0x2b, 0x26, 0x29, 0x25, 0x27, 0x24, 0x26, 0x24, 0x25, 0x23, 0x24, 0x23, + 0x24, 0x23, 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xc6, 0x00, 0xa4, 0x00, 0x89, 0x00, 0x77, 0x02, + 0x67, 0x04, 0x5e, 0x06, 0x55, 0x08, 0x50, 0x0a, 0x4b, 0x0c, 0x47, 0x0d, 0x44, 0x0e, 0x41, 0x0f, + 0x3e, 0x10, 0x3d, 0x11, 0x3b, 0x12, 0x39, 0x12, 0x38, 0x13, 0x37, 0x14, 0x35, 0x15, 0x35, 0x16, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x74, 0x11, 0x63, 0x11, 0x55, 0x11, 0x4c, 0x12, + 0x44, 0x13, 0x40, 0x14, 0x3b, 0x15, 0x39, 0x16, 0x36, 0x17, 0x34, 0x17, 0x33, 0x18, 0x31, 0x18, + 0x30, 0x19, 0x2f, 0x19, 0x2e, 0x1a, 0x2d, 0x1a, 0x2d, 0x1a, 0x2c, 0x1b, 0x2b, 0x1b, 0x2b, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0xb5, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x6f, 0x00, 0x52, 0x00, 0x37, 0x00, 0x20, 0x00, + 0x17, 0x04, 0x10, 0x07, 0x0a, 0x0a, 0x05, 0x0d, 0x00, 0x0f, 0x00, 0x14, 0x00, 0x19, 0x00, 0x1d, + 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, 0x00, + 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, + 0x3a, 0x2f, 0x32, 0x2b, 0x2e, 0x28, 0x2b, 0x27, 0x28, 0x25, 0x27, 0x25, 0x27, 0x24, 0x25, 0x24, + 0x25, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, 0x23, 0x22, 0x23, 0x22, + 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0xcd, 0x00, 0xb0, 0x00, 0x9a, 0x00, 0x87, 0x00, + 0x78, 0x01, 0x6c, 0x03, 0x64, 0x04, 0x5c, 0x05, 0x57, 0x07, 0x51, 0x09, 0x4e, 0x0a, 0x4a, 0x0b, + 0x48, 0x0c, 0x45, 0x0c, 0x43, 0x0e, 0x40, 0x0f, 0x3f, 0x0f, 0x3d, 0x0f, 0x3c, 0x11, 0x3b, 0x12, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x77, 0x11, 0x69, 0x11, 0x5e, 0x11, 0x54, 0x11, + 0x4d, 0x11, 0x47, 0x12, 0x43, 0x13, 0x3f, 0x13, 0x3c, 0x14, 0x39, 0x15, 0x38, 0x16, 0x36, 0x16, + 0x35, 0x17, 0x33, 0x17, 0x32, 0x18, 0x31, 0x18, 0x30, 0x18, 0x2f, 0x18, 0x2f, 0x19, 0x2e, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0xb8, 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7e, 0x00, 0x64, 0x00, 0x4c, 0x00, 0x34, 0x00, + 0x20, 0x00, 0x18, 0x03, 0x11, 0x06, 0x0c, 0x09, 0x07, 0x0c, 0x03, 0x0e, 0x00, 0x10, 0x00, 0x15, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, 0x00, + 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x3b, 0x32, 0x34, 0x2d, 0x2f, 0x2a, 0x2c, 0x28, 0x2a, 0x27, 0x29, 0x26, 0x27, 0x25, 0x27, 0x25, + 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x22, 0xcf, 0x00, 0xb9, 0x00, 0xa4, 0x00, 0x94, 0x00, + 0x85, 0x00, 0x7a, 0x01, 0x70, 0x02, 0x68, 0x03, 0x61, 0x04, 0x5c, 0x05, 0x57, 0x07, 0x53, 0x07, + 0x50, 0x09, 0x4c, 0x0a, 0x4a, 0x0a, 0x48, 0x0c, 0x45, 0x0c, 0x44, 0x0c, 0x42, 0x0d, 0x41, 0x0f, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x78, 0x11, 0x6d, 0x11, 0x63, 0x11, 0x5b, 0x11, + 0x53, 0x11, 0x4e, 0x11, 0x49, 0x12, 0x45, 0x12, 0x41, 0x13, 0x3f, 0x13, 0x3c, 0x14, 0x3a, 0x14, + 0x39, 0x15, 0x37, 0x16, 0x36, 0x16, 0x35, 0x17, 0x33, 0x17, 0x33, 0x17, 0x32, 0x17, 0x31, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, 0x1a, 0x00, 0x1d, + 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, 0x1a, 0x00, 0x1d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9d, 0x00, 0x89, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x46, 0x00, + 0x32, 0x00, 0x20, 0x00, 0x19, 0x03, 0x13, 0x06, 0x0e, 0x08, 0x09, 0x0b, 0x05, 0x0d, 0x01, 0x0e, + 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, 0x00, + 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, 0x00, + 0x3c, 0x34, 0x35, 0x2f, 0x31, 0x2c, 0x2e, 0x2a, 0x2c, 0x28, 0x2a, 0x27, 0x28, 0x27, 0x28, 0x26, + 0x27, 0x25, 0x27, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x23, + 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, 0xd2, 0x00, 0xbe, 0x00, 0xad, 0x00, 0x9d, 0x00, + 0x90, 0x00, 0x84, 0x00, 0x7a, 0x00, 0x72, 0x01, 0x6b, 0x02, 0x65, 0x04, 0x60, 0x04, 0x5b, 0x05, + 0x58, 0x06, 0x54, 0x07, 0x51, 0x07, 0x4f, 0x09, 0x4c, 0x0a, 0x4a, 0x0a, 0x48, 0x0b, 0x46, 0x0c, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7a, 0x11, 0x70, 0x11, 0x67, 0x11, 0x5f, 0x11, + 0x59, 0x11, 0x53, 0x11, 0x4e, 0x11, 0x4a, 0x11, 0x46, 0x12, 0x43, 0x13, 0x41, 0x13, 0x3e, 0x13, + 0x3d, 0x14, 0x3b, 0x14, 0x39, 0x14, 0x38, 0x15, 0x37, 0x16, 0x36, 0x16, 0x35, 0x16, 0x34, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, + 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, + 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x92, 0x00, 0x7e, 0x00, 0x6a, 0x00, 0x56, 0x00, + 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x19, 0x03, 0x14, 0x05, 0x0f, 0x08, 0x0b, 0x0a, 0x07, 0x0c, + 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, 0x00, + 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, 0x00, + 0x3c, 0x35, 0x36, 0x31, 0x32, 0x2e, 0x2f, 0x2b, 0x2d, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x27, + 0x28, 0x27, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x23, 0x24, 0x23, 0xd3, 0x00, 0xc2, 0x00, 0xb2, 0x00, 0xa4, 0x00, + 0x98, 0x00, 0x8d, 0x00, 0x83, 0x00, 0x7b, 0x00, 0x73, 0x01, 0x6e, 0x02, 0x67, 0x02, 0x63, 0x04, + 0x5f, 0x04, 0x5b, 0x05, 0x58, 0x05, 0x55, 0x07, 0x52, 0x07, 0x50, 0x07, 0x4e, 0x09, 0x4c, 0x0a, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7a, 0x11, 0x72, 0x11, 0x6a, 0x11, 0x63, 0x11, + 0x5d, 0x11, 0x57, 0x11, 0x52, 0x11, 0x4e, 0x11, 0x4a, 0x11, 0x48, 0x12, 0x44, 0x12, 0x42, 0x13, + 0x40, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x14, 0x3a, 0x14, 0x39, 0x14, 0x38, 0x15, 0x37, 0x16, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1a, + 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa8, 0x00, 0x99, 0x00, 0x87, 0x00, 0x75, 0x00, 0x62, 0x00, + 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x00, 0x1a, 0x02, 0x15, 0x05, 0x10, 0x07, 0x0c, 0x09, + 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, 0x00, + 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, 0x00, + 0x3c, 0x36, 0x37, 0x32, 0x33, 0x2f, 0x30, 0x2d, 0x2e, 0x2b, 0x2d, 0x2a, 0x2b, 0x28, 0x2a, 0x28, + 0x28, 0x27, 0x28, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, + 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, 0xd5, 0x00, 0xc5, 0x00, 0xb7, 0x00, 0xaa, 0x00, + 0x9f, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x83, 0x00, 0x7c, 0x00, 0x75, 0x01, 0x6f, 0x01, 0x69, 0x02, + 0x66, 0x02, 0x61, 0x04, 0x5e, 0x04, 0x5a, 0x05, 0x58, 0x05, 0x55, 0x06, 0x53, 0x07, 0x51, 0x07, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7b, 0x11, 0x73, 0x11, 0x6c, 0x11, 0x66, 0x11, + 0x60, 0x11, 0x5b, 0x11, 0x56, 0x11, 0x52, 0x11, 0x4f, 0x11, 0x4b, 0x11, 0x48, 0x11, 0x45, 0x12, + 0x44, 0x12, 0x41, 0x13, 0x40, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x14, 0x3a, 0x14, 0x39, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, + 0x00, 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, 0x00, + 0x5c, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x1a, 0x02, 0x16, 0x04, 0x11, 0x06, + 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, 0x00, + 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, 0x00, + 0x3c, 0x37, 0x38, 0x33, 0x34, 0x30, 0x31, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2c, 0x2a, 0x2b, 0x28, + 0x2a, 0x28, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0xd5, 0x00, 0xc8, 0x00, 0xbb, 0x00, 0xaf, 0x00, + 0xa5, 0x00, 0x9b, 0x00, 0x92, 0x00, 0x8a, 0x00, 0x82, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x70, 0x01, + 0x6c, 0x02, 0x67, 0x02, 0x64, 0x03, 0x60, 0x04, 0x5d, 0x04, 0x5b, 0x05, 0x58, 0x05, 0x55, 0x05, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7b, 0x11, 0x75, 0x11, 0x6e, 0x11, 0x68, 0x11, + 0x63, 0x11, 0x5e, 0x11, 0x5a, 0x11, 0x56, 0x11, 0x52, 0x11, 0x4f, 0x11, 0x4c, 0x11, 0x49, 0x11, + 0x47, 0x12, 0x44, 0x12, 0x43, 0x12, 0x41, 0x13, 0x3f, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, + 0x00, 0x1c, 0x00, 0x16, 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x95, 0x00, 0x86, 0x00, 0x77, 0x00, + 0x67, 0x00, 0x57, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2c, 0x00, 0x1f, 0x00, 0x1b, 0x02, 0x16, 0x04, + 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, 0x00, + 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, 0x00, + 0x3d, 0x38, 0x38, 0x34, 0x35, 0x31, 0x33, 0x2f, 0x30, 0x2d, 0x2e, 0x2c, 0x2d, 0x2b, 0x2b, 0x2a, + 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x27, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x25, 0x27, 0x25, + 0x26, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x24, 0xd6, 0x00, 0xca, 0x00, 0xbe, 0x00, 0xb3, 0x00, + 0xaa, 0x00, 0xa0, 0x00, 0x98, 0x00, 0x90, 0x00, 0x88, 0x00, 0x82, 0x00, 0x7d, 0x00, 0x77, 0x00, + 0x71, 0x01, 0x6d, 0x01, 0x69, 0x02, 0x66, 0x02, 0x63, 0x04, 0x60, 0x04, 0x5d, 0x04, 0x5b, 0x05, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x76, 0x11, 0x70, 0x11, 0x6a, 0x11, + 0x66, 0x11, 0x61, 0x11, 0x5d, 0x11, 0x59, 0x11, 0x55, 0x11, 0x52, 0x11, 0x4f, 0x11, 0x4c, 0x11, + 0x49, 0x11, 0x47, 0x11, 0x45, 0x12, 0x44, 0x12, 0x42, 0x13, 0x41, 0x13, 0x3f, 0x13, 0x3e, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, 0x2b, 0x00, 0x26, + 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xb0, 0x00, 0xa6, 0x00, 0x9a, 0x00, 0x8d, 0x00, 0x7f, 0x00, + 0x70, 0x00, 0x61, 0x00, 0x53, 0x00, 0x45, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x1b, 0x02, + 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, 0x00, + 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, + 0x3d, 0x38, 0x39, 0x35, 0x36, 0x32, 0x33, 0x30, 0x31, 0x2e, 0x30, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, + 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, + 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb7, 0x00, + 0xad, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x96, 0x00, 0x8f, 0x00, 0x88, 0x00, 0x81, 0x00, 0x7d, 0x00, + 0x77, 0x00, 0x73, 0x01, 0x6f, 0x01, 0x6b, 0x02, 0x67, 0x02, 0x64, 0x02, 0x62, 0x04, 0x5f, 0x04, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x77, 0x11, 0x71, 0x11, 0x6c, 0x11, + 0x67, 0x11, 0x63, 0x11, 0x5f, 0x11, 0x5c, 0x11, 0x58, 0x11, 0x55, 0x11, 0x51, 0x11, 0x4f, 0x11, + 0x4c, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, 0x12, 0x44, 0x12, 0x43, 0x12, 0x42, 0x13, 0x40, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, + 0x00, 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xa9, 0x00, 0x9e, 0x00, 0x92, 0x00, 0x85, 0x00, + 0x78, 0x00, 0x6a, 0x00, 0x5c, 0x00, 0x4f, 0x00, 0x42, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, + 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, 0x00, + 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, 0x00, + 0x3d, 0x39, 0x39, 0x36, 0x36, 0x33, 0x33, 0x30, 0x32, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2a, + 0x2c, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0xd7, 0x00, 0xcc, 0x00, 0xc3, 0x00, 0xba, 0x00, + 0xb1, 0x00, 0xa8, 0x00, 0xa0, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8d, 0x00, 0x87, 0x00, 0x81, 0x00, + 0x7d, 0x00, 0x78, 0x00, 0x74, 0x00, 0x70, 0x01, 0x6c, 0x01, 0x69, 0x02, 0x66, 0x02, 0x63, 0x02, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x77, 0x11, 0x72, 0x11, 0x6e, 0x11, + 0x69, 0x11, 0x65, 0x11, 0x61, 0x11, 0x5d, 0x11, 0x5a, 0x11, 0x57, 0x11, 0x54, 0x11, 0x51, 0x11, + 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, 0x45, 0x12, 0x44, 0x12, 0x42, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x39, 0x39, 0x36, 0x36, 0x33, 0x34, 0x32, 0x33, 0x30, 0x30, 0x2e, 0x30, 0x2d, 0x2d, 0x2c, + 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x27, 0x29, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x25, 0xd7, 0x00, 0xce, 0x00, 0xc5, 0x00, 0xbc, 0x00, + 0xb4, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x91, 0x00, 0x8c, 0x00, 0x87, 0x00, + 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x75, 0x00, 0x71, 0x01, 0x6d, 0x01, 0x6b, 0x02, 0x68, 0x02, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x78, 0x11, 0x73, 0x11, 0x6f, 0x11, + 0x6b, 0x11, 0x67, 0x11, 0x63, 0x11, 0x60, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x57, 0x11, 0x54, 0x11, + 0x51, 0x11, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, 0x46, 0x12, 0x45, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x39, 0x3a, 0x36, 0x37, 0x34, 0x35, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2d, 0x2e, 0x2d, + 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x27, + 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbe, 0x00, + 0xb6, 0x00, 0xaf, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x90, 0x00, 0x8a, 0x00, + 0x86, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x75, 0x00, 0x72, 0x01, 0x6f, 0x01, 0x6b, 0x01, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x78, 0x11, 0x74, 0x11, 0x70, 0x11, + 0x6c, 0x11, 0x68, 0x11, 0x65, 0x11, 0x61, 0x11, 0x5f, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x56, 0x11, + 0x54, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3a, 0x3a, 0x37, 0x38, 0x35, 0x36, 0x33, 0x33, 0x31, 0x32, 0x30, 0x30, 0x2e, 0x30, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2b, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, + 0x28, 0x27, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0xd8, 0x00, 0xd0, 0x00, 0xc8, 0x00, 0xc0, 0x00, + 0xb9, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa5, 0x00, 0x9f, 0x00, 0x99, 0x00, 0x94, 0x00, 0x8f, 0x00, + 0x8a, 0x00, 0x86, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x76, 0x00, 0x73, 0x00, 0x70, 0x01, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x79, 0x11, 0x75, 0x11, 0x71, 0x11, + 0x6d, 0x11, 0x6a, 0x11, 0x66, 0x11, 0x63, 0x11, 0x60, 0x11, 0x5d, 0x11, 0x5b, 0x11, 0x58, 0x11, + 0x56, 0x11, 0x54, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4d, 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x49, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3a, 0x3a, 0x37, 0x38, 0x36, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x2d, + 0x2f, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x29, 0x27, 0xd9, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc2, 0x00, + 0xbb, 0x00, 0xb4, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa2, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x92, 0x00, + 0x8e, 0x00, 0x89, 0x00, 0x85, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x76, 0x00, 0x73, 0x00, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x79, 0x11, 0x75, 0x11, 0x72, 0x11, + 0x6e, 0x11, 0x6b, 0x11, 0x68, 0x11, 0x65, 0x11, 0x62, 0x11, 0x5f, 0x11, 0x5c, 0x11, 0x5a, 0x11, + 0x58, 0x11, 0x55, 0x11, 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, 0x4c, 0x11, 0x4a, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x36, 0x33, 0x34, 0x33, 0x33, 0x30, 0x32, 0x30, 0x30, 0x2e, + 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, + 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, + 0xbc, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9a, 0x00, 0x96, 0x00, + 0x91, 0x00, 0x8d, 0x00, 0x89, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x77, 0x00, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x79, 0x11, 0x76, 0x11, 0x72, 0x11, + 0x6f, 0x11, 0x6c, 0x11, 0x69, 0x11, 0x66, 0x11, 0x63, 0x11, 0x61, 0x11, 0x5e, 0x11, 0x5c, 0x11, + 0x59, 0x11, 0x57, 0x11, 0x55, 0x11, 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, 0x4c, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3a, 0x3b, 0x39, 0x39, 0x36, 0x36, 0x34, 0x35, 0x33, 0x33, 0x31, 0x33, 0x30, 0x30, 0x30, + 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, + 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0xd9, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc4, 0x00, + 0xbe, 0x00, 0xb8, 0x00, 0xb2, 0x00, 0xad, 0x00, 0xa8, 0x00, 0xa2, 0x00, 0x9d, 0x00, 0x99, 0x00, + 0x94, 0x00, 0x90, 0x00, 0x8b, 0x00, 0x88, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7b, 0x00, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x7a, 0x11, 0x76, 0x11, 0x73, 0x11, + 0x70, 0x11, 0x6d, 0x11, 0x6a, 0x11, 0x67, 0x11, 0x65, 0x11, 0x62, 0x11, 0x5f, 0x11, 0x5d, 0x11, + 0x5b, 0x11, 0x59, 0x11, 0x56, 0x11, 0x55, 0x11, 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3b, 0x3b, 0x39, 0x3a, 0x36, 0x36, 0x35, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x31, 0x30, + 0x30, 0x2f, 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x28, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x28, 0xd9, 0x00, 0xd3, 0x00, 0xcc, 0x00, 0xc6, 0x00, + 0xc0, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9b, 0x00, + 0x97, 0x00, 0x93, 0x00, 0x8f, 0x00, 0x8b, 0x00, 0x88, 0x00, 0x83, 0x00, 0x81, 0x00, 0x7d, 0x00, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x7a, 0x11, 0x77, 0x11, 0x74, 0x11, + 0x71, 0x11, 0x6e, 0x11, 0x6b, 0x11, 0x68, 0x11, 0x66, 0x11, 0x63, 0x11, 0x61, 0x11, 0x5e, 0x11, + 0x5c, 0x11, 0x5a, 0x11, 0x58, 0x11, 0x56, 0x11, 0x55, 0x11, 0x52, 0x11, 0x51, 0x11, 0x4f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3b, 0x3b, 0x39, 0x39, 0x36, 0x37, 0x36, 0x36, 0x33, 0x34, 0x33, 0x33, 0x31, 0x32, 0x30, + 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc7, 0x00, + 0xc1, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa7, 0x00, 0xa2, 0x00, 0x9e, 0x00, + 0x9a, 0x00, 0x95, 0x00, 0x93, 0x00, 0x8e, 0x00, 0x8b, 0x00, 0x87, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x7a, 0x11, 0x77, 0x11, 0x74, 0x11, + 0x71, 0x11, 0x6f, 0x11, 0x6c, 0x11, 0x69, 0x11, 0x67, 0x11, 0x64, 0x11, 0x62, 0x11, 0x60, 0x11, + 0x5e, 0x11, 0x5b, 0x11, 0x5a, 0x11, 0x58, 0x11, 0x56, 0x11, 0x54, 0x11, 0x52, 0x11, 0x51, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x98, 0x00, 0xb8, 0x00, 0xc5, 0x00, 0xcb, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, + 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, 0x00, + 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x44, 0x00, 0x9d, 0x00, 0xbc, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xcf, 0x00, 0xd2, 0x00, 0xd3, 0x00, + 0xd5, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, + 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0x90, 0x00, 0xbd, 0x00, 0xcc, 0x00, 0xd1, 0x00, + 0xd5, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, + 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x01, 0x8b, 0x00, 0xa0, 0x00, 0xad, 0x00, 0xb6, 0x00, 0xbc, 0x00, 0xc1, 0x00, 0xc4, 0x00, + 0xc7, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, + 0xd2, 0x00, 0xd2, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x25, 0x05, 0x6b, 0x00, 0x8e, 0x00, 0xa4, 0x00, 0xb0, 0x00, 0xb9, 0x00, 0xbe, 0x00, 0xc2, 0x00, + 0xc5, 0x00, 0xc8, 0x00, 0xca, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, + 0xd1, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0x81, 0x02, 0xa4, 0x00, 0xb5, 0x00, 0xc0, 0x00, + 0xc6, 0x00, 0xcb, 0x00, 0xcd, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, + 0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x56, 0x09, 0x72, 0x02, 0x87, 0x00, 0x96, 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb0, 0x00, 0xb5, 0x00, + 0xb9, 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc9, 0x00, + 0xca, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x23, 0x0d, 0x51, 0x01, 0x73, 0x00, 0x89, 0x00, 0x9a, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, + 0xb7, 0x00, 0xbb, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc8, 0x00, + 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0x80, 0x06, 0x97, 0x00, 0xa8, 0x00, 0xb3, 0x00, + 0xbb, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc7, 0x00, 0xca, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, + 0xd0, 0x00, 0xd1, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4f, 0x11, 0x65, 0x07, 0x76, 0x03, 0x84, 0x01, 0x8f, 0x00, 0x99, 0x00, 0xa1, 0x00, 0xa7, 0x00, + 0xad, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb8, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc0, 0x00, + 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x12, 0x45, 0x04, 0x60, 0x00, 0x77, 0x00, 0x87, 0x00, 0x94, 0x00, 0x9d, 0x00, 0xa4, 0x00, + 0xaa, 0x00, 0xaf, 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, + 0xc2, 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0x7f, 0x09, 0x91, 0x02, 0x9e, 0x00, 0xaa, 0x00, + 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc6, 0x00, 0xc8, 0x00, 0xca, 0x00, + 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd1, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4b, 0x17, 0x5d, 0x0c, 0x6c, 0x06, 0x78, 0x03, 0x83, 0x01, 0x8c, 0x00, 0x94, 0x00, 0x9b, 0x00, + 0xa1, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0xba, 0x00, + 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x15, 0x3d, 0x08, 0x55, 0x02, 0x67, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x98, 0x00, + 0x9f, 0x00, 0xa5, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb9, 0x00, + 0xbb, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0x7f, 0x0a, 0x8d, 0x04, 0x99, 0x01, 0xa2, 0x00, + 0xaa, 0x00, 0xb1, 0x00, 0xb6, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc3, 0x00, 0xc5, 0x00, + 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x1c, 0x57, 0x11, 0x64, 0x0a, 0x70, 0x06, 0x79, 0x03, 0x82, 0x02, 0x8a, 0x00, 0x91, 0x00, + 0x97, 0x00, 0x9c, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, + 0xb4, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x17, 0x38, 0x0b, 0x4c, 0x04, 0x5e, 0x01, 0x6c, 0x00, 0x7a, 0x00, 0x84, 0x00, 0x8d, 0x00, + 0x95, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, + 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0x7f, 0x0b, 0x8a, 0x05, 0x94, 0x02, 0x9d, 0x00, + 0xa4, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc1, 0x00, + 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x20, 0x54, 0x15, 0x5f, 0x0e, 0x69, 0x09, 0x72, 0x05, 0x7a, 0x03, 0x82, 0x02, 0x88, 0x01, + 0x8f, 0x00, 0x94, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa8, 0x00, 0xac, 0x00, + 0xae, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x18, 0x35, 0x0d, 0x47, 0x06, 0x55, 0x03, 0x64, 0x01, 0x70, 0x00, 0x7a, 0x00, 0x83, 0x00, + 0x8b, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, + 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0x7f, 0x0c, 0x89, 0x06, 0x92, 0x03, 0x99, 0x01, + 0xa0, 0x00, 0xa6, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbd, 0x00, + 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x23, 0x50, 0x18, 0x5b, 0x11, 0x64, 0x0b, 0x6c, 0x08, 0x74, 0x05, 0x7b, 0x03, 0x82, 0x02, + 0x87, 0x01, 0x8c, 0x00, 0x91, 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, + 0xa7, 0x00, 0xab, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1a, 0x32, 0x0f, 0x42, 0x08, 0x50, 0x04, 0x5c, 0x02, 0x68, 0x00, 0x72, 0x00, 0x7b, 0x00, + 0x83, 0x00, 0x8a, 0x00, 0x90, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, + 0xa8, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0x7f, 0x0d, 0x88, 0x07, 0x8f, 0x04, 0x96, 0x02, + 0x9c, 0x01, 0xa2, 0x00, 0xa7, 0x00, 0xac, 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb9, 0x00, + 0xbb, 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc6, 0x00, + 0x2f, 0x00, 0x5f, 0x00, 0x99, 0x00, 0xac, 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, + 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, + 0x3b, 0x0b, 0x6f, 0x00, 0x9f, 0x00, 0xaf, 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbc, 0x00, + 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x6d, 0x00, 0x8d, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0xb2, 0x00, 0xba, 0x00, 0xbb, 0x00, + 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, + 0x47, 0x07, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, + 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, + 0x45, 0x25, 0x4e, 0x1b, 0x57, 0x13, 0x60, 0x0e, 0x68, 0x0a, 0x6f, 0x07, 0x76, 0x05, 0x7b, 0x03, + 0x82, 0x02, 0x86, 0x02, 0x8b, 0x01, 0x90, 0x00, 0x93, 0x00, 0x97, 0x00, 0x9b, 0x00, 0x9e, 0x00, + 0xa1, 0x00, 0xa3, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x1b, 0x30, 0x11, 0x3e, 0x0a, 0x4b, 0x05, 0x57, 0x03, 0x61, 0x01, 0x6b, 0x00, 0x73, 0x00, + 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8f, 0x00, 0x93, 0x00, 0x97, 0x00, 0x9c, 0x00, 0x9f, 0x00, + 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0x7f, 0x0d, 0x86, 0x08, 0x8d, 0x05, 0x94, 0x02, + 0x9a, 0x01, 0x9f, 0x00, 0xa4, 0x00, 0xa8, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb6, 0x00, + 0xb8, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, + 0x00, 0x00, 0x0f, 0x00, 0x5f, 0x00, 0x8b, 0x00, 0x9f, 0x00, 0xaa, 0x00, 0xb0, 0x00, 0xb3, 0x00, + 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, + 0x07, 0x17, 0x27, 0x02, 0x6f, 0x00, 0x93, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb5, 0x00, + 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6d, 0x00, 0x54, 0x00, 0x6b, 0x00, 0x87, 0x00, 0x98, 0x00, 0xa6, 0x00, 0xb0, 0x00, 0xb3, 0x00, + 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, + 0x0f, 0x0f, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, 0x00, + 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, + 0x45, 0x28, 0x4d, 0x1d, 0x55, 0x16, 0x5d, 0x11, 0x64, 0x0c, 0x6b, 0x09, 0x71, 0x07, 0x77, 0x05, + 0x7b, 0x03, 0x81, 0x02, 0x85, 0x02, 0x8a, 0x01, 0x8e, 0x00, 0x92, 0x00, 0x95, 0x00, 0x98, 0x00, + 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1b, 0x2f, 0x12, 0x3b, 0x0c, 0x47, 0x07, 0x51, 0x04, 0x5c, 0x02, 0x65, 0x01, 0x6e, 0x00, + 0x75, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8d, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, + 0x9c, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa5, 0x00, 0x7f, 0x0d, 0x86, 0x09, 0x8c, 0x06, 0x92, 0x03, + 0x97, 0x02, 0x9c, 0x01, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, + 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x44, 0x00, 0x6d, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, + 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, 0x00, + 0x00, 0x2c, 0x02, 0x12, 0x22, 0x00, 0x58, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9c, 0x00, 0xa4, 0x00, + 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8d, 0x00, 0x6b, 0x00, 0x28, 0x00, 0x52, 0x00, 0x70, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, + 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, 0x00, + 0x00, 0x26, 0x05, 0x05, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, 0x00, + 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0x44, 0x2a, 0x4b, 0x20, 0x53, 0x18, 0x5a, 0x13, 0x61, 0x0f, 0x67, 0x0b, 0x6d, 0x09, 0x73, 0x07, + 0x77, 0x05, 0x7c, 0x03, 0x81, 0x02, 0x85, 0x02, 0x88, 0x01, 0x8d, 0x01, 0x90, 0x00, 0x93, 0x00, + 0x96, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x1c, 0x2d, 0x13, 0x39, 0x0d, 0x44, 0x09, 0x4e, 0x05, 0x57, 0x04, 0x60, 0x02, 0x67, 0x01, + 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, 0x00, 0x94, 0x00, + 0x97, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0xa0, 0x00, 0x7f, 0x0e, 0x85, 0x09, 0x8b, 0x06, 0x90, 0x04, + 0x95, 0x02, 0x9a, 0x02, 0x9e, 0x01, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0xb2, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x33, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, + 0x8e, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0x00, 0x36, 0x00, 0x25, 0x09, 0x0b, 0x21, 0x00, 0x4a, 0x00, 0x68, 0x00, 0x7d, 0x00, 0x8b, 0x00, + 0x96, 0x00, 0x9d, 0x00, 0xa3, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x87, 0x00, 0x52, 0x00, 0x11, 0x00, 0x39, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, + 0x8e, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0x00, 0x33, 0x00, 0x1c, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, 0x00, + 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, + 0x43, 0x2b, 0x4a, 0x22, 0x51, 0x1a, 0x57, 0x15, 0x5e, 0x11, 0x63, 0x0d, 0x69, 0x0a, 0x6e, 0x08, + 0x74, 0x06, 0x78, 0x05, 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, 0x8c, 0x01, 0x8f, 0x00, + 0x92, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9b, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1c, 0x2d, 0x14, 0x37, 0x0e, 0x41, 0x0a, 0x4a, 0x07, 0x53, 0x04, 0x5b, 0x02, 0x63, 0x01, + 0x69, 0x00, 0x70, 0x00, 0x77, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x87, 0x00, 0x8a, 0x00, 0x8f, 0x00, + 0x92, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x7f, 0x0e, 0x85, 0x0a, 0x8a, 0x07, 0x8f, 0x05, + 0x93, 0x03, 0x98, 0x02, 0x9c, 0x01, 0xa0, 0x00, 0xa3, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, 0x00, + 0xaf, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x28, 0x00, 0x48, 0x00, 0x5f, 0x00, + 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, + 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x16, 0x0e, 0x08, 0x20, 0x00, 0x42, 0x00, 0x5c, 0x00, 0x6f, 0x00, + 0x7e, 0x00, 0x89, 0x00, 0x92, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa6, 0x00, 0x98, 0x00, 0x70, 0x00, 0x39, 0x00, 0x02, 0x00, 0x28, 0x00, 0x48, 0x00, 0x5f, 0x00, + 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, + 0x00, 0x38, 0x00, 0x2a, 0x00, 0x09, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, 0x00, + 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, 0x00, + 0x43, 0x2c, 0x49, 0x23, 0x50, 0x1c, 0x56, 0x17, 0x5b, 0x12, 0x61, 0x0f, 0x66, 0x0c, 0x6b, 0x0a, + 0x6f, 0x08, 0x75, 0x06, 0x78, 0x05, 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, 0x8b, 0x01, + 0x8e, 0x01, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x1c, 0x2c, 0x15, 0x36, 0x0f, 0x3e, 0x0b, 0x48, 0x07, 0x50, 0x05, 0x58, 0x04, 0x5f, 0x02, + 0x66, 0x01, 0x6c, 0x00, 0x71, 0x00, 0x77, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, + 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x97, 0x00, 0x7f, 0x0e, 0x84, 0x0a, 0x89, 0x07, 0x8d, 0x05, + 0x92, 0x03, 0x96, 0x02, 0x9a, 0x02, 0x9e, 0x01, 0xa1, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xaa, 0x00, + 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xba, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x3d, 0x00, + 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, 0x93, 0x00, 0x98, 0x00, + 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x02, 0x0e, 0x12, 0x06, 0x20, 0x00, 0x3c, 0x00, 0x52, 0x00, + 0x64, 0x00, 0x73, 0x00, 0x7e, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb2, 0x00, 0xa6, 0x00, 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, + 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, 0x93, 0x00, 0x98, 0x00, + 0x00, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, 0x00, + 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, + 0x43, 0x2e, 0x48, 0x25, 0x4e, 0x1e, 0x54, 0x19, 0x59, 0x14, 0x5f, 0x11, 0x63, 0x0e, 0x69, 0x0b, + 0x6c, 0x09, 0x71, 0x07, 0x76, 0x06, 0x78, 0x05, 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x86, 0x02, + 0x8a, 0x01, 0x8e, 0x01, 0x90, 0x00, 0x92, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1d, 0x2b, 0x16, 0x34, 0x10, 0x3d, 0x0c, 0x45, 0x09, 0x4c, 0x06, 0x54, 0x04, 0x5b, 0x02, + 0x61, 0x02, 0x67, 0x01, 0x6d, 0x00, 0x73, 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x86, 0x00, + 0x89, 0x00, 0x8d, 0x00, 0x90, 0x00, 0x93, 0x00, 0x7f, 0x0e, 0x84, 0x0b, 0x88, 0x08, 0x8d, 0x06, + 0x91, 0x04, 0x94, 0x03, 0x98, 0x02, 0x9c, 0x01, 0x9f, 0x01, 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, + 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, + 0x35, 0x00, 0x48, 0x00, 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, + 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x18, 0x07, 0x0c, 0x14, 0x05, 0x20, 0x00, 0x37, 0x00, + 0x4c, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x86, 0x00, 0x8d, 0x00, 0x92, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, 0x48, 0x00, 0x21, 0x00, 0x01, 0x00, 0x1c, 0x00, + 0x35, 0x00, 0x48, 0x00, 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, + 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, 0x00, + 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, 0x00, + 0x42, 0x2f, 0x48, 0x26, 0x4d, 0x20, 0x52, 0x1a, 0x58, 0x16, 0x5c, 0x12, 0x61, 0x0f, 0x66, 0x0c, + 0x6b, 0x0b, 0x6d, 0x09, 0x73, 0x07, 0x76, 0x06, 0x79, 0x05, 0x7d, 0x04, 0x81, 0x03, 0x83, 0x02, + 0x86, 0x02, 0x89, 0x02, 0x8d, 0x01, 0x8f, 0x01, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x1d, 0x2a, 0x16, 0x33, 0x11, 0x3b, 0x0c, 0x43, 0x0a, 0x4a, 0x07, 0x51, 0x05, 0x58, 0x04, + 0x5e, 0x02, 0x64, 0x01, 0x69, 0x01, 0x6f, 0x00, 0x74, 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, + 0x85, 0x00, 0x89, 0x00, 0x8b, 0x00, 0x8f, 0x00, 0x7f, 0x0e, 0x83, 0x0b, 0x88, 0x08, 0x8c, 0x06, + 0x90, 0x05, 0x93, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x01, 0xa0, 0x00, 0xa3, 0x00, 0xa6, 0x00, + 0xa8, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, + 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x20, 0x00, 0x10, 0x0b, 0x0a, 0x16, 0x04, 0x20, 0x00, + 0x34, 0x00, 0x46, 0x00, 0x56, 0x00, 0x62, 0x00, 0x6d, 0x00, 0x77, 0x00, 0x7f, 0x00, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, + 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, 0x00, + 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, 0x00, + 0x42, 0x30, 0x47, 0x28, 0x4c, 0x21, 0x51, 0x1c, 0x56, 0x17, 0x5a, 0x14, 0x60, 0x11, 0x63, 0x0e, + 0x68, 0x0c, 0x6c, 0x0a, 0x6f, 0x08, 0x74, 0x07, 0x77, 0x06, 0x79, 0x05, 0x7d, 0x04, 0x81, 0x03, + 0x83, 0x02, 0x85, 0x02, 0x88, 0x02, 0x8c, 0x01, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1e, 0x2a, 0x17, 0x32, 0x12, 0x39, 0x0e, 0x40, 0x0a, 0x48, 0x07, 0x4f, 0x05, 0x55, 0x04, + 0x5a, 0x03, 0x60, 0x02, 0x66, 0x01, 0x6b, 0x00, 0x70, 0x00, 0x75, 0x00, 0x79, 0x00, 0x7d, 0x00, + 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x7f, 0x0f, 0x83, 0x0b, 0x87, 0x09, 0x8b, 0x07, + 0x8e, 0x05, 0x92, 0x03, 0x96, 0x02, 0x99, 0x02, 0x9b, 0x01, 0x9e, 0x01, 0xa1, 0x00, 0xa4, 0x00, + 0xa6, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, 0x60, 0x00, 0x6a, 0x00, + 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x33, 0x00, 0x27, 0x00, 0x18, 0x04, 0x0d, 0x0e, 0x08, 0x17, 0x04, + 0x20, 0x00, 0x32, 0x00, 0x42, 0x00, 0x50, 0x00, 0x5c, 0x00, 0x67, 0x00, 0x70, 0x00, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0xb6, 0x00, 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, + 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, 0x60, 0x00, 0x6a, 0x00, + 0x00, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x08, 0x00, 0x1c, 0x00, 0x2e, 0x00, + 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, 0x00, + 0x42, 0x31, 0x47, 0x29, 0x4b, 0x22, 0x50, 0x1d, 0x55, 0x19, 0x58, 0x15, 0x5e, 0x12, 0x61, 0x0f, + 0x65, 0x0d, 0x6a, 0x0b, 0x6c, 0x09, 0x70, 0x08, 0x75, 0x07, 0x77, 0x06, 0x79, 0x05, 0x7d, 0x04, + 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, 0x87, 0x02, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1e, 0x29, 0x18, 0x31, 0x12, 0x38, 0x0f, 0x3f, 0x0c, 0x45, 0x09, 0x4c, 0x07, 0x52, 0x05, + 0x58, 0x04, 0x5d, 0x02, 0x63, 0x02, 0x67, 0x01, 0x6c, 0x00, 0x71, 0x00, 0x75, 0x00, 0x79, 0x00, + 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x7f, 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8a, 0x07, + 0x8e, 0x06, 0x91, 0x04, 0x94, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x01, 0xa0, 0x01, 0xa2, 0x00, + 0xa4, 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb2, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x12, 0x07, 0x0c, 0x10, 0x07, + 0x18, 0x03, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4c, 0x00, 0x57, 0x00, 0x61, 0x00, 0x6a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, + 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x0f, 0x00, 0x20, 0x00, + 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, 0x00, + 0x42, 0x31, 0x46, 0x2a, 0x4a, 0x24, 0x4f, 0x1f, 0x53, 0x1a, 0x58, 0x16, 0x5c, 0x13, 0x60, 0x11, + 0x63, 0x0e, 0x67, 0x0c, 0x6b, 0x0b, 0x6d, 0x09, 0x72, 0x07, 0x75, 0x07, 0x77, 0x06, 0x79, 0x05, + 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1e, 0x29, 0x18, 0x30, 0x13, 0x37, 0x0f, 0x3d, 0x0c, 0x44, 0x0a, 0x4a, 0x07, 0x50, 0x05, + 0x55, 0x04, 0x5b, 0x04, 0x60, 0x02, 0x64, 0x01, 0x69, 0x01, 0x6d, 0x00, 0x72, 0x00, 0x76, 0x00, + 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x83, 0x00, 0x7f, 0x0f, 0x83, 0x0c, 0x86, 0x09, 0x8a, 0x07, + 0x8d, 0x06, 0x90, 0x05, 0x93, 0x03, 0x96, 0x02, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, 0x00, + 0xa3, 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x02, 0x0e, 0x0a, 0x0a, + 0x11, 0x06, 0x19, 0x03, 0x1f, 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x53, 0x00, 0x5c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, 0x59, 0x00, 0x41, 0x00, + 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x04, 0x00, 0x14, 0x00, + 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, + 0x42, 0x32, 0x46, 0x2b, 0x49, 0x25, 0x4f, 0x1f, 0x52, 0x1b, 0x57, 0x18, 0x59, 0x14, 0x5f, 0x12, + 0x62, 0x0f, 0x65, 0x0d, 0x6a, 0x0c, 0x6c, 0x0a, 0x6e, 0x09, 0x73, 0x07, 0x76, 0x06, 0x78, 0x06, + 0x7a, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1e, 0x28, 0x19, 0x2f, 0x14, 0x35, 0x0f, 0x3c, 0x0c, 0x42, 0x0a, 0x48, 0x07, 0x4e, 0x06, + 0x53, 0x05, 0x58, 0x04, 0x5d, 0x02, 0x62, 0x02, 0x66, 0x01, 0x6b, 0x01, 0x6f, 0x00, 0x73, 0x00, + 0x76, 0x00, 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x7f, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x07, + 0x8c, 0x06, 0x8f, 0x05, 0x92, 0x03, 0x95, 0x03, 0x98, 0x02, 0x9a, 0x02, 0x9d, 0x01, 0x9f, 0x01, + 0xa1, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x39, 0x00, + 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x13, 0x05, 0x0d, + 0x0c, 0x09, 0x13, 0x06, 0x19, 0x03, 0x1f, 0x00, 0x2d, 0x00, 0x3a, 0x00, 0x45, 0x00, 0x4f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, + 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x39, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x0a, 0x00, + 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, 0x00, + 0x42, 0x33, 0x45, 0x2c, 0x48, 0x26, 0x4e, 0x21, 0x50, 0x1c, 0x56, 0x19, 0x58, 0x16, 0x5c, 0x13, + 0x60, 0x11, 0x62, 0x0e, 0x67, 0x0c, 0x6a, 0x0b, 0x6d, 0x0a, 0x70, 0x09, 0x74, 0x07, 0x76, 0x06, + 0x78, 0x06, 0x7a, 0x05, 0x7e, 0x04, 0x81, 0x03, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1e, 0x28, 0x19, 0x2f, 0x15, 0x35, 0x11, 0x3b, 0x0d, 0x41, 0x0b, 0x46, 0x09, 0x4c, 0x07, + 0x51, 0x05, 0x55, 0x04, 0x5b, 0x04, 0x5f, 0x02, 0x63, 0x02, 0x68, 0x01, 0x6b, 0x00, 0x70, 0x00, + 0x73, 0x00, 0x77, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x7f, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x08, + 0x8c, 0x06, 0x8f, 0x05, 0x91, 0x04, 0x94, 0x03, 0x97, 0x02, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, + 0xa0, 0x01, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xad, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, + 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x23, 0x00, 0x19, 0x00, 0x0f, + 0x07, 0x0c, 0x0e, 0x08, 0x14, 0x05, 0x1a, 0x02, 0x1f, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x42, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, + 0x49, 0x00, 0x35, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, + 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x01, 0x00, + 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, 0x00, + 0x5d, 0x1a, 0x6d, 0x16, 0x72, 0x15, 0x75, 0x14, 0x77, 0x13, 0x78, 0x13, 0x79, 0x13, 0x7a, 0x12, + 0x7a, 0x12, 0x7b, 0x12, 0x7b, 0x12, 0x7b, 0x12, 0x7c, 0x12, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, + 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7d, 0x11, 0xbd, 0x02, 0xa4, 0x06, 0x97, 0x09, 0x91, 0x0a, + 0x8d, 0x0b, 0x8a, 0x0c, 0x89, 0x0d, 0x88, 0x0d, 0x86, 0x0d, 0x86, 0x0e, 0x85, 0x0e, 0x85, 0x0e, + 0x84, 0x0e, 0x84, 0x0e, 0x83, 0x0f, 0x83, 0x0f, 0x83, 0x0f, 0x83, 0x0f, 0x82, 0x0f, 0x82, 0x0f, + 0x33, 0x11, 0x5f, 0x11, 0x6f, 0x11, 0x74, 0x11, 0x77, 0x11, 0x78, 0x11, 0x7a, 0x11, 0x7a, 0x11, + 0x7b, 0x11, 0x7b, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7d, 0x11, 0x7d, 0x11, + 0x7d, 0x11, 0x7d, 0x11, 0x7d, 0x11, 0x7d, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1d, 0x00, 0x14, + 0x03, 0x0e, 0x09, 0x0b, 0x0f, 0x08, 0x15, 0x05, 0x1a, 0x02, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, 0x7b, 0x00, 0x68, 0x00, + 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, + 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, + 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, 0x00, + 0x50, 0x24, 0x5f, 0x1f, 0x66, 0x1b, 0x6b, 0x19, 0x6e, 0x18, 0x70, 0x17, 0x73, 0x16, 0x73, 0x15, + 0x75, 0x15, 0x76, 0x15, 0x76, 0x15, 0x77, 0x14, 0x77, 0x14, 0x78, 0x13, 0x78, 0x13, 0x79, 0x13, + 0x79, 0x13, 0x7a, 0x13, 0x7a, 0x13, 0x7b, 0x13, 0xcc, 0x00, 0xb5, 0x00, 0xa8, 0x02, 0x9e, 0x04, + 0x99, 0x05, 0x94, 0x06, 0x92, 0x07, 0x8f, 0x08, 0x8d, 0x09, 0x8c, 0x09, 0x8b, 0x0a, 0x8a, 0x0a, + 0x89, 0x0b, 0x88, 0x0b, 0x88, 0x0b, 0x87, 0x0c, 0x87, 0x0c, 0x86, 0x0c, 0x86, 0x0c, 0x86, 0x0c, + 0x23, 0x13, 0x46, 0x11, 0x58, 0x11, 0x63, 0x11, 0x69, 0x11, 0x6d, 0x11, 0x70, 0x11, 0x72, 0x11, + 0x73, 0x11, 0x75, 0x11, 0x76, 0x11, 0x77, 0x11, 0x77, 0x11, 0x78, 0x11, 0x78, 0x11, 0x79, 0x11, + 0x79, 0x11, 0x79, 0x11, 0x7a, 0x11, 0x7a, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, + 0x00, 0x10, 0x05, 0x0d, 0x0b, 0x0a, 0x10, 0x07, 0x16, 0x04, 0x1b, 0x02, 0x1f, 0x00, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, + 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, + 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x00, + 0x4a, 0x2a, 0x57, 0x24, 0x5f, 0x20, 0x64, 0x1e, 0x68, 0x1c, 0x6a, 0x1b, 0x6d, 0x19, 0x6e, 0x18, + 0x70, 0x18, 0x72, 0x17, 0x72, 0x17, 0x73, 0x16, 0x74, 0x15, 0x75, 0x15, 0x76, 0x15, 0x76, 0x15, + 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, 0x77, 0x14, 0xd1, 0x00, 0xc0, 0x00, 0xb3, 0x00, 0xaa, 0x01, + 0xa2, 0x02, 0x9d, 0x03, 0x99, 0x04, 0x96, 0x05, 0x94, 0x06, 0x92, 0x06, 0x90, 0x07, 0x8f, 0x07, + 0x8d, 0x08, 0x8d, 0x08, 0x8c, 0x09, 0x8b, 0x09, 0x8a, 0x09, 0x8a, 0x0a, 0x89, 0x0a, 0x89, 0x0b, + 0x22, 0x17, 0x39, 0x11, 0x4a, 0x11, 0x55, 0x11, 0x5e, 0x11, 0x63, 0x11, 0x67, 0x11, 0x6a, 0x11, + 0x6c, 0x11, 0x6e, 0x11, 0x70, 0x11, 0x71, 0x11, 0x72, 0x11, 0x73, 0x11, 0x74, 0x11, 0x75, 0x11, + 0x75, 0x11, 0x76, 0x11, 0x76, 0x11, 0x77, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x25, 0x00, 0x1d, + 0x00, 0x15, 0x01, 0x0e, 0x07, 0x0c, 0x0c, 0x09, 0x11, 0x06, 0x16, 0x04, 0x1b, 0x02, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, + 0x6a, 0x00, 0x59, 0x00, 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, + 0x00, 0x07, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, 0x00, + 0x48, 0x2e, 0x53, 0x28, 0x5a, 0x24, 0x5f, 0x22, 0x63, 0x1f, 0x66, 0x1e, 0x69, 0x1c, 0x6a, 0x1b, + 0x6c, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x70, 0x18, 0x71, 0x18, 0x72, 0x18, 0x72, 0x17, 0x72, 0x17, + 0x73, 0x16, 0x74, 0x15, 0x75, 0x15, 0x76, 0x15, 0xd5, 0x00, 0xc6, 0x00, 0xbb, 0x00, 0xb2, 0x00, + 0xaa, 0x00, 0xa4, 0x01, 0xa0, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x97, 0x04, 0x95, 0x05, 0x93, 0x05, + 0x92, 0x06, 0x91, 0x06, 0x90, 0x07, 0x8e, 0x07, 0x8e, 0x07, 0x8d, 0x07, 0x8c, 0x08, 0x8c, 0x09, + 0x22, 0x1a, 0x33, 0x13, 0x41, 0x11, 0x4c, 0x11, 0x54, 0x11, 0x5b, 0x11, 0x5f, 0x11, 0x63, 0x11, + 0x66, 0x11, 0x68, 0x11, 0x6a, 0x11, 0x6c, 0x11, 0x6e, 0x11, 0x6f, 0x11, 0x70, 0x11, 0x71, 0x11, + 0x72, 0x11, 0x72, 0x11, 0x73, 0x11, 0x74, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x07, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, + 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x07, 0x0f, 0x0f, 0x00, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x31, 0x50, 0x2b, 0x56, 0x27, 0x5b, 0x24, 0x5f, 0x22, 0x62, 0x20, 0x65, 0x1f, 0x67, 0x1d, + 0x69, 0x1d, 0x6a, 0x1b, 0x6c, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x6f, 0x18, 0x71, 0x18, + 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x72, 0x17, 0xd6, 0x00, 0xcb, 0x00, 0xc0, 0x00, 0xb8, 0x00, + 0xb1, 0x00, 0xab, 0x00, 0xa6, 0x01, 0xa2, 0x01, 0x9f, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x98, 0x03, + 0x96, 0x04, 0x94, 0x05, 0x93, 0x05, 0x92, 0x06, 0x91, 0x06, 0x90, 0x06, 0x8f, 0x06, 0x8f, 0x07, + 0x22, 0x1b, 0x2f, 0x15, 0x3b, 0x12, 0x44, 0x11, 0x4d, 0x11, 0x53, 0x11, 0x59, 0x11, 0x5d, 0x11, + 0x60, 0x11, 0x63, 0x11, 0x66, 0x11, 0x67, 0x11, 0x69, 0x11, 0x6b, 0x11, 0x6c, 0x11, 0x6d, 0x11, + 0x6e, 0x11, 0x6f, 0x11, 0x70, 0x11, 0x71, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0f, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, 0x00, + 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x3f, 0x00, 0x05, 0x05, 0x00, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, + 0x00, 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x33, 0x4d, 0x2e, 0x53, 0x29, 0x58, 0x26, 0x5c, 0x24, 0x5f, 0x22, 0x61, 0x21, 0x65, 0x20, + 0x65, 0x1e, 0x68, 0x1d, 0x69, 0x1d, 0x6a, 0x1b, 0x6b, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, + 0x6e, 0x19, 0x6f, 0x18, 0x71, 0x18, 0x72, 0x18, 0xd7, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbd, 0x00, + 0xb6, 0x00, 0xb0, 0x00, 0xab, 0x00, 0xa7, 0x00, 0xa4, 0x01, 0xa1, 0x02, 0x9e, 0x02, 0x9c, 0x02, + 0x9a, 0x03, 0x98, 0x03, 0x97, 0x03, 0x96, 0x04, 0x94, 0x05, 0x93, 0x05, 0x92, 0x05, 0x91, 0x06, + 0x21, 0x1c, 0x2d, 0x16, 0x37, 0x13, 0x40, 0x11, 0x47, 0x11, 0x4e, 0x11, 0x53, 0x11, 0x57, 0x11, + 0x5b, 0x11, 0x5e, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x67, 0x11, 0x68, 0x11, 0x6a, 0x11, + 0x6b, 0x11, 0x6c, 0x11, 0x6d, 0x11, 0x6e, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x26, 0x05, 0x05, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, 0x00, + 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x00, 0x00, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, + 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x34, 0x4c, 0x2f, 0x51, 0x2c, 0x56, 0x28, 0x5a, 0x26, 0x5c, 0x24, 0x60, 0x23, 0x61, 0x21, + 0x64, 0x20, 0x65, 0x1f, 0x66, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, 0x6b, 0x1a, 0x6d, 0x1a, + 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0xd8, 0x00, 0xcf, 0x00, 0xc7, 0x00, 0xc0, 0x00, + 0xba, 0x00, 0xb5, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa8, 0x00, 0xa5, 0x01, 0xa2, 0x01, 0xa0, 0x02, + 0x9e, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, 0x03, 0x97, 0x03, 0x96, 0x03, 0x95, 0x04, 0x94, 0x05, + 0x22, 0x1d, 0x2b, 0x17, 0x34, 0x14, 0x3b, 0x12, 0x43, 0x11, 0x49, 0x11, 0x4e, 0x11, 0x52, 0x11, + 0x56, 0x11, 0x5a, 0x11, 0x5d, 0x11, 0x5f, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, + 0x68, 0x11, 0x69, 0x11, 0x6a, 0x11, 0x6b, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x33, 0x00, 0x1c, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, 0x00, + 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x16, + 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x36, 0x4a, 0x31, 0x50, 0x2d, 0x54, 0x2a, 0x57, 0x28, 0x5b, 0x26, 0x5c, 0x24, 0x60, 0x23, + 0x61, 0x21, 0x63, 0x20, 0x65, 0x20, 0x65, 0x1e, 0x67, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, + 0x6a, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, + 0xbe, 0x00, 0xb9, 0x00, 0xb4, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa3, 0x01, + 0xa1, 0x01, 0x9f, 0x02, 0x9d, 0x02, 0x9b, 0x02, 0x9a, 0x02, 0x99, 0x03, 0x98, 0x03, 0x97, 0x03, + 0x21, 0x1e, 0x2a, 0x18, 0x32, 0x15, 0x39, 0x13, 0x3f, 0x12, 0x45, 0x11, 0x4a, 0x11, 0x4e, 0x11, + 0x52, 0x11, 0x56, 0x11, 0x59, 0x11, 0x5c, 0x11, 0x5d, 0x11, 0x60, 0x11, 0x61, 0x11, 0x63, 0x11, + 0x65, 0x11, 0x66, 0x11, 0x67, 0x11, 0x68, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x2a, 0x00, 0x09, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, 0x00, + 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x36, 0x49, 0x32, 0x4e, 0x2f, 0x52, 0x2c, 0x56, 0x29, 0x58, 0x27, 0x5b, 0x26, 0x5d, 0x24, + 0x60, 0x23, 0x60, 0x22, 0x62, 0x20, 0x65, 0x20, 0x65, 0x1f, 0x66, 0x1e, 0x68, 0x1d, 0x69, 0x1d, + 0x69, 0x1d, 0x69, 0x1c, 0x6a, 0x1b, 0x6c, 0x1a, 0xd9, 0x00, 0xd2, 0x00, 0xcc, 0x00, 0xc6, 0x00, + 0xc1, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xb3, 0x00, 0xaf, 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, + 0xa4, 0x01, 0xa2, 0x01, 0xa0, 0x01, 0x9e, 0x02, 0x9d, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, 0x02, + 0x22, 0x1e, 0x29, 0x19, 0x30, 0x16, 0x36, 0x13, 0x3c, 0x12, 0x41, 0x11, 0x46, 0x11, 0x4a, 0x11, + 0x4f, 0x11, 0x52, 0x11, 0x55, 0x11, 0x58, 0x11, 0x5a, 0x11, 0x5c, 0x11, 0x5f, 0x11, 0x60, 0x11, + 0x62, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, 0x00, + 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, 0x00, + 0x08, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x37, 0x48, 0x33, 0x4d, 0x30, 0x51, 0x2d, 0x54, 0x2a, 0x57, 0x29, 0x59, 0x27, 0x5c, 0x26, + 0x5d, 0x24, 0x60, 0x23, 0x60, 0x22, 0x62, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x1f, 0x66, 0x1d, + 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc8, 0x00, + 0xc3, 0x00, 0xbe, 0x00, 0xba, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, + 0xa7, 0x00, 0xa5, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0xa0, 0x02, 0x9e, 0x02, 0x9d, 0x02, 0x9c, 0x02, + 0x21, 0x1e, 0x28, 0x1a, 0x2e, 0x17, 0x34, 0x14, 0x39, 0x13, 0x3f, 0x12, 0x43, 0x11, 0x48, 0x11, + 0x4b, 0x11, 0x4f, 0x11, 0x52, 0x11, 0x55, 0x11, 0x57, 0x11, 0x59, 0x11, 0x5c, 0x11, 0x5d, 0x11, + 0x5f, 0x11, 0x61, 0x11, 0x62, 0x11, 0x63, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, 0x00, + 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, 0x00, + 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x38, 0x48, 0x34, 0x4c, 0x31, 0x4f, 0x2e, 0x53, 0x2c, 0x56, 0x2a, 0x57, 0x28, 0x5a, 0x27, + 0x5c, 0x26, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, 0x65, 0x20, 0x65, 0x20, + 0x65, 0x1e, 0x67, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0xd9, 0x00, 0xd4, 0x00, 0xce, 0x00, 0xca, 0x00, + 0xc5, 0x00, 0xc1, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, + 0xaa, 0x00, 0xa8, 0x00, 0xa6, 0x00, 0xa4, 0x01, 0xa2, 0x01, 0xa0, 0x01, 0x9f, 0x02, 0x9e, 0x02, + 0x22, 0x1f, 0x27, 0x1a, 0x2d, 0x17, 0x33, 0x15, 0x38, 0x13, 0x3c, 0x13, 0x41, 0x12, 0x44, 0x11, + 0x48, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, 0x57, 0x11, 0x59, 0x11, 0x5b, 0x11, + 0x5c, 0x11, 0x5e, 0x11, 0x5f, 0x11, 0x61, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, 0x00, + 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, 0x00, + 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x38, 0x47, 0x35, 0x4b, 0x32, 0x4e, 0x2f, 0x52, 0x2d, 0x53, 0x2b, 0x57, 0x2a, 0x58, 0x27, + 0x5b, 0x27, 0x5c, 0x25, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, 0x65, 0x20, + 0x65, 0x20, 0x65, 0x1f, 0x66, 0x1e, 0x68, 0x1d, 0xda, 0x00, 0xd4, 0x00, 0xd0, 0x00, 0xcb, 0x00, + 0xc7, 0x00, 0xc2, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb5, 0x00, 0xb2, 0x00, 0xaf, 0x00, + 0xad, 0x00, 0xaa, 0x00, 0xa8, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0xa0, 0x01, + 0x21, 0x1f, 0x27, 0x1b, 0x2c, 0x18, 0x31, 0x16, 0x36, 0x14, 0x3a, 0x13, 0x3e, 0x12, 0x42, 0x11, + 0x45, 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, 0x56, 0x11, 0x58, 0x11, + 0x5a, 0x11, 0x5c, 0x11, 0x5d, 0x11, 0x5e, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x08, 0x00, 0x1c, 0x00, 0x2e, 0x00, + 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, 0x00, + 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x39, 0x46, 0x35, 0x4a, 0x32, 0x4e, 0x30, 0x50, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x29, + 0x58, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, 0x63, 0x20, + 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x1f, 0xda, 0x00, 0xd5, 0x00, 0xd1, 0x00, 0xcc, 0x00, + 0xc8, 0x00, 0xc4, 0x00, 0xc1, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb7, 0x00, 0xb4, 0x00, 0xb2, 0x00, + 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa9, 0x00, 0xa7, 0x00, 0xa5, 0x00, 0xa4, 0x01, 0xa2, 0x01, + 0x22, 0x1f, 0x27, 0x1b, 0x2c, 0x18, 0x30, 0x16, 0x35, 0x14, 0x39, 0x13, 0x3d, 0x13, 0x40, 0x12, + 0x44, 0x11, 0x47, 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, 0x56, 0x11, + 0x58, 0x11, 0x59, 0x11, 0x5b, 0x11, 0x5c, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x0f, 0x00, 0x20, 0x00, + 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, 0x00, + 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x39, 0x46, 0x36, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x2e, 0x52, 0x2d, 0x53, 0x2b, 0x57, 0x2a, + 0x57, 0x28, 0x5a, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, + 0x62, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0xda, 0x00, 0xd6, 0x00, 0xd1, 0x00, 0xcd, 0x00, + 0xc9, 0x00, 0xc6, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb3, 0x00, + 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa7, 0x00, 0xa6, 0x00, 0xa4, 0x00, + 0x21, 0x1f, 0x26, 0x1c, 0x2b, 0x19, 0x2f, 0x17, 0x33, 0x15, 0x37, 0x14, 0x3b, 0x13, 0x3e, 0x12, + 0x41, 0x12, 0x44, 0x11, 0x47, 0x11, 0x4a, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, + 0x55, 0x11, 0x57, 0x11, 0x59, 0x11, 0x5a, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x04, 0x00, 0x14, 0x00, + 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, 0x00, + 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x39, 0x45, 0x36, 0x49, 0x34, 0x4c, 0x31, 0x4f, 0x30, 0x51, 0x2e, 0x53, 0x2c, 0x55, 0x2a, + 0x57, 0x2a, 0x57, 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x22, 0x61, 0x21, 0x64, 0x20, 0x65, 0x20, 0xda, 0x00, 0xd6, 0x00, 0xd2, 0x00, 0xce, 0x00, + 0xcb, 0x00, 0xc7, 0x00, 0xc4, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, 0x00, + 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x00, 0xa6, 0x00, + 0x22, 0x1f, 0x26, 0x1c, 0x2a, 0x19, 0x2e, 0x17, 0x32, 0x16, 0x36, 0x14, 0x39, 0x13, 0x3d, 0x13, + 0x40, 0x12, 0x43, 0x11, 0x45, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x11, + 0x53, 0x11, 0x55, 0x11, 0x56, 0x11, 0x58, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x0a, 0x00, + 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, 0x00, + 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3a, 0x45, 0x37, 0x48, 0x35, 0x4a, 0x32, 0x4e, 0x31, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2b, + 0x57, 0x2a, 0x57, 0x29, 0x58, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5e, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xcf, 0x00, + 0xcc, 0x00, 0xc8, 0x00, 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xba, 0x00, 0xb7, 0x00, + 0xb5, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x00, + 0x21, 0x20, 0x26, 0x1c, 0x2a, 0x1a, 0x2d, 0x18, 0x31, 0x16, 0x35, 0x14, 0x38, 0x13, 0x3b, 0x13, + 0x3e, 0x12, 0x41, 0x12, 0x44, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, + 0x51, 0x11, 0x53, 0x11, 0x55, 0x11, 0x56, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x01, 0x00, + 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, 0x00, + 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3a, 0x45, 0x37, 0x48, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x2f, 0x52, 0x2e, 0x53, 0x2c, + 0x55, 0x2a, 0x57, 0x2a, 0x57, 0x29, 0x59, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5f, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xd0, 0x00, + 0xcc, 0x00, 0xc9, 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb9, 0x00, + 0xb7, 0x00, 0xb5, 0x00, 0xb3, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xaa, 0x00, + 0x21, 0x20, 0x25, 0x1d, 0x29, 0x1a, 0x2d, 0x18, 0x30, 0x17, 0x33, 0x15, 0x37, 0x14, 0x3a, 0x13, + 0x3d, 0x13, 0x3f, 0x12, 0x42, 0x12, 0x44, 0x11, 0x47, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, + 0x4f, 0x11, 0x51, 0x11, 0x53, 0x11, 0x55, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, + 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, 0x00, + 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3a, 0x45, 0x38, 0x47, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x30, 0x51, 0x2e, 0x53, 0x2e, + 0x53, 0x2b, 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x28, 0x5a, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x25, + 0x5f, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0xdb, 0x00, 0xd7, 0x00, 0xd4, 0x00, 0xd0, 0x00, + 0xcd, 0x00, 0xca, 0x00, 0xc7, 0x00, 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbd, 0x00, 0xbb, 0x00, + 0xb8, 0x00, 0xb6, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xac, 0x00, + 0x21, 0x20, 0x25, 0x1d, 0x29, 0x1a, 0x2c, 0x18, 0x2f, 0x17, 0x33, 0x16, 0x36, 0x14, 0x39, 0x13, + 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x43, 0x11, 0x45, 0x11, 0x47, 0x11, 0x4a, 0x11, 0x4c, 0x11, + 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x52, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, + 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, 0x00, + 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3b, 0x44, 0x39, 0x46, 0x35, 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x2e, 0x53, 0x2e, + 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x24, 0x5f, 0x23, 0x60, 0x23, 0x60, 0x23, 0xdb, 0x00, 0xd8, 0x00, 0xd4, 0x00, 0xd1, 0x00, + 0xce, 0x00, 0xcb, 0x00, 0xc8, 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbc, 0x00, + 0xba, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, + 0x21, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x18, 0x2f, 0x17, 0x32, 0x16, 0x35, 0x14, 0x38, 0x14, + 0x3a, 0x13, 0x3d, 0x13, 0x3f, 0x12, 0x42, 0x12, 0x44, 0x11, 0x46, 0x11, 0x48, 0x11, 0x4a, 0x11, + 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, + 0x00, 0x07, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, 0x00, + 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3b, 0x44, 0x39, 0x46, 0x36, 0x4a, 0x35, 0x4a, 0x32, 0x4e, 0x31, 0x4f, 0x2f, 0x51, 0x2e, + 0x53, 0x2e, 0x53, 0x2b, 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x29, 0x59, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x26, 0x5c, 0x24, 0x5f, 0x23, 0x60, 0x23, 0xdb, 0x00, 0xd8, 0x00, 0xd5, 0x00, 0xd2, 0x00, + 0xcf, 0x00, 0xcc, 0x00, 0xca, 0x00, 0xc6, 0x00, 0xc4, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbd, 0x00, + 0xbb, 0x00, 0xb9, 0x00, 0xb8, 0x00, 0xb5, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, + 0x21, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x19, 0x2e, 0x17, 0x31, 0x16, 0x34, 0x15, 0x37, 0x14, + 0x39, 0x13, 0x3b, 0x13, 0x3e, 0x13, 0x40, 0x12, 0x42, 0x12, 0x45, 0x11, 0x46, 0x11, 0x49, 0x11, + 0x4a, 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x27, 0x68, 0x32, 0x51, 0x36, 0x4a, 0x38, 0x47, + 0x3a, 0x46, 0x3a, 0x45, 0x3b, 0x44, 0x3c, 0x43, 0x3c, 0x43, 0x3c, 0x43, 0x3c, 0x42, 0x3d, 0x42, + 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, + 0x41, 0x0e, 0x16, 0x27, 0x23, 0x31, 0x2a, 0x35, 0x2e, 0x38, 0x31, 0x39, 0x33, 0x3a, 0x34, 0x3b, + 0x35, 0x3b, 0x36, 0x3c, 0x37, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, + 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3b, 0x3d, 0x68, 0x00, 0x32, 0x27, 0x36, 0x31, 0x38, 0x35, + 0x3a, 0x38, 0x3a, 0x39, 0x3b, 0x3a, 0x3c, 0x3b, 0x3c, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3c, + 0x3d, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x32, 0x28, 0x36, 0x2c, 0x38, 0x2f, 0x3a, + 0x32, 0x3a, 0x34, 0x3b, 0x35, 0x3c, 0x36, 0x3c, 0x37, 0x3c, 0x37, 0x3c, 0x38, 0x3d, 0x39, 0x3d, + 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, + 0x00, 0x97, 0x01, 0x67, 0x09, 0x56, 0x11, 0x4f, 0x17, 0x4b, 0x1c, 0x49, 0x20, 0x47, 0x23, 0x46, + 0x25, 0x45, 0x28, 0x44, 0x2a, 0x44, 0x2b, 0x43, 0x2c, 0x43, 0x2e, 0x43, 0x2f, 0x42, 0x30, 0x42, + 0x31, 0x42, 0x31, 0x42, 0x32, 0x42, 0x33, 0x42, 0x1a, 0x5c, 0x25, 0x4f, 0x2a, 0x4a, 0x2e, 0x48, + 0x31, 0x46, 0x33, 0x45, 0x34, 0x44, 0x36, 0x44, 0x37, 0x43, 0x37, 0x43, 0x38, 0x43, 0x38, 0x42, + 0x39, 0x42, 0x39, 0x42, 0x39, 0x41, 0x3a, 0x41, 0x3a, 0x41, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x99, 0x00, 0x5f, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x28, 0x25, 0x2d, 0x28, 0x30, 0x2b, 0x32, + 0x2e, 0x34, 0x2f, 0x35, 0x31, 0x36, 0x32, 0x37, 0x33, 0x38, 0x34, 0x38, 0x35, 0x39, 0x36, 0x39, + 0x36, 0x39, 0x36, 0x3a, 0x37, 0x3a, 0x37, 0x3a, 0x38, 0x3b, 0x38, 0x3b, 0x39, 0x3b, 0x39, 0x3b, + 0x00, 0xb7, 0x00, 0x8b, 0x02, 0x72, 0x07, 0x65, 0x0c, 0x5c, 0x11, 0x57, 0x15, 0x53, 0x18, 0x50, + 0x1b, 0x4e, 0x1d, 0x4d, 0x20, 0x4b, 0x22, 0x4a, 0x23, 0x49, 0x25, 0x48, 0x26, 0x48, 0x28, 0x47, + 0x29, 0x47, 0x2a, 0x46, 0x2b, 0x46, 0x2c, 0x45, 0x16, 0x6d, 0x1f, 0x5f, 0x24, 0x57, 0x28, 0x53, + 0x2b, 0x50, 0x2d, 0x4d, 0x2f, 0x4c, 0x31, 0x4a, 0x32, 0x49, 0x33, 0x48, 0x34, 0x48, 0x35, 0x47, + 0x35, 0x46, 0x36, 0x46, 0x36, 0x45, 0x37, 0x45, 0x38, 0x45, 0x38, 0x45, 0x39, 0x44, 0x39, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xac, 0x00, 0x8b, 0x00, 0x44, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x25, 0x23, 0x28, 0x26, 0x2b, 0x28, 0x2e, + 0x2a, 0x2f, 0x2c, 0x31, 0x2e, 0x32, 0x2f, 0x33, 0x30, 0x34, 0x31, 0x35, 0x32, 0x36, 0x33, 0x36, + 0x33, 0x36, 0x34, 0x37, 0x35, 0x38, 0x36, 0x38, 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, + 0x00, 0xc5, 0x00, 0xa0, 0x00, 0x87, 0x03, 0x76, 0x06, 0x6b, 0x0a, 0x64, 0x0e, 0x5f, 0x11, 0x5b, + 0x13, 0x57, 0x16, 0x55, 0x18, 0x53, 0x1a, 0x51, 0x1c, 0x50, 0x1e, 0x4e, 0x20, 0x4d, 0x21, 0x4c, + 0x22, 0x4b, 0x24, 0x4a, 0x25, 0x49, 0x26, 0x48, 0x15, 0x72, 0x1b, 0x66, 0x20, 0x5f, 0x24, 0x5a, + 0x27, 0x56, 0x29, 0x53, 0x2c, 0x51, 0x2d, 0x50, 0x2f, 0x4e, 0x30, 0x4d, 0x31, 0x4c, 0x32, 0x4b, + 0x32, 0x4a, 0x33, 0x49, 0x34, 0x49, 0x35, 0x48, 0x35, 0x48, 0x35, 0x47, 0x35, 0x46, 0x36, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x6d, 0x00, 0x33, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x35, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x24, 0x23, 0x26, 0x25, 0x29, 0x27, 0x2b, + 0x28, 0x2c, 0x2a, 0x2e, 0x2c, 0x2f, 0x2d, 0x30, 0x2e, 0x31, 0x2f, 0x32, 0x30, 0x33, 0x30, 0x33, + 0x32, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, 0x36, 0x36, 0x37, + 0x00, 0xcb, 0x00, 0xad, 0x00, 0x96, 0x01, 0x84, 0x03, 0x78, 0x06, 0x70, 0x09, 0x69, 0x0b, 0x64, + 0x0e, 0x60, 0x10, 0x5d, 0x13, 0x59, 0x15, 0x57, 0x17, 0x56, 0x19, 0x54, 0x1a, 0x52, 0x1c, 0x51, + 0x1d, 0x50, 0x1f, 0x4f, 0x1f, 0x4f, 0x21, 0x4e, 0x14, 0x75, 0x19, 0x6b, 0x1e, 0x64, 0x22, 0x5f, + 0x24, 0x5b, 0x27, 0x58, 0x29, 0x56, 0x2a, 0x54, 0x2c, 0x52, 0x2d, 0x51, 0x2e, 0x4f, 0x2f, 0x4e, + 0x30, 0x4e, 0x31, 0x4d, 0x31, 0x4c, 0x32, 0x4a, 0x33, 0x4a, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x23, 0x23, 0x25, 0x24, 0x27, 0x25, 0x28, + 0x27, 0x2a, 0x28, 0x2c, 0x2a, 0x2d, 0x2b, 0x2e, 0x2c, 0x2f, 0x2d, 0x30, 0x2e, 0x31, 0x2f, 0x32, + 0x30, 0x32, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, + 0x00, 0xcf, 0x00, 0xb6, 0x00, 0xa0, 0x00, 0x8f, 0x01, 0x83, 0x03, 0x79, 0x05, 0x72, 0x08, 0x6c, + 0x0a, 0x68, 0x0c, 0x64, 0x0f, 0x61, 0x10, 0x5e, 0x12, 0x5b, 0x14, 0x59, 0x16, 0x58, 0x17, 0x56, + 0x19, 0x55, 0x1a, 0x53, 0x1b, 0x52, 0x1c, 0x50, 0x13, 0x77, 0x18, 0x6e, 0x1c, 0x68, 0x1f, 0x63, + 0x22, 0x5f, 0x24, 0x5c, 0x26, 0x59, 0x28, 0x57, 0x29, 0x56, 0x2a, 0x54, 0x2c, 0x53, 0x2d, 0x52, + 0x2e, 0x50, 0x2e, 0x4f, 0x2f, 0x4f, 0x31, 0x4e, 0x31, 0x4d, 0x31, 0x4d, 0x31, 0x4c, 0x32, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, 0x48, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x23, 0x22, 0x24, 0x24, 0x26, 0x25, 0x27, + 0x26, 0x29, 0x27, 0x2a, 0x28, 0x2b, 0x2a, 0x2d, 0x2a, 0x2d, 0x2c, 0x2e, 0x2d, 0x30, 0x2d, 0x30, + 0x2e, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x34, + 0x00, 0xd1, 0x00, 0xbc, 0x00, 0xa9, 0x00, 0x99, 0x00, 0x8c, 0x02, 0x82, 0x03, 0x7a, 0x05, 0x74, + 0x07, 0x6f, 0x09, 0x6b, 0x0b, 0x67, 0x0d, 0x63, 0x0f, 0x61, 0x10, 0x5f, 0x12, 0x5c, 0x14, 0x5a, + 0x15, 0x58, 0x16, 0x58, 0x18, 0x57, 0x19, 0x56, 0x13, 0x78, 0x17, 0x70, 0x1b, 0x6a, 0x1e, 0x66, + 0x20, 0x62, 0x22, 0x5f, 0x24, 0x5c, 0x26, 0x5b, 0x27, 0x58, 0x29, 0x57, 0x2a, 0x56, 0x2b, 0x53, + 0x2c, 0x53, 0x2d, 0x52, 0x2e, 0x51, 0x2e, 0x50, 0x2f, 0x4f, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x24, 0x23, 0x25, 0x24, 0x27, + 0x25, 0x27, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2e, + 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x33, 0x30, 0x33, 0x31, 0x33, + 0x00, 0xd3, 0x00, 0xc1, 0x00, 0xaf, 0x00, 0xa1, 0x00, 0x94, 0x00, 0x8a, 0x02, 0x82, 0x03, 0x7b, + 0x05, 0x76, 0x07, 0x71, 0x09, 0x6d, 0x0a, 0x69, 0x0c, 0x66, 0x0e, 0x63, 0x0f, 0x61, 0x10, 0x60, + 0x12, 0x5d, 0x13, 0x5c, 0x14, 0x59, 0x16, 0x58, 0x13, 0x79, 0x16, 0x72, 0x19, 0x6d, 0x1c, 0x69, + 0x1f, 0x65, 0x20, 0x61, 0x23, 0x60, 0x24, 0x5c, 0x26, 0x5b, 0x27, 0x59, 0x28, 0x57, 0x2a, 0x57, + 0x2a, 0x55, 0x2b, 0x53, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2e, 0x51, 0x2e, 0x4f, 0x2f, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, + 0x25, 0x27, 0x26, 0x28, 0x26, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2d, 0x2a, 0x2d, + 0x2c, 0x2d, 0x2d, 0x2e, 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, + 0x00, 0xd4, 0x00, 0xc4, 0x00, 0xb5, 0x00, 0xa7, 0x00, 0x9b, 0x00, 0x91, 0x01, 0x88, 0x02, 0x82, + 0x03, 0x7b, 0x05, 0x77, 0x07, 0x73, 0x08, 0x6e, 0x0a, 0x6b, 0x0b, 0x69, 0x0c, 0x66, 0x0e, 0x63, + 0x0f, 0x61, 0x10, 0x60, 0x12, 0x5f, 0x13, 0x5c, 0x12, 0x79, 0x15, 0x73, 0x18, 0x6e, 0x1b, 0x6a, + 0x1d, 0x67, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x24, 0x5d, 0x26, 0x5c, 0x27, 0x5a, 0x27, 0x58, + 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, + 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, + 0x24, 0x26, 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2c, + 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x30, 0x30, 0x30, + 0x00, 0xd5, 0x00, 0xc7, 0x00, 0xb9, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8f, 0x01, 0x87, + 0x02, 0x82, 0x03, 0x7b, 0x05, 0x77, 0x06, 0x74, 0x08, 0x6f, 0x09, 0x6c, 0x0b, 0x6b, 0x0c, 0x68, + 0x0d, 0x65, 0x0e, 0x63, 0x0f, 0x62, 0x10, 0x60, 0x12, 0x7a, 0x15, 0x75, 0x18, 0x70, 0x1a, 0x6c, + 0x1d, 0x69, 0x1e, 0x65, 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x24, 0x5d, 0x26, 0x5c, 0x27, 0x5b, + 0x27, 0x58, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, 0x2c, 0x53, 0x2e, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, 0x59, 0x00, 0x41, + 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, + 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, + 0x24, 0x25, 0x25, 0x27, 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, 0x2d, 0x30, 0x2d, 0x30, + 0x00, 0xd6, 0x00, 0xc9, 0x00, 0xbc, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9c, 0x00, 0x94, 0x00, 0x8c, + 0x02, 0x86, 0x02, 0x81, 0x03, 0x7c, 0x05, 0x78, 0x06, 0x75, 0x07, 0x71, 0x09, 0x6d, 0x0a, 0x6c, + 0x0b, 0x6a, 0x0c, 0x67, 0x0d, 0x65, 0x0e, 0x62, 0x12, 0x7b, 0x15, 0x76, 0x18, 0x72, 0x1a, 0x6d, + 0x1b, 0x6a, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x63, 0x22, 0x60, 0x23, 0x60, 0x23, 0x5d, 0x25, 0x5c, + 0x27, 0x5c, 0x27, 0x5a, 0x28, 0x57, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2a, 0x55, 0x2b, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, + 0x00, 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1a, 0x00, + 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x21, 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x24, + 0x24, 0x25, 0x24, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, + 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, + 0x00, 0xd6, 0x00, 0xcb, 0x00, 0xbf, 0x00, 0xb4, 0x00, 0xab, 0x00, 0xa1, 0x00, 0x99, 0x00, 0x91, + 0x01, 0x8b, 0x02, 0x85, 0x02, 0x81, 0x04, 0x7c, 0x05, 0x78, 0x06, 0x76, 0x07, 0x73, 0x08, 0x6f, + 0x09, 0x6c, 0x0b, 0x6b, 0x0c, 0x6a, 0x0c, 0x67, 0x12, 0x7b, 0x15, 0x76, 0x17, 0x72, 0x19, 0x6e, + 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x66, 0x20, 0x65, 0x20, 0x62, 0x22, 0x60, 0x23, 0x60, 0x23, 0x5d, + 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x27, 0x58, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x56, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, + 0x00, 0x49, 0x00, 0x35, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, 0x00, + 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, + 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, + 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x00, 0xd7, 0x00, 0xcc, 0x00, 0xc2, 0x00, 0xb8, 0x00, 0xae, 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x95, + 0x00, 0x90, 0x01, 0x8a, 0x02, 0x85, 0x03, 0x81, 0x04, 0x7c, 0x05, 0x78, 0x06, 0x76, 0x07, 0x74, + 0x08, 0x70, 0x09, 0x6d, 0x0a, 0x6c, 0x0b, 0x6a, 0x12, 0x7b, 0x14, 0x77, 0x16, 0x73, 0x18, 0x70, + 0x1a, 0x6d, 0x1c, 0x6a, 0x1d, 0x69, 0x1e, 0x65, 0x20, 0x65, 0x20, 0x62, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x59, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, 0x7b, 0x00, 0x68, + 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, + 0x1c, 0x00, 0x16, 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, 0x26, 0x28, 0x28, 0x28, + 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2d, + 0x00, 0xd7, 0x00, 0xcd, 0x00, 0xc4, 0x00, 0xbb, 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9a, + 0x00, 0x93, 0x00, 0x8e, 0x01, 0x88, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7c, 0x05, 0x79, 0x06, 0x77, + 0x07, 0x75, 0x07, 0x72, 0x09, 0x6e, 0x09, 0x6d, 0x12, 0x7b, 0x14, 0x77, 0x15, 0x73, 0x18, 0x71, + 0x1a, 0x6d, 0x1a, 0x6b, 0x1d, 0x69, 0x1d, 0x67, 0x1f, 0x65, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x5e, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5a, 0x27, 0x58, 0x29, 0x57, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, + 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, 0x2b, 0x00, 0x26, 0x00, + 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, + 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x26, 0x28, + 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, + 0x00, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbd, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa4, 0x00, 0x9e, + 0x00, 0x97, 0x00, 0x92, 0x01, 0x8d, 0x02, 0x88, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7d, 0x05, 0x79, + 0x06, 0x77, 0x07, 0x75, 0x07, 0x73, 0x09, 0x70, 0x12, 0x7c, 0x13, 0x78, 0x15, 0x75, 0x18, 0x72, + 0x19, 0x6e, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, 0x64, 0x21, 0x61, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x27, 0x59, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, + 0x00, 0x6a, 0x00, 0x59, 0x00, 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, 0x00, + 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23, + 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, + 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbe, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa8, 0x00, 0xa1, + 0x00, 0x9b, 0x00, 0x95, 0x00, 0x90, 0x01, 0x8c, 0x02, 0x87, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7d, + 0x05, 0x79, 0x06, 0x77, 0x06, 0x76, 0x07, 0x74, 0x11, 0x7c, 0x13, 0x78, 0x15, 0x76, 0x17, 0x72, + 0x18, 0x6f, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x65, 0x20, 0x64, + 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x00, 0x2f, 0x00, 0x5f, 0x00, 0x99, 0x00, 0xac, 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, + 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, + 0x00, 0x9a, 0x00, 0x6d, 0x00, 0x8d, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0xb2, 0x00, 0xba, 0x00, 0xbb, + 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x3b, 0x00, 0x6f, 0x00, 0x9f, 0x00, 0xaf, 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbc, + 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, + 0x07, 0x47, 0x00, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, + 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, + 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, + 0x27, 0x29, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc0, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xac, 0x00, 0xa4, + 0x00, 0x9e, 0x00, 0x98, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, + 0x04, 0x7d, 0x05, 0x79, 0x06, 0x78, 0x06, 0x76, 0x11, 0x7c, 0x13, 0x79, 0x15, 0x76, 0x17, 0x72, + 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x66, 0x20, 0x65, 0x20, 0x65, + 0x20, 0x63, 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x5f, 0x00, 0x8b, 0x00, 0x9f, 0x00, 0xaa, 0x00, 0xb0, 0x00, 0xb3, + 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, + 0x00, 0x6d, 0x00, 0x54, 0x00, 0x6b, 0x00, 0x87, 0x00, 0x98, 0x00, 0xa6, 0x00, 0xb0, 0x00, 0xb3, + 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x07, 0x02, 0x27, 0x00, 0x6f, 0x00, 0x93, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb5, + 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, + 0x0f, 0x0f, 0x00, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, + 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, + 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x2a, 0x2b, + 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xc9, 0x00, 0xc2, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xae, 0x00, 0xa7, + 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x96, 0x00, 0x92, 0x01, 0x8e, 0x01, 0x89, 0x02, 0x86, 0x02, 0x83, + 0x03, 0x81, 0x04, 0x7e, 0x05, 0x7a, 0x06, 0x78, 0x11, 0x7c, 0x13, 0x79, 0x15, 0x76, 0x16, 0x73, + 0x18, 0x72, 0x19, 0x6e, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x69, 0x1e, 0x65, 0x20, 0x65, + 0x20, 0x65, 0x20, 0x62, 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, 0x24, 0x5c, 0x26, 0x5c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x44, 0x00, 0x6d, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, + 0x00, 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, + 0x00, 0x8d, 0x00, 0x6b, 0x00, 0x28, 0x00, 0x52, 0x00, 0x70, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, + 0x00, 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x12, 0x02, 0x00, 0x22, 0x00, 0x58, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9c, 0x00, 0xa4, + 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, + 0x26, 0x00, 0x05, 0x05, 0x00, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, + 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, + 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, + 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, + 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xca, 0x00, 0xc4, 0x00, 0xbd, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xaa, + 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x91, 0x01, 0x8e, 0x02, 0x89, 0x02, 0x85, + 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, 0x05, 0x7a, 0x11, 0x7c, 0x13, 0x7a, 0x15, 0x76, 0x15, 0x74, + 0x18, 0x72, 0x18, 0x6f, 0x1a, 0x6d, 0x1a, 0x6c, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x67, 0x1f, 0x65, + 0x20, 0x65, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, 0x24, 0x5c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x33, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, + 0x00, 0x8e, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, + 0x00, 0x9b, 0x00, 0x87, 0x00, 0x52, 0x00, 0x11, 0x00, 0x39, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, + 0x00, 0x8e, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x25, 0x00, 0x0b, 0x09, 0x00, 0x21, 0x00, 0x4a, 0x00, 0x68, 0x00, 0x7d, 0x00, 0x8b, + 0x00, 0x96, 0x00, 0x9d, 0x00, 0xa3, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, + 0x33, 0x00, 0x1c, 0x00, 0x00, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, + 0x00, 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, + 0x23, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, + 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb1, 0x00, 0xac, + 0x00, 0xa6, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x97, 0x00, 0x93, 0x00, 0x90, 0x01, 0x8d, 0x02, 0x88, + 0x02, 0x85, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, 0x11, 0x7c, 0x13, 0x7a, 0x15, 0x76, 0x15, 0x75, + 0x18, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x69, 0x1e, 0x66, + 0x20, 0x65, 0x20, 0x65, 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x28, 0x00, 0x48, 0x00, 0x5f, + 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, + 0x00, 0xa6, 0x00, 0x98, 0x00, 0x70, 0x00, 0x39, 0x00, 0x02, 0x00, 0x28, 0x00, 0x48, 0x00, 0x5f, + 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x2f, 0x00, 0x16, 0x00, 0x08, 0x0e, 0x00, 0x20, 0x00, 0x42, 0x00, 0x5c, 0x00, 0x6f, + 0x00, 0x7e, 0x00, 0x89, 0x00, 0x92, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa9, + 0x38, 0x00, 0x2a, 0x00, 0x09, 0x00, 0x00, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, + 0x00, 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, + 0x16, 0xa4, 0x01, 0xbb, 0x00, 0xc6, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, + 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, + 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x05, 0x9d, 0x00, 0xbc, 0x00, 0xc6, 0x00, 0xcd, + 0x00, 0xcf, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, + 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x02, 0xbd, 0x00, 0xcc, 0x00, 0xd1, 0x00, 0xd5, + 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xda, + 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x3d, + 0x00, 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, 0x93, 0x00, 0x98, + 0x00, 0xb2, 0x00, 0xa6, 0x00, 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, + 0x00, 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, 0x93, 0x00, 0x98, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0e, 0x02, 0x06, 0x12, 0x00, 0x20, 0x00, 0x3c, 0x00, 0x52, + 0x00, 0x64, 0x00, 0x73, 0x00, 0x7e, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9e, + 0x3a, 0x00, 0x31, 0x00, 0x19, 0x00, 0x00, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, + 0x00, 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, + 0x23, 0x74, 0x09, 0x91, 0x02, 0xa3, 0x00, 0xaf, 0x00, 0xb7, 0x00, 0xbd, 0x00, 0xc2, 0x00, 0xc5, + 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, + 0x00, 0xd2, 0x00, 0xd2, 0x00, 0xd2, 0x00, 0xd3, 0x0d, 0x6b, 0x01, 0x8e, 0x00, 0xa4, 0x00, 0xb0, + 0x00, 0xb9, 0x00, 0xbe, 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc8, 0x00, 0xca, 0x00, 0xcc, 0x00, 0xcc, + 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x06, 0xa4, 0x00, 0xb5, 0x00, 0xc0, 0x00, 0xc6, + 0x00, 0xcb, 0x00, 0xcd, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, + 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x35, 0x00, 0x48, 0x00, 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, + 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, 0x48, 0x00, 0x21, 0x00, 0x01, 0x00, 0x1c, + 0x00, 0x35, 0x00, 0x48, 0x00, 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x18, 0x00, 0x0c, 0x07, 0x05, 0x14, 0x00, 0x20, 0x00, 0x37, + 0x00, 0x4c, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x86, 0x00, 0x8d, 0x00, 0x92, + 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x00, 0x00, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, + 0x00, 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, + 0x2a, 0x61, 0x11, 0x78, 0x07, 0x8a, 0x03, 0x98, 0x01, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, + 0x00, 0xba, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc9, + 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x12, 0x51, 0x04, 0x73, 0x00, 0x89, 0x00, 0x9a, + 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbb, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc3, + 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x09, 0x97, 0x02, 0xa8, 0x00, 0xb3, 0x00, 0xbb, + 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc7, 0x00, 0xca, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xd0, + 0x00, 0xd1, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, 0x00, 0xd5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, + 0x00, 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x1c, 0x00, 0x00, + 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x20, 0x00, 0x10, 0x00, 0x0a, 0x0b, 0x04, 0x16, 0x00, 0x20, + 0x00, 0x34, 0x00, 0x46, 0x00, 0x56, 0x00, 0x62, 0x00, 0x6d, 0x00, 0x77, 0x00, 0x7f, 0x00, 0x85, + 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, + 0x00, 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, + 0x2e, 0x59, 0x17, 0x6b, 0x0c, 0x7a, 0x06, 0x87, 0x03, 0x92, 0x01, 0x9b, 0x00, 0xa2, 0x00, 0xa8, + 0x00, 0xad, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, + 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x15, 0x45, 0x08, 0x60, 0x02, 0x77, 0x00, 0x87, + 0x00, 0x94, 0x00, 0x9d, 0x00, 0xa4, 0x00, 0xaa, 0x00, 0xaf, 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, + 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0a, 0x91, 0x04, 0x9e, 0x01, 0xaa, 0x00, 0xb2, + 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc6, 0x00, 0xc8, 0x00, 0xca, 0x00, 0xcb, + 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, 0x60, 0x00, 0x6a, + 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, 0x60, 0x00, 0x6a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3b, 0x00, 0x33, 0x00, 0x27, 0x00, 0x18, 0x00, 0x0d, 0x04, 0x08, 0x0e, 0x04, 0x17, + 0x00, 0x20, 0x00, 0x32, 0x00, 0x42, 0x00, 0x50, 0x00, 0x5c, 0x00, 0x67, 0x00, 0x70, 0x00, 0x78, + 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x2e, + 0x00, 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, + 0x31, 0x53, 0x1c, 0x62, 0x11, 0x70, 0x0a, 0x7b, 0x06, 0x85, 0x03, 0x8e, 0x02, 0x96, 0x00, 0x9c, + 0x00, 0xa2, 0x00, 0xa7, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb8, 0x00, 0xba, + 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbf, 0x17, 0x3d, 0x0b, 0x55, 0x04, 0x67, 0x01, 0x78, + 0x00, 0x85, 0x00, 0x90, 0x00, 0x98, 0x00, 0x9f, 0x00, 0xa5, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xb1, + 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc1, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0b, 0x8d, 0x05, 0x99, 0x02, 0xa2, 0x00, 0xaa, + 0x00, 0xb1, 0x00, 0xb6, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc7, + 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, + 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, + 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x0c, 0x07, 0x07, 0x10, + 0x03, 0x18, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4c, 0x00, 0x57, 0x00, 0x61, 0x00, 0x6a, + 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x20, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, + 0x33, 0x50, 0x20, 0x5c, 0x15, 0x68, 0x0e, 0x72, 0x09, 0x7c, 0x05, 0x84, 0x03, 0x8c, 0x02, 0x92, + 0x01, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, + 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, 0x18, 0x38, 0x0d, 0x4c, 0x06, 0x5e, 0x03, 0x6c, + 0x01, 0x7a, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x95, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, + 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0c, 0x8a, 0x06, 0x94, 0x03, 0x9d, 0x01, 0xa4, + 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, + 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, + 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, 0x59, 0x00, 0x41, + 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x00, 0x0e, 0x02, 0x0a, 0x0a, + 0x06, 0x11, 0x03, 0x19, 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x53, 0x00, 0x5c, + 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x14, + 0x00, 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, + 0x34, 0x4e, 0x23, 0x58, 0x18, 0x62, 0x10, 0x6c, 0x0b, 0x75, 0x08, 0x7c, 0x05, 0x83, 0x03, 0x8a, + 0x02, 0x8f, 0x01, 0x95, 0x00, 0x9a, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xac, + 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb5, 0x1a, 0x35, 0x0f, 0x47, 0x08, 0x55, 0x04, 0x64, + 0x02, 0x70, 0x00, 0x7a, 0x00, 0x83, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa0, + 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb7, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0d, 0x89, 0x07, 0x92, 0x04, 0x99, 0x02, 0xa0, + 0x01, 0xa6, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbd, 0x00, 0xbe, + 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xca, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x39, + 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, + 0x00, 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x0d, 0x05, + 0x09, 0x0c, 0x06, 0x13, 0x03, 0x19, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x3a, 0x00, 0x45, 0x00, 0x4f, + 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x00, 0x00, 0x0a, + 0x00, 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, + 0x35, 0x4c, 0x26, 0x55, 0x1b, 0x5e, 0x13, 0x67, 0x0e, 0x6f, 0x0a, 0x76, 0x07, 0x7d, 0x05, 0x82, + 0x03, 0x88, 0x02, 0x8e, 0x02, 0x92, 0x01, 0x97, 0x00, 0x9b, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, + 0x00, 0xa8, 0x00, 0xab, 0x00, 0xac, 0x00, 0xaf, 0x1b, 0x33, 0x10, 0x42, 0x0a, 0x50, 0x05, 0x5c, + 0x03, 0x68, 0x01, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x8a, 0x00, 0x90, 0x00, 0x96, 0x00, 0x99, + 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0d, 0x88, 0x08, 0x8f, 0x05, 0x96, 0x02, 0x9c, + 0x01, 0xa2, 0x00, 0xa7, 0x00, 0xac, 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xbb, + 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, + 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, + 0x00, 0x49, 0x00, 0x35, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x23, 0x00, 0x19, 0x00, 0x0f, 0x00, + 0x0c, 0x07, 0x08, 0x0e, 0x05, 0x14, 0x02, 0x1a, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x42, + 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x01, + 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, + 0x36, 0x4a, 0x28, 0x52, 0x1d, 0x5b, 0x16, 0x63, 0x10, 0x6a, 0x0c, 0x71, 0x09, 0x77, 0x07, 0x7d, + 0x05, 0x82, 0x03, 0x87, 0x02, 0x8c, 0x02, 0x90, 0x01, 0x94, 0x00, 0x99, 0x00, 0x9c, 0x00, 0x9e, + 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xaa, 0x1b, 0x30, 0x12, 0x3e, 0x0c, 0x4b, 0x07, 0x57, + 0x04, 0x61, 0x02, 0x6b, 0x01, 0x73, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8f, 0x00, 0x93, + 0x00, 0x97, 0x00, 0x9c, 0x00, 0x9f, 0x00, 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xac, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0d, 0x86, 0x09, 0x8d, 0x06, 0x94, 0x03, 0x9a, + 0x02, 0x9f, 0x01, 0xa4, 0x00, 0xa8, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb6, 0x00, 0xb8, + 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, + 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, 0x7b, 0x00, 0x68, + 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1d, 0x00, 0x14, 0x00, + 0x0e, 0x03, 0x0b, 0x09, 0x08, 0x0f, 0x05, 0x15, 0x02, 0x1a, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, + 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, + 0x37, 0x49, 0x2a, 0x51, 0x20, 0x58, 0x18, 0x5f, 0x13, 0x66, 0x0f, 0x6c, 0x0b, 0x73, 0x09, 0x78, + 0x07, 0x7e, 0x05, 0x82, 0x03, 0x86, 0x02, 0x8b, 0x02, 0x8f, 0x01, 0x92, 0x01, 0x96, 0x00, 0x9a, + 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa4, 0x1c, 0x2f, 0x13, 0x3b, 0x0d, 0x47, 0x09, 0x51, + 0x05, 0x5c, 0x04, 0x65, 0x02, 0x6e, 0x01, 0x75, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8d, + 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9c, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa5, 0x00, 0xa7, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x86, 0x09, 0x8c, 0x06, 0x92, 0x04, 0x97, + 0x02, 0x9c, 0x02, 0xa1, 0x01, 0xa5, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb5, + 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, + 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, + 0x10, 0x00, 0x0d, 0x05, 0x0a, 0x0b, 0x07, 0x10, 0x04, 0x16, 0x02, 0x1b, 0x00, 0x1f, 0x00, 0x2a, + 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, + 0x38, 0x48, 0x2b, 0x4f, 0x22, 0x56, 0x1a, 0x5c, 0x15, 0x63, 0x11, 0x69, 0x0d, 0x6e, 0x0a, 0x74, + 0x08, 0x78, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x85, 0x03, 0x8a, 0x02, 0x8e, 0x02, 0x90, 0x01, 0x93, + 0x00, 0x98, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x1c, 0x2d, 0x14, 0x39, 0x0e, 0x44, 0x0a, 0x4e, + 0x07, 0x57, 0x04, 0x60, 0x02, 0x67, 0x01, 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x87, + 0x00, 0x8c, 0x00, 0x90, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0xa0, 0x00, 0xa2, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x85, 0x0a, 0x8b, 0x07, 0x90, 0x05, 0x95, + 0x03, 0x9a, 0x02, 0x9e, 0x01, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, + 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, + 0x00, 0x6a, 0x00, 0x59, 0x00, 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x25, 0x00, 0x1d, 0x00, + 0x15, 0x00, 0x0e, 0x01, 0x0c, 0x07, 0x09, 0x0c, 0x06, 0x11, 0x04, 0x16, 0x02, 0x1b, 0x00, 0x1f, + 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, 0x00, + 0x07, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, + 0x38, 0x48, 0x2d, 0x4e, 0x23, 0x54, 0x1c, 0x5a, 0x17, 0x5f, 0x12, 0x66, 0x0f, 0x6a, 0x0c, 0x71, + 0x0a, 0x74, 0x08, 0x79, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x89, 0x02, 0x8d, 0x02, 0x8f, + 0x01, 0x92, 0x01, 0x95, 0x00, 0x99, 0x00, 0x9b, 0x1c, 0x2d, 0x15, 0x37, 0x0f, 0x41, 0x0b, 0x4a, + 0x07, 0x53, 0x05, 0x5b, 0x04, 0x63, 0x02, 0x69, 0x01, 0x70, 0x00, 0x77, 0x00, 0x7d, 0x00, 0x81, + 0x00, 0x87, 0x00, 0x8a, 0x00, 0x8f, 0x00, 0x92, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9e, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x85, 0x0a, 0x8a, 0x07, 0x8f, 0x05, 0x93, + 0x03, 0x98, 0x02, 0x9c, 0x02, 0xa0, 0x01, 0xa3, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, + 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x39, 0x47, 0x2e, 0x4d, 0x25, 0x53, 0x1e, 0x58, 0x19, 0x5e, 0x14, 0x63, 0x11, 0x68, 0x0e, 0x6c, + 0x0b, 0x72, 0x09, 0x75, 0x07, 0x79, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x88, 0x02, 0x8c, + 0x02, 0x8e, 0x01, 0x91, 0x01, 0x93, 0x00, 0x97, 0x1d, 0x2c, 0x16, 0x36, 0x10, 0x3e, 0x0c, 0x48, + 0x09, 0x50, 0x06, 0x58, 0x04, 0x5f, 0x02, 0x66, 0x02, 0x6c, 0x01, 0x71, 0x00, 0x77, 0x00, 0x7d, + 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9a, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x84, 0x0b, 0x89, 0x08, 0x8d, 0x06, 0x92, + 0x04, 0x96, 0x03, 0x9a, 0x02, 0x9e, 0x01, 0xa1, 0x01, 0xa4, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, + 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x39, 0x46, 0x2f, 0x4c, 0x26, 0x51, 0x20, 0x56, 0x1a, 0x5c, 0x16, 0x60, 0x12, 0x66, 0x0f, 0x69, + 0x0c, 0x6e, 0x0b, 0x73, 0x09, 0x76, 0x07, 0x7a, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x88, + 0x02, 0x8b, 0x02, 0x8e, 0x02, 0x90, 0x01, 0x92, 0x1d, 0x2b, 0x16, 0x34, 0x11, 0x3d, 0x0c, 0x45, + 0x0a, 0x4c, 0x07, 0x54, 0x05, 0x5b, 0x04, 0x61, 0x02, 0x67, 0x01, 0x6d, 0x01, 0x73, 0x00, 0x78, + 0x00, 0x7d, 0x00, 0x81, 0x00, 0x86, 0x00, 0x89, 0x00, 0x8d, 0x00, 0x90, 0x00, 0x93, 0x00, 0x95, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x84, 0x0b, 0x88, 0x08, 0x8d, 0x06, 0x91, + 0x05, 0x94, 0x03, 0x98, 0x02, 0x9c, 0x02, 0x9f, 0x01, 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, + 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x39, 0x46, 0x30, 0x4b, 0x28, 0x50, 0x21, 0x55, 0x1c, 0x59, 0x17, 0x5e, 0x14, 0x63, 0x10, 0x68, + 0x0e, 0x6b, 0x0c, 0x70, 0x0a, 0x74, 0x08, 0x76, 0x07, 0x7b, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x83, + 0x03, 0x87, 0x02, 0x8a, 0x02, 0x8d, 0x02, 0x8f, 0x1e, 0x2a, 0x17, 0x33, 0x12, 0x3b, 0x0e, 0x43, + 0x0a, 0x4a, 0x07, 0x51, 0x05, 0x58, 0x04, 0x5e, 0x03, 0x64, 0x02, 0x69, 0x01, 0x6f, 0x00, 0x74, + 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, 0x00, 0x89, 0x00, 0x8b, 0x00, 0x8f, 0x00, 0x93, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0b, 0x88, 0x09, 0x8c, 0x07, 0x90, + 0x05, 0x93, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x01, 0xa0, 0x01, 0xa3, 0x00, 0xa6, 0x00, 0xa8, + 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x46, 0x31, 0x4a, 0x29, 0x4e, 0x22, 0x54, 0x1d, 0x57, 0x19, 0x5d, 0x15, 0x60, 0x12, 0x65, + 0x0f, 0x69, 0x0d, 0x6c, 0x0b, 0x71, 0x09, 0x74, 0x08, 0x77, 0x07, 0x7b, 0x06, 0x7f, 0x05, 0x81, + 0x04, 0x83, 0x03, 0x86, 0x02, 0x8a, 0x02, 0x8c, 0x1e, 0x2a, 0x18, 0x32, 0x12, 0x39, 0x0f, 0x40, + 0x0c, 0x48, 0x09, 0x4f, 0x07, 0x55, 0x05, 0x5a, 0x04, 0x60, 0x02, 0x66, 0x02, 0x6b, 0x01, 0x70, + 0x00, 0x75, 0x00, 0x79, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8b, 0x07, 0x8e, + 0x06, 0x92, 0x04, 0x96, 0x03, 0x99, 0x02, 0x9b, 0x02, 0x9e, 0x01, 0xa1, 0x01, 0xa4, 0x00, 0xa6, + 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x45, 0x31, 0x49, 0x2a, 0x4d, 0x24, 0x52, 0x1f, 0x56, 0x1a, 0x5b, 0x16, 0x5f, 0x13, 0x63, + 0x10, 0x67, 0x0e, 0x6a, 0x0c, 0x6e, 0x0b, 0x72, 0x09, 0x75, 0x07, 0x77, 0x07, 0x7b, 0x06, 0x7f, + 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, 0x02, 0x89, 0x1e, 0x29, 0x18, 0x31, 0x13, 0x38, 0x0f, 0x3f, + 0x0c, 0x45, 0x0a, 0x4c, 0x07, 0x52, 0x05, 0x58, 0x04, 0x5d, 0x04, 0x63, 0x02, 0x67, 0x01, 0x6c, + 0x01, 0x71, 0x00, 0x75, 0x00, 0x79, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x8b, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8a, 0x07, 0x8e, + 0x06, 0x91, 0x05, 0x94, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x02, 0xa0, 0x01, 0xa2, 0x00, 0xa4, + 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x45, 0x32, 0x48, 0x2b, 0x4d, 0x25, 0x51, 0x20, 0x55, 0x1b, 0x59, 0x18, 0x5e, 0x14, 0x60, + 0x12, 0x65, 0x0f, 0x68, 0x0d, 0x6b, 0x0c, 0x6f, 0x0a, 0x73, 0x09, 0x75, 0x07, 0x77, 0x06, 0x7b, + 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, 0x1e, 0x29, 0x19, 0x30, 0x14, 0x37, 0x0f, 0x3d, + 0x0c, 0x44, 0x0a, 0x4a, 0x07, 0x50, 0x06, 0x55, 0x05, 0x5b, 0x04, 0x60, 0x02, 0x64, 0x02, 0x69, + 0x01, 0x6d, 0x01, 0x72, 0x00, 0x76, 0x00, 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x83, 0x00, 0x87, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0c, 0x86, 0x0a, 0x8a, 0x07, 0x8d, + 0x06, 0x90, 0x05, 0x93, 0x03, 0x96, 0x03, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, 0x01, 0xa3, + 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x45, 0x33, 0x48, 0x2c, 0x4d, 0x26, 0x4f, 0x21, 0x55, 0x1c, 0x57, 0x19, 0x5c, 0x16, 0x5f, + 0x13, 0x63, 0x10, 0x67, 0x0e, 0x6a, 0x0c, 0x6c, 0x0b, 0x71, 0x0a, 0x73, 0x09, 0x76, 0x07, 0x78, + 0x06, 0x7c, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x1e, 0x28, 0x19, 0x2f, 0x15, 0x35, 0x10, 0x3c, + 0x0d, 0x42, 0x0b, 0x48, 0x09, 0x4e, 0x07, 0x53, 0x05, 0x58, 0x04, 0x5d, 0x04, 0x62, 0x02, 0x66, + 0x02, 0x6b, 0x01, 0x6f, 0x00, 0x73, 0x00, 0x76, 0x00, 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x83, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x08, 0x8c, + 0x06, 0x8f, 0x05, 0x92, 0x04, 0x95, 0x03, 0x98, 0x02, 0x9a, 0x02, 0x9d, 0x02, 0x9f, 0x01, 0xa1, + 0x01, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x44, 0x33, 0x47, 0x2c, 0x4c, 0x27, 0x4f, 0x22, 0x54, 0x1e, 0x56, 0x1a, 0x5a, 0x17, 0x5e, + 0x14, 0x60, 0x12, 0x65, 0x10, 0x68, 0x0e, 0x6a, 0x0c, 0x6e, 0x0b, 0x72, 0x09, 0x74, 0x08, 0x76, + 0x07, 0x78, 0x06, 0x7c, 0x06, 0x7f, 0x05, 0x81, 0x1e, 0x28, 0x19, 0x2f, 0x16, 0x35, 0x12, 0x3b, + 0x0f, 0x41, 0x0c, 0x46, 0x0a, 0x4c, 0x07, 0x51, 0x05, 0x55, 0x05, 0x5b, 0x04, 0x5f, 0x02, 0x63, + 0x02, 0x68, 0x01, 0x6b, 0x01, 0x70, 0x00, 0x73, 0x00, 0x77, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x81, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x82, 0x0c, 0x86, 0x0b, 0x89, 0x09, 0x8c, + 0x07, 0x8f, 0x06, 0x91, 0x05, 0x94, 0x03, 0x97, 0x02, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, + 0x01, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xad, 0x00, 0xaf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2f, 0x23, 0x25, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0x44, 0x05, 0x25, 0x0d, 0x23, 0x12, 0x22, 0x15, 0x22, 0x17, 0x22, 0x18, 0x22, 0x1a, 0x22, + 0x1b, 0x22, 0x1b, 0x22, 0x1c, 0x22, 0x1c, 0x22, 0x1c, 0x22, 0x1d, 0x22, 0x1d, 0x22, 0x1e, 0x22, + 0x1e, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x10, 0x33, 0x13, 0x23, 0x17, 0x22, 0x1a, 0x22, + 0x1b, 0x22, 0x1c, 0x22, 0x1d, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x1f, 0x22, 0x1f, 0x22, + 0x1f, 0x22, 0x1f, 0x22, 0x1f, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x35, 0x25, 0x2b, 0x23, 0x27, 0x22, 0x25, 0x22, 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0x9d, 0x00, 0x6b, 0x01, 0x51, 0x04, 0x45, 0x08, 0x3d, 0x0b, 0x38, 0x0d, 0x35, 0x0f, 0x33, + 0x10, 0x30, 0x12, 0x2f, 0x13, 0x2d, 0x14, 0x2d, 0x15, 0x2c, 0x16, 0x2b, 0x16, 0x2a, 0x17, 0x2a, + 0x18, 0x29, 0x18, 0x29, 0x19, 0x28, 0x19, 0x28, 0x10, 0x5f, 0x10, 0x46, 0x11, 0x39, 0x13, 0x33, + 0x15, 0x2f, 0x16, 0x2d, 0x17, 0x2b, 0x18, 0x2a, 0x19, 0x29, 0x1a, 0x28, 0x1a, 0x27, 0x1b, 0x27, + 0x1b, 0x27, 0x1c, 0x26, 0x1c, 0x26, 0x1c, 0x26, 0x1d, 0x25, 0x1d, 0x25, 0x1d, 0x25, 0x1d, 0x25, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x2a, 0x2e, 0x26, 0x2a, 0x25, 0x27, 0x24, 0x26, 0x23, 0x25, 0x23, 0x24, 0x23, 0x24, 0x22, + 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xbc, 0x00, 0x8e, 0x00, 0x73, 0x00, 0x60, 0x02, 0x55, 0x04, 0x4c, 0x06, 0x47, 0x08, 0x42, + 0x0a, 0x3e, 0x0c, 0x3b, 0x0d, 0x39, 0x0e, 0x37, 0x0f, 0x36, 0x10, 0x34, 0x11, 0x33, 0x12, 0x32, + 0x12, 0x31, 0x13, 0x30, 0x14, 0x2f, 0x15, 0x2f, 0x10, 0x6f, 0x10, 0x58, 0x10, 0x4a, 0x11, 0x41, + 0x12, 0x3b, 0x13, 0x37, 0x14, 0x34, 0x15, 0x32, 0x16, 0x30, 0x17, 0x2e, 0x17, 0x2d, 0x18, 0x2c, + 0x18, 0x2c, 0x19, 0x2b, 0x19, 0x2a, 0x1a, 0x2a, 0x1a, 0x29, 0x1a, 0x29, 0x1b, 0x28, 0x1b, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x39, 0x2e, 0x31, 0x29, 0x2c, 0x27, 0x2a, 0x26, 0x28, 0x25, 0x27, 0x24, 0x25, 0x24, 0x25, 0x23, + 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, + 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xc6, 0x00, 0xa4, 0x00, 0x89, 0x00, 0x77, 0x00, 0x67, 0x01, 0x5e, 0x03, 0x55, 0x04, 0x50, + 0x05, 0x4b, 0x07, 0x47, 0x09, 0x44, 0x0a, 0x41, 0x0b, 0x3e, 0x0c, 0x3d, 0x0c, 0x3b, 0x0e, 0x39, + 0x0f, 0x38, 0x0f, 0x37, 0x0f, 0x35, 0x10, 0x35, 0x10, 0x74, 0x10, 0x63, 0x10, 0x55, 0x10, 0x4c, + 0x11, 0x44, 0x11, 0x40, 0x12, 0x3b, 0x13, 0x39, 0x13, 0x36, 0x14, 0x34, 0x15, 0x33, 0x16, 0x31, + 0x16, 0x30, 0x17, 0x2f, 0x17, 0x2e, 0x18, 0x2d, 0x18, 0x2d, 0x18, 0x2c, 0x18, 0x2b, 0x19, 0x2b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x31, 0x33, 0x2c, 0x2f, 0x29, 0x2b, 0x28, 0x29, 0x26, 0x28, 0x25, 0x27, 0x25, 0x26, 0x24, + 0x25, 0x24, 0x25, 0x24, 0x25, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xcd, 0x00, 0xb0, 0x00, 0x9a, 0x00, 0x87, 0x00, 0x78, 0x00, 0x6c, 0x01, 0x64, 0x02, 0x5c, + 0x03, 0x57, 0x04, 0x51, 0x05, 0x4e, 0x07, 0x4a, 0x07, 0x48, 0x09, 0x45, 0x0a, 0x43, 0x0a, 0x40, + 0x0c, 0x3f, 0x0c, 0x3d, 0x0c, 0x3c, 0x0d, 0x3b, 0x10, 0x77, 0x10, 0x69, 0x10, 0x5e, 0x10, 0x54, + 0x10, 0x4d, 0x11, 0x47, 0x11, 0x43, 0x12, 0x3f, 0x12, 0x3c, 0x13, 0x39, 0x13, 0x38, 0x14, 0x36, + 0x14, 0x35, 0x15, 0x33, 0x16, 0x32, 0x16, 0x31, 0x17, 0x30, 0x17, 0x2f, 0x17, 0x2f, 0x17, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x33, 0x34, 0x2e, 0x30, 0x2b, 0x2d, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x29, 0x26, 0x27, 0x25, + 0x27, 0x25, 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x23, 0x24, 0x23, + 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, 0x23, 0x23, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xcf, 0x00, 0xb9, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x85, 0x00, 0x7a, 0x00, 0x70, 0x00, 0x68, + 0x01, 0x61, 0x02, 0x5c, 0x04, 0x57, 0x04, 0x53, 0x05, 0x50, 0x06, 0x4c, 0x07, 0x4a, 0x07, 0x48, + 0x09, 0x45, 0x0a, 0x44, 0x0a, 0x42, 0x0b, 0x41, 0x10, 0x78, 0x10, 0x6d, 0x10, 0x63, 0x10, 0x5b, + 0x10, 0x53, 0x10, 0x4e, 0x11, 0x49, 0x11, 0x45, 0x11, 0x41, 0x12, 0x3f, 0x13, 0x3c, 0x13, 0x3a, + 0x13, 0x39, 0x14, 0x37, 0x14, 0x36, 0x14, 0x35, 0x15, 0x33, 0x16, 0x33, 0x16, 0x32, 0x16, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x34, 0x36, 0x30, 0x32, 0x2d, 0x2f, 0x2b, 0x2d, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, + 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd2, 0x00, 0xbe, 0x00, 0xad, 0x00, 0x9d, 0x00, 0x90, 0x00, 0x84, 0x00, 0x7a, 0x00, 0x72, + 0x00, 0x6b, 0x01, 0x65, 0x02, 0x60, 0x02, 0x5b, 0x04, 0x58, 0x04, 0x54, 0x05, 0x51, 0x05, 0x4f, + 0x07, 0x4c, 0x07, 0x4a, 0x07, 0x48, 0x09, 0x46, 0x10, 0x7a, 0x10, 0x70, 0x10, 0x67, 0x10, 0x5f, + 0x10, 0x59, 0x10, 0x53, 0x10, 0x4e, 0x11, 0x4a, 0x11, 0x46, 0x11, 0x43, 0x12, 0x41, 0x12, 0x3e, + 0x13, 0x3d, 0x13, 0x3b, 0x13, 0x39, 0x13, 0x38, 0x14, 0x37, 0x14, 0x36, 0x14, 0x35, 0x15, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x35, 0x37, 0x31, 0x33, 0x2e, 0x30, 0x2c, 0x2d, 0x2a, 0x2c, 0x29, 0x2b, 0x28, 0x29, 0x27, + 0x28, 0x27, 0x28, 0x27, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, + 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd3, 0x00, 0xc2, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x98, 0x00, 0x8d, 0x00, 0x83, 0x00, 0x7b, + 0x00, 0x73, 0x00, 0x6e, 0x01, 0x67, 0x01, 0x63, 0x02, 0x5f, 0x02, 0x5b, 0x04, 0x58, 0x04, 0x55, + 0x05, 0x52, 0x05, 0x50, 0x06, 0x4e, 0x07, 0x4c, 0x10, 0x7a, 0x10, 0x72, 0x10, 0x6a, 0x10, 0x63, + 0x10, 0x5d, 0x10, 0x57, 0x10, 0x52, 0x10, 0x4e, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x44, 0x11, 0x42, + 0x12, 0x40, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x13, 0x3a, 0x13, 0x39, 0x14, 0x38, 0x14, 0x37, + 0x0f, 0x00, 0x1f, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x0b, 0x3b, 0x17, 0x07, 0x2c, 0x00, 0x36, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x3d, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x42, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x07, 0x47, 0x0f, 0x0f, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, + 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3c, 0x37, 0x37, 0x32, 0x34, 0x30, 0x31, 0x2d, 0x2f, 0x2c, 0x2d, 0x2b, 0x2b, 0x29, 0x2b, 0x28, + 0x29, 0x27, 0x28, 0x27, 0x28, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, + 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd5, 0x00, 0xc5, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x9f, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x83, + 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6f, 0x00, 0x69, 0x01, 0x66, 0x02, 0x61, 0x02, 0x5e, 0x03, 0x5a, + 0x04, 0x58, 0x04, 0x55, 0x05, 0x53, 0x05, 0x51, 0x10, 0x7b, 0x10, 0x73, 0x10, 0x6c, 0x10, 0x66, + 0x10, 0x60, 0x10, 0x5b, 0x10, 0x56, 0x10, 0x52, 0x10, 0x4f, 0x11, 0x4b, 0x11, 0x48, 0x11, 0x45, + 0x11, 0x44, 0x12, 0x41, 0x12, 0x40, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x13, 0x3a, 0x13, 0x39, + 0x00, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x00, 0x6f, 0x02, 0x27, 0x12, 0x02, 0x25, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, 0x00, + 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x30, 0x00, 0x39, 0x00, 0x42, 0x00, 0x41, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x00, 0x7f, 0x00, 0x3f, 0x05, 0x05, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, 0x00, + 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, + 0x3c, 0x37, 0x38, 0x33, 0x34, 0x31, 0x32, 0x2f, 0x30, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x29, + 0x2b, 0x28, 0x29, 0x28, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x25, 0x27, 0x25, 0x27, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd5, 0x00, 0xc8, 0x00, 0xbb, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x92, 0x00, 0x8a, + 0x00, 0x82, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x70, 0x00, 0x6c, 0x01, 0x67, 0x01, 0x64, 0x02, 0x60, + 0x02, 0x5d, 0x04, 0x5b, 0x04, 0x58, 0x04, 0x55, 0x10, 0x7b, 0x10, 0x75, 0x10, 0x6e, 0x10, 0x68, + 0x10, 0x63, 0x10, 0x5e, 0x10, 0x5a, 0x10, 0x56, 0x10, 0x52, 0x10, 0x4f, 0x11, 0x4c, 0x11, 0x49, + 0x11, 0x47, 0x11, 0x44, 0x11, 0x43, 0x12, 0x41, 0x12, 0x3f, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x31, 0x00, 0x35, 0x00, + 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, + 0x00, 0x9f, 0x00, 0x6f, 0x00, 0x22, 0x0b, 0x09, 0x16, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, + 0x33, 0x00, 0x35, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x39, 0x00, 0x16, 0x00, 0x28, 0x00, 0x2f, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, 0x00, + 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, + 0x00, 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, + 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, 0x00, + 0x3d, 0x38, 0x39, 0x34, 0x35, 0x32, 0x33, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2b, 0x2c, 0x2b, + 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, + 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x25, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd6, 0x00, 0xca, 0x00, 0xbe, 0x00, 0xb3, 0x00, 0xaa, 0x00, 0xa0, 0x00, 0x98, 0x00, 0x90, + 0x00, 0x88, 0x00, 0x82, 0x00, 0x7d, 0x00, 0x77, 0x00, 0x71, 0x00, 0x6d, 0x01, 0x69, 0x01, 0x66, + 0x02, 0x63, 0x02, 0x60, 0x02, 0x5d, 0x04, 0x5b, 0x10, 0x7c, 0x10, 0x76, 0x10, 0x70, 0x10, 0x6a, + 0x10, 0x65, 0x10, 0x61, 0x10, 0x5d, 0x10, 0x59, 0x10, 0x55, 0x10, 0x52, 0x10, 0x4f, 0x11, 0x4c, + 0x11, 0x49, 0x11, 0x47, 0x11, 0x45, 0x11, 0x44, 0x12, 0x42, 0x12, 0x41, 0x12, 0x3f, 0x13, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, 0x00, + 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, + 0x00, 0xaf, 0x00, 0x93, 0x00, 0x58, 0x00, 0x21, 0x08, 0x0e, 0x0e, 0x02, 0x18, 0x00, 0x20, 0x00, + 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00, 0x36, 0x00, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x42, 0x00, 0x28, 0x00, 0x09, 0x00, 0x16, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, 0x00, + 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, + 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x0b, 0x00, 0x16, 0x00, + 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x3d, 0x38, 0x39, 0x35, 0x36, 0x33, 0x33, 0x30, 0x31, 0x2f, 0x30, 0x2d, 0x2e, 0x2c, 0x2d, 0x2b, + 0x2b, 0x2b, 0x2b, 0x28, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb7, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x96, + 0x00, 0x8f, 0x00, 0x88, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x77, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x6b, + 0x01, 0x67, 0x01, 0x64, 0x02, 0x62, 0x02, 0x5f, 0x10, 0x7c, 0x10, 0x77, 0x10, 0x71, 0x10, 0x6c, + 0x10, 0x67, 0x10, 0x63, 0x10, 0x5f, 0x10, 0x5c, 0x10, 0x58, 0x10, 0x55, 0x10, 0x51, 0x10, 0x4f, + 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, 0x11, 0x44, 0x11, 0x43, 0x12, 0x42, 0x12, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, + 0x25, 0x00, 0x2a, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, 0x00, + 0x00, 0xb5, 0x00, 0xa4, 0x00, 0x7a, 0x00, 0x4a, 0x00, 0x20, 0x06, 0x12, 0x0c, 0x07, 0x10, 0x00, + 0x18, 0x00, 0x1f, 0x00, 0x24, 0x00, 0x28, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x41, 0x00, 0x2f, 0x00, 0x16, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, + 0x25, 0x00, 0x2a, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, 0x00, + 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, + 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, 0x00, + 0x3d, 0x39, 0x3a, 0x36, 0x36, 0x33, 0x34, 0x31, 0x32, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2b, + 0x2d, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x27, 0x28, 0x26, 0x28, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x25, 0x27, 0x25, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd7, 0x00, 0xcc, 0x00, 0xc3, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa0, 0x00, 0x99, + 0x00, 0x93, 0x00, 0x8d, 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x74, 0x00, 0x70, + 0x00, 0x6c, 0x01, 0x69, 0x01, 0x66, 0x02, 0x63, 0x10, 0x7c, 0x10, 0x77, 0x10, 0x72, 0x10, 0x6e, + 0x10, 0x69, 0x10, 0x65, 0x10, 0x61, 0x10, 0x5d, 0x10, 0x5a, 0x10, 0x57, 0x10, 0x54, 0x10, 0x51, + 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, 0x45, 0x11, 0x44, 0x12, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, + 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, + 0x00, 0xb9, 0x00, 0xad, 0x00, 0x8f, 0x00, 0x68, 0x00, 0x42, 0x00, 0x20, 0x05, 0x14, 0x0a, 0x0b, + 0x0d, 0x04, 0x12, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x26, 0x00, 0x29, 0x00, 0x2c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x3d, 0x00, 0x2e, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, + 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, + 0x00, 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, + 0x00, 0x08, 0x02, 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, 0x00, + 0x3d, 0x39, 0x3a, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, + 0x2d, 0x2a, 0x2c, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, + 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x26, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd7, 0x00, 0xce, 0x00, 0xc5, 0x00, 0xbc, 0x00, 0xb4, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9e, + 0x00, 0x97, 0x00, 0x91, 0x00, 0x8c, 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x75, + 0x00, 0x71, 0x00, 0x6d, 0x01, 0x6b, 0x01, 0x68, 0x10, 0x7c, 0x10, 0x78, 0x10, 0x73, 0x10, 0x6f, + 0x10, 0x6b, 0x10, 0x67, 0x10, 0x63, 0x10, 0x60, 0x10, 0x5c, 0x10, 0x59, 0x10, 0x57, 0x10, 0x54, + 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, 0x46, 0x11, 0x45, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2d, 0x00, + 0x00, 0xba, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x7d, 0x00, 0x5c, 0x00, 0x3c, 0x00, 0x20, 0x04, 0x16, + 0x08, 0x0e, 0x0c, 0x07, 0x0e, 0x02, 0x13, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2d, 0x00, + 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, + 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x04, 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, + 0x3d, 0x39, 0x3a, 0x36, 0x37, 0x34, 0x35, 0x33, 0x33, 0x30, 0x32, 0x30, 0x30, 0x2e, 0x2f, 0x2d, + 0x2d, 0x2c, 0x2d, 0x2a, 0x2c, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb6, 0x00, 0xaf, 0x00, 0xa8, 0x00, 0xa1, + 0x00, 0x9c, 0x00, 0x96, 0x00, 0x90, 0x00, 0x8a, 0x00, 0x86, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x79, + 0x00, 0x75, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6b, 0x10, 0x7d, 0x10, 0x78, 0x10, 0x74, 0x10, 0x70, + 0x10, 0x6c, 0x10, 0x68, 0x10, 0x65, 0x10, 0x61, 0x10, 0x5f, 0x10, 0x5c, 0x10, 0x59, 0x10, 0x56, + 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, 0x00, + 0x00, 0xbc, 0x00, 0xb5, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x6f, 0x00, 0x52, 0x00, 0x37, 0x00, 0x20, + 0x04, 0x17, 0x07, 0x10, 0x0a, 0x0a, 0x0d, 0x05, 0x0f, 0x00, 0x14, 0x00, 0x19, 0x00, 0x1d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, 0x00, + 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, + 0x00, 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, + 0x3d, 0x3a, 0x3a, 0x37, 0x38, 0x35, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x30, 0x2f, 0x30, 0x2d, + 0x2e, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x27, 0x29, 0x27, 0x27, 0x27, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc8, 0x00, 0xc0, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa5, + 0x00, 0x9f, 0x00, 0x99, 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8a, 0x00, 0x86, 0x00, 0x81, 0x00, 0x7d, + 0x00, 0x79, 0x00, 0x76, 0x00, 0x73, 0x00, 0x70, 0x10, 0x7d, 0x10, 0x79, 0x10, 0x75, 0x10, 0x71, + 0x10, 0x6d, 0x10, 0x6a, 0x10, 0x66, 0x10, 0x63, 0x10, 0x60, 0x10, 0x5d, 0x10, 0x5b, 0x10, 0x58, + 0x10, 0x56, 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x49, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, + 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7e, 0x00, 0x64, 0x00, 0x4c, 0x00, 0x34, + 0x00, 0x20, 0x03, 0x18, 0x06, 0x11, 0x09, 0x0c, 0x0c, 0x07, 0x0e, 0x03, 0x10, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, + 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, + 0x00, 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, + 0x3d, 0x3a, 0x3a, 0x38, 0x38, 0x36, 0x36, 0x33, 0x34, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2e, + 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x28, 0x27, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd9, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc2, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xae, 0x00, 0xa8, + 0x00, 0xa2, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x92, 0x00, 0x8e, 0x00, 0x89, 0x00, 0x85, 0x00, 0x81, + 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x76, 0x00, 0x73, 0x10, 0x7d, 0x10, 0x79, 0x10, 0x75, 0x10, 0x72, + 0x10, 0x6e, 0x10, 0x6b, 0x10, 0x68, 0x10, 0x65, 0x10, 0x62, 0x10, 0x5f, 0x10, 0x5c, 0x10, 0x5a, + 0x10, 0x58, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4e, 0x11, 0x4c, 0x11, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, 0x1a, 0x00, 0x1d, 0x00, + 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9d, 0x00, 0x89, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x46, + 0x00, 0x32, 0x00, 0x20, 0x03, 0x19, 0x06, 0x13, 0x08, 0x0e, 0x0b, 0x09, 0x0d, 0x05, 0x0e, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, 0x1a, 0x00, 0x1d, 0x00, + 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, + 0x00, 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, + 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x36, 0x34, 0x35, 0x33, 0x33, 0x31, 0x32, 0x30, 0x30, 0x2f, + 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2c, 0x2d, 0x2a, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, + 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xaa, + 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x91, 0x00, 0x8d, 0x00, 0x89, 0x00, 0x84, + 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x77, 0x10, 0x7d, 0x10, 0x79, 0x10, 0x76, 0x10, 0x72, + 0x10, 0x6f, 0x10, 0x6c, 0x10, 0x69, 0x10, 0x66, 0x10, 0x63, 0x10, 0x61, 0x10, 0x5e, 0x10, 0x5c, + 0x10, 0x59, 0x10, 0x57, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4e, 0x11, 0x4c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, + 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x92, 0x00, 0x7e, 0x00, 0x6a, 0x00, 0x56, + 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x03, 0x19, 0x05, 0x14, 0x08, 0x0f, 0x0a, 0x0b, 0x0c, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, + 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, + 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, + 0x00, 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, + 0x3d, 0x3b, 0x3b, 0x39, 0x3a, 0x36, 0x36, 0x35, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x30, 0x30, + 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, + 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc4, 0x00, 0xbe, 0x00, 0xb8, 0x00, 0xb2, 0x00, 0xad, + 0x00, 0xa8, 0x00, 0xa2, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x94, 0x00, 0x90, 0x00, 0x8b, 0x00, 0x88, + 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7b, 0x10, 0x7d, 0x10, 0x7a, 0x10, 0x76, 0x10, 0x73, + 0x10, 0x70, 0x10, 0x6d, 0x10, 0x6a, 0x10, 0x67, 0x10, 0x65, 0x10, 0x62, 0x10, 0x5f, 0x10, 0x5d, + 0x10, 0x5b, 0x10, 0x59, 0x10, 0x56, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, + 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa8, 0x00, 0x99, 0x00, 0x87, 0x00, 0x75, 0x00, 0x62, + 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x02, 0x1a, 0x05, 0x15, 0x07, 0x10, 0x09, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1a, 0x00, + 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, + 0x00, 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, + 0x3d, 0x3b, 0x3b, 0x39, 0x39, 0x36, 0x37, 0x35, 0x36, 0x33, 0x33, 0x33, 0x33, 0x30, 0x31, 0x30, + 0x30, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcc, 0x00, 0xc6, 0x00, 0xc0, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xaf, + 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x00, 0x8b, + 0x00, 0x88, 0x00, 0x83, 0x00, 0x81, 0x00, 0x7d, 0x10, 0x7d, 0x10, 0x7a, 0x10, 0x77, 0x10, 0x74, + 0x10, 0x71, 0x10, 0x6e, 0x10, 0x6b, 0x10, 0x68, 0x10, 0x65, 0x10, 0x63, 0x10, 0x61, 0x10, 0x5e, + 0x10, 0x5c, 0x10, 0x5a, 0x10, 0x58, 0x10, 0x56, 0x10, 0x55, 0x10, 0x52, 0x10, 0x51, 0x10, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, + 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, + 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2d, 0x00, 0x1f, 0x02, 0x1a, 0x04, 0x16, 0x06, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, 0x00, + 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, + 0x00, 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, + 0x27, 0x76, 0x1a, 0x79, 0x16, 0x7b, 0x15, 0x7c, 0x14, 0x7c, 0x13, 0x7d, 0x13, 0x7d, 0x13, 0x7d, + 0x12, 0x7d, 0x12, 0x7d, 0x12, 0x7e, 0x12, 0x7e, 0x12, 0x7e, 0x12, 0x7e, 0x12, 0x7e, 0x11, 0x7e, + 0x11, 0x7e, 0x11, 0x7e, 0x11, 0x7f, 0x11, 0x7f, 0x13, 0x5f, 0x10, 0x6f, 0x10, 0x74, 0x10, 0x77, + 0x10, 0x78, 0x10, 0x7a, 0x10, 0x7a, 0x10, 0x7b, 0x10, 0x7b, 0x10, 0x7c, 0x10, 0x7c, 0x10, 0x7c, + 0x10, 0x7c, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, + 0x00, 0x90, 0x02, 0x81, 0x06, 0x80, 0x09, 0x7f, 0x0a, 0x7f, 0x0b, 0x7f, 0x0c, 0x7f, 0x0d, 0x7f, + 0x0d, 0x7f, 0x0d, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0f, 0x7f, + 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x95, 0x00, 0x86, 0x00, 0x77, + 0x00, 0x67, 0x00, 0x57, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2c, 0x00, 0x1f, 0x02, 0x1b, 0x04, 0x16, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, + 0x1c, 0x00, 0x16, 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, + 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, + 0x00, 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, + 0x32, 0x5f, 0x25, 0x68, 0x1f, 0x6d, 0x1c, 0x71, 0x19, 0x72, 0x18, 0x74, 0x17, 0x76, 0x16, 0x77, + 0x16, 0x77, 0x15, 0x78, 0x15, 0x79, 0x15, 0x79, 0x14, 0x7a, 0x14, 0x7a, 0x13, 0x7a, 0x13, 0x7b, + 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, 0x17, 0x46, 0x11, 0x58, 0x10, 0x63, 0x10, 0x69, + 0x10, 0x6d, 0x10, 0x70, 0x10, 0x72, 0x10, 0x73, 0x10, 0x75, 0x10, 0x76, 0x10, 0x77, 0x10, 0x77, + 0x10, 0x78, 0x10, 0x78, 0x10, 0x79, 0x10, 0x79, 0x10, 0x79, 0x10, 0x7a, 0x10, 0x7a, 0x10, 0x7a, + 0x00, 0xbd, 0x00, 0xa4, 0x00, 0x97, 0x02, 0x91, 0x04, 0x8d, 0x05, 0x8a, 0x06, 0x89, 0x07, 0x88, + 0x08, 0x86, 0x09, 0x86, 0x09, 0x85, 0x0a, 0x85, 0x0a, 0x84, 0x0b, 0x84, 0x0b, 0x83, 0x0b, 0x83, + 0x0c, 0x83, 0x0c, 0x83, 0x0c, 0x82, 0x0c, 0x82, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xb0, 0x00, 0xa6, 0x00, 0x9a, 0x00, 0x8d, 0x00, 0x7f, + 0x00, 0x70, 0x00, 0x61, 0x00, 0x53, 0x00, 0x45, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x02, 0x1b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, 0x2b, 0x00, 0x26, 0x00, + 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, + 0x00, 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, + 0x36, 0x56, 0x2a, 0x5f, 0x24, 0x65, 0x20, 0x68, 0x1e, 0x6c, 0x1c, 0x6e, 0x1b, 0x70, 0x19, 0x71, + 0x18, 0x72, 0x18, 0x73, 0x18, 0x75, 0x17, 0x75, 0x16, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, 0x77, + 0x15, 0x77, 0x15, 0x77, 0x15, 0x78, 0x15, 0x79, 0x1a, 0x39, 0x13, 0x4a, 0x11, 0x55, 0x10, 0x5e, + 0x10, 0x63, 0x10, 0x67, 0x10, 0x6a, 0x10, 0x6c, 0x10, 0x6e, 0x10, 0x70, 0x10, 0x71, 0x10, 0x72, + 0x10, 0x73, 0x10, 0x74, 0x10, 0x75, 0x10, 0x75, 0x10, 0x76, 0x10, 0x76, 0x10, 0x77, 0x10, 0x77, + 0x00, 0xcc, 0x00, 0xb5, 0x00, 0xa8, 0x00, 0x9e, 0x01, 0x99, 0x02, 0x94, 0x03, 0x92, 0x04, 0x8f, + 0x05, 0x8d, 0x06, 0x8c, 0x06, 0x8b, 0x07, 0x8a, 0x07, 0x89, 0x08, 0x88, 0x08, 0x88, 0x09, 0x87, + 0x09, 0x87, 0x09, 0x86, 0x0a, 0x86, 0x0a, 0x86, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xa9, 0x00, 0x9e, 0x00, 0x92, 0x00, 0x85, + 0x00, 0x78, 0x00, 0x6a, 0x00, 0x5c, 0x00, 0x4f, 0x00, 0x42, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, 0x00, + 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, + 0x00, 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, + 0x38, 0x52, 0x2e, 0x59, 0x28, 0x5f, 0x24, 0x63, 0x22, 0x67, 0x1f, 0x69, 0x1e, 0x6b, 0x1c, 0x6d, + 0x1b, 0x6e, 0x1a, 0x70, 0x1a, 0x71, 0x19, 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x73, 0x17, 0x75, + 0x17, 0x76, 0x16, 0x76, 0x15, 0x76, 0x15, 0x76, 0x1b, 0x33, 0x15, 0x41, 0x12, 0x4c, 0x11, 0x54, + 0x10, 0x5b, 0x10, 0x5f, 0x10, 0x63, 0x10, 0x66, 0x10, 0x68, 0x10, 0x6a, 0x10, 0x6c, 0x10, 0x6e, + 0x10, 0x6f, 0x10, 0x70, 0x10, 0x71, 0x10, 0x72, 0x10, 0x72, 0x10, 0x73, 0x10, 0x74, 0x10, 0x74, + 0x00, 0xd1, 0x00, 0xc0, 0x00, 0xb3, 0x00, 0xaa, 0x00, 0xa2, 0x00, 0x9d, 0x01, 0x99, 0x02, 0x96, + 0x02, 0x94, 0x03, 0x92, 0x04, 0x90, 0x05, 0x8f, 0x05, 0x8d, 0x06, 0x8d, 0x06, 0x8c, 0x07, 0x8b, + 0x07, 0x8a, 0x07, 0x8a, 0x07, 0x89, 0x08, 0x89, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x47, 0x0f, 0x0f, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, + 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x47, 0x00, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, + 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x39, 0x4f, 0x31, 0x55, 0x2b, 0x5b, 0x27, 0x5f, 0x24, 0x63, 0x22, 0x65, 0x20, 0x68, 0x1f, 0x69, + 0x1d, 0x6b, 0x1d, 0x6d, 0x1b, 0x6d, 0x1a, 0x6e, 0x1a, 0x6f, 0x1a, 0x71, 0x19, 0x72, 0x18, 0x72, + 0x18, 0x72, 0x18, 0x72, 0x18, 0x73, 0x18, 0x74, 0x1c, 0x2f, 0x16, 0x3b, 0x13, 0x44, 0x11, 0x4d, + 0x11, 0x53, 0x10, 0x59, 0x10, 0x5d, 0x10, 0x60, 0x10, 0x63, 0x10, 0x65, 0x10, 0x67, 0x10, 0x69, + 0x10, 0x6b, 0x10, 0x6c, 0x10, 0x6d, 0x10, 0x6e, 0x10, 0x6f, 0x10, 0x70, 0x10, 0x71, 0x10, 0x71, + 0x00, 0xd5, 0x00, 0xc6, 0x00, 0xbb, 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x00, 0xa0, 0x01, 0x9c, + 0x01, 0x9a, 0x02, 0x97, 0x02, 0x95, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, 0x05, 0x90, 0x05, 0x8e, + 0x06, 0x8e, 0x06, 0x8d, 0x06, 0x8c, 0x06, 0x8c, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x00, 0x3f, 0x05, 0x05, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, 0x00, + 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0f, 0x00, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, + 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x4c, 0x33, 0x53, 0x2e, 0x58, 0x29, 0x5c, 0x27, 0x5f, 0x24, 0x62, 0x22, 0x64, 0x20, 0x66, + 0x20, 0x68, 0x1e, 0x69, 0x1d, 0x6a, 0x1d, 0x6c, 0x1c, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x1a, 0x6f, + 0x1a, 0x71, 0x19, 0x72, 0x18, 0x72, 0x18, 0x72, 0x1d, 0x2d, 0x17, 0x37, 0x14, 0x40, 0x12, 0x47, + 0x11, 0x4e, 0x11, 0x53, 0x10, 0x57, 0x10, 0x5b, 0x10, 0x5e, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, + 0x10, 0x67, 0x10, 0x68, 0x10, 0x6a, 0x10, 0x6b, 0x10, 0x6c, 0x10, 0x6d, 0x10, 0x6e, 0x10, 0x6f, + 0x00, 0xd6, 0x00, 0xcb, 0x00, 0xc0, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xab, 0x00, 0xa6, 0x00, 0xa2, + 0x00, 0x9f, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x98, 0x02, 0x96, 0x03, 0x94, 0x03, 0x93, 0x03, 0x92, + 0x04, 0x91, 0x05, 0x90, 0x05, 0x8f, 0x05, 0x8f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, + 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x05, 0x05, 0x00, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, + 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x4b, 0x34, 0x51, 0x2f, 0x55, 0x2c, 0x59, 0x29, 0x5c, 0x26, 0x60, 0x24, 0x61, 0x23, 0x64, + 0x21, 0x65, 0x20, 0x67, 0x1f, 0x69, 0x1d, 0x69, 0x1d, 0x6a, 0x1d, 0x6c, 0x1c, 0x6d, 0x1b, 0x6d, + 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6f, 0x1a, 0x71, 0x1e, 0x2b, 0x18, 0x34, 0x15, 0x3b, 0x13, 0x43, + 0x12, 0x49, 0x11, 0x4e, 0x11, 0x52, 0x10, 0x56, 0x10, 0x5a, 0x10, 0x5d, 0x10, 0x5f, 0x10, 0x61, + 0x10, 0x63, 0x10, 0x65, 0x10, 0x66, 0x10, 0x68, 0x10, 0x69, 0x10, 0x6a, 0x10, 0x6b, 0x10, 0x6c, + 0x00, 0xd7, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbd, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xab, 0x00, 0xa7, + 0x00, 0xa4, 0x00, 0xa1, 0x01, 0x9e, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x98, 0x02, 0x97, 0x02, 0x96, + 0x03, 0x94, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x0b, 0x00, 0x16, 0x00, + 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x1c, 0x00, 0x00, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, + 0x00, 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x49, 0x36, 0x4f, 0x31, 0x54, 0x2d, 0x57, 0x2a, 0x5a, 0x28, 0x5c, 0x26, 0x60, 0x24, 0x61, + 0x23, 0x64, 0x21, 0x65, 0x20, 0x65, 0x20, 0x68, 0x1e, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x6b, + 0x1c, 0x6d, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1e, 0x2a, 0x19, 0x32, 0x16, 0x39, 0x13, 0x3f, + 0x12, 0x45, 0x11, 0x4a, 0x11, 0x4e, 0x11, 0x52, 0x10, 0x56, 0x10, 0x59, 0x10, 0x5c, 0x10, 0x5d, + 0x10, 0x60, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x66, 0x10, 0x67, 0x10, 0x68, 0x10, 0x69, + 0x00, 0xd8, 0x00, 0xcf, 0x00, 0xc7, 0x00, 0xc0, 0x00, 0xba, 0x00, 0xb5, 0x00, 0xb0, 0x00, 0xac, + 0x00, 0xa8, 0x00, 0xa5, 0x00, 0xa2, 0x00, 0xa0, 0x01, 0x9e, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x99, + 0x02, 0x97, 0x02, 0x96, 0x03, 0x95, 0x03, 0x94, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, + 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x2a, 0x00, 0x09, 0x00, 0x00, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, + 0x00, 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x48, 0x37, 0x4d, 0x32, 0x51, 0x2f, 0x55, 0x2c, 0x58, 0x29, 0x5b, 0x27, 0x5d, 0x26, 0x60, + 0x24, 0x60, 0x23, 0x63, 0x22, 0x65, 0x20, 0x65, 0x20, 0x66, 0x1f, 0x68, 0x1e, 0x69, 0x1d, 0x69, + 0x1d, 0x69, 0x1d, 0x6b, 0x1c, 0x6d, 0x1b, 0x6d, 0x1e, 0x29, 0x1a, 0x30, 0x17, 0x36, 0x14, 0x3c, + 0x13, 0x41, 0x12, 0x46, 0x11, 0x4a, 0x11, 0x4f, 0x11, 0x52, 0x10, 0x55, 0x10, 0x58, 0x10, 0x5a, + 0x10, 0x5c, 0x10, 0x5f, 0x10, 0x60, 0x10, 0x62, 0x10, 0x63, 0x10, 0x65, 0x10, 0x65, 0x10, 0x67, + 0x00, 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb4, 0x00, 0xb0, + 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa3, 0x00, 0xa1, 0x01, 0x9f, 0x01, 0x9d, 0x01, 0x9b, + 0x02, 0x9a, 0x02, 0x99, 0x02, 0x98, 0x02, 0x97, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, + 0x00, 0x08, 0x02, 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x31, 0x00, 0x19, 0x00, 0x00, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, + 0x00, 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x48, 0x37, 0x4c, 0x33, 0x50, 0x30, 0x54, 0x2d, 0x57, 0x2a, 0x59, 0x29, 0x5c, 0x27, 0x5d, + 0x26, 0x60, 0x24, 0x60, 0x23, 0x62, 0x22, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x67, 0x1f, 0x69, + 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x6a, 0x1f, 0x28, 0x1a, 0x2e, 0x17, 0x34, 0x15, 0x39, + 0x13, 0x3f, 0x13, 0x43, 0x12, 0x48, 0x11, 0x4b, 0x11, 0x4f, 0x11, 0x52, 0x10, 0x55, 0x10, 0x57, + 0x10, 0x59, 0x10, 0x5c, 0x10, 0x5d, 0x10, 0x5f, 0x10, 0x61, 0x10, 0x62, 0x10, 0x63, 0x10, 0x64, + 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xcc, 0x00, 0xc6, 0x00, 0xc1, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xb3, + 0x00, 0xaf, 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa2, 0x00, 0xa0, 0x01, 0x9e, + 0x01, 0x9d, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, + 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x04, 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x00, 0x00, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, + 0x00, 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x47, 0x38, 0x4b, 0x34, 0x4f, 0x31, 0x53, 0x2e, 0x55, 0x2c, 0x57, 0x2a, 0x5a, 0x28, 0x5c, + 0x27, 0x5d, 0x26, 0x60, 0x23, 0x60, 0x23, 0x61, 0x23, 0x64, 0x21, 0x65, 0x20, 0x65, 0x20, 0x65, + 0x20, 0x68, 0x1e, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1f, 0x27, 0x1b, 0x2d, 0x18, 0x33, 0x16, 0x38, + 0x14, 0x3c, 0x13, 0x41, 0x12, 0x44, 0x11, 0x48, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, + 0x10, 0x57, 0x10, 0x59, 0x10, 0x5b, 0x10, 0x5c, 0x10, 0x5e, 0x10, 0x5f, 0x10, 0x61, 0x10, 0x62, + 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc8, 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xba, 0x00, 0xb6, + 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa7, 0x00, 0xa5, 0x00, 0xa3, 0x00, 0xa1, + 0x01, 0xa0, 0x01, 0x9e, 0x01, 0x9d, 0x02, 0x9c, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, + 0x00, 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, + 0x00, 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x47, 0x38, 0x4b, 0x35, 0x4e, 0x32, 0x51, 0x2f, 0x53, 0x2d, 0x57, 0x2b, 0x58, 0x2a, 0x5b, + 0x27, 0x5c, 0x27, 0x5d, 0x25, 0x60, 0x23, 0x60, 0x23, 0x61, 0x23, 0x64, 0x21, 0x65, 0x20, 0x65, + 0x20, 0x65, 0x20, 0x66, 0x1f, 0x69, 0x1e, 0x69, 0x1f, 0x27, 0x1b, 0x2c, 0x18, 0x31, 0x16, 0x36, + 0x14, 0x3a, 0x13, 0x3e, 0x13, 0x42, 0x12, 0x45, 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, + 0x10, 0x54, 0x10, 0x56, 0x10, 0x58, 0x10, 0x5a, 0x10, 0x5c, 0x10, 0x5d, 0x10, 0x5e, 0x10, 0x60, + 0x00, 0xd9, 0x00, 0xd4, 0x00, 0xce, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xc1, 0x00, 0xbd, 0x00, 0xb9, + 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa8, 0x00, 0xa6, 0x00, 0xa4, + 0x00, 0xa2, 0x00, 0xa0, 0x01, 0x9f, 0x01, 0x9e, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, + 0x00, 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x2e, + 0x00, 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x46, 0x39, 0x4a, 0x35, 0x4d, 0x32, 0x50, 0x30, 0x53, 0x2e, 0x55, 0x2c, 0x57, 0x2a, 0x58, + 0x29, 0x5c, 0x27, 0x5c, 0x27, 0x5d, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x63, 0x22, 0x65, + 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x67, 0x1f, 0x27, 0x1c, 0x2c, 0x19, 0x30, 0x17, 0x35, + 0x15, 0x39, 0x14, 0x3d, 0x13, 0x40, 0x12, 0x44, 0x12, 0x47, 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, + 0x11, 0x51, 0x10, 0x54, 0x10, 0x56, 0x10, 0x58, 0x10, 0x59, 0x10, 0x5b, 0x10, 0x5c, 0x10, 0x5e, + 0x00, 0xda, 0x00, 0xd4, 0x00, 0xd0, 0x00, 0xcb, 0x00, 0xc7, 0x00, 0xc2, 0x00, 0xbe, 0x00, 0xbb, + 0x00, 0xb8, 0x00, 0xb5, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa8, 0x00, 0xa6, + 0x00, 0xa4, 0x00, 0xa3, 0x00, 0xa1, 0x01, 0xa0, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, + 0x00, 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x20, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x46, 0x39, 0x49, 0x36, 0x4c, 0x33, 0x4f, 0x31, 0x52, 0x2e, 0x53, 0x2d, 0x57, 0x2b, 0x57, + 0x2a, 0x59, 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x62, + 0x22, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x1f, 0x26, 0x1c, 0x2b, 0x19, 0x2f, 0x17, 0x33, + 0x16, 0x37, 0x14, 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x44, 0x11, 0x47, 0x11, 0x4a, 0x11, 0x4d, + 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x55, 0x10, 0x57, 0x10, 0x59, 0x10, 0x5a, 0x10, 0x5b, + 0x00, 0xda, 0x00, 0xd5, 0x00, 0xd1, 0x00, 0xcc, 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc1, 0x00, 0xbd, + 0x00, 0xba, 0x00, 0xb7, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa9, + 0x00, 0xa7, 0x00, 0xa5, 0x00, 0xa4, 0x00, 0xa2, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, + 0x00, 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x14, + 0x00, 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x45, 0x39, 0x48, 0x36, 0x4b, 0x34, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x55, 0x2c, 0x57, + 0x2a, 0x57, 0x2a, 0x5a, 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x61, 0x22, 0x64, 0x21, 0x65, 0x20, 0x65, 0x20, 0x26, 0x1c, 0x2a, 0x1a, 0x2e, 0x18, 0x32, + 0x16, 0x36, 0x14, 0x39, 0x13, 0x3d, 0x13, 0x40, 0x12, 0x43, 0x12, 0x45, 0x11, 0x48, 0x11, 0x4b, + 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x53, 0x10, 0x55, 0x10, 0x56, 0x10, 0x58, 0x10, 0x5a, + 0x00, 0xda, 0x00, 0xd6, 0x00, 0xd1, 0x00, 0xcd, 0x00, 0xc9, 0x00, 0xc6, 0x00, 0xc2, 0x00, 0xbf, + 0x00, 0xbc, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, + 0x00, 0xa9, 0x00, 0xa7, 0x00, 0xa6, 0x00, 0xa4, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, + 0x00, 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x00, 0x00, 0x0a, + 0x00, 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x45, 0x3a, 0x48, 0x37, 0x4a, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x53, 0x2e, 0x53, 0x2e, 0x56, + 0x2b, 0x57, 0x2a, 0x58, 0x29, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x61, 0x23, 0x64, 0x21, 0x65, 0x20, 0x26, 0x1d, 0x2a, 0x1a, 0x2d, 0x18, 0x31, + 0x17, 0x35, 0x15, 0x38, 0x14, 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x44, 0x12, 0x46, 0x11, 0x49, + 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x53, 0x10, 0x55, 0x10, 0x56, 0x10, 0x58, + 0x00, 0xda, 0x00, 0xd6, 0x00, 0xd2, 0x00, 0xce, 0x00, 0xcb, 0x00, 0xc7, 0x00, 0xc4, 0x00, 0xc1, + 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, + 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x00, 0xa6, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, + 0x00, 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x01, + 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x45, 0x3a, 0x47, 0x38, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x54, + 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x59, 0x29, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x63, 0x20, 0x25, 0x1d, 0x29, 0x1a, 0x2d, 0x18, 0x30, + 0x17, 0x33, 0x16, 0x37, 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, 0x13, 0x42, 0x12, 0x44, 0x11, 0x47, + 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x53, 0x10, 0x55, 0x10, 0x56, + 0x00, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xc8, 0x00, 0xc5, 0x00, 0xc2, + 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xba, 0x00, 0xb7, 0x00, 0xb5, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, + 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, + 0x00, 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x44, 0x3a, 0x47, 0x38, 0x4a, 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, + 0x2e, 0x56, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x5a, 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5f, + 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x20, 0x25, 0x1d, 0x29, 0x1b, 0x2c, 0x18, 0x2f, + 0x17, 0x33, 0x16, 0x36, 0x14, 0x39, 0x14, 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x43, 0x12, 0x45, + 0x11, 0x47, 0x11, 0x4a, 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x52, 0x10, 0x54, + 0x00, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xd0, 0x00, 0xcc, 0x00, 0xc9, 0x00, 0xc6, 0x00, 0xc3, + 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb9, 0x00, 0xb7, 0x00, 0xb5, 0x00, 0xb3, 0x00, 0xb0, + 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xaa, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, + 0x00, 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x4a, 0x35, 0x4b, 0x34, 0x4f, 0x31, 0x4f, 0x31, 0x53, 0x2e, 0x53, + 0x2e, 0x54, 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x2a, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5f, 0x24, 0x60, 0x23, 0x60, 0x23, 0x60, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x19, 0x2f, + 0x17, 0x32, 0x16, 0x35, 0x15, 0x38, 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, 0x13, 0x42, 0x12, 0x44, + 0x12, 0x46, 0x11, 0x48, 0x11, 0x4a, 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x52, + 0x00, 0xdb, 0x00, 0xd7, 0x00, 0xd4, 0x00, 0xd0, 0x00, 0xcd, 0x00, 0xca, 0x00, 0xc7, 0x00, 0xc5, + 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, 0x00, 0xb2, + 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xac, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, + 0x00, 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, 0x00, + 0x07, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x51, 0x2f, 0x53, + 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x29, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5c, 0x26, 0x5f, 0x24, 0x60, 0x23, 0x60, 0x20, 0x25, 0x1d, 0x28, 0x1c, 0x2b, 0x1a, 0x2e, + 0x18, 0x31, 0x17, 0x34, 0x16, 0x37, 0x14, 0x39, 0x13, 0x3b, 0x13, 0x3e, 0x13, 0x40, 0x12, 0x42, + 0x12, 0x45, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4a, 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, + 0x00, 0xdb, 0x00, 0xd8, 0x00, 0xd4, 0x00, 0xd1, 0x00, 0xce, 0x00, 0xcb, 0x00, 0xc8, 0x00, 0xc6, + 0x00, 0xc3, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xba, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, + 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x56, 0x7f, 0x47, 0x63, 0x44, 0x57, 0x43, 0x52, + 0x43, 0x4e, 0x42, 0x4c, 0x42, 0x4a, 0x42, 0x49, 0x42, 0x48, 0x42, 0x47, 0x42, 0x46, 0x42, 0x45, + 0x42, 0x45, 0x42, 0x45, 0x42, 0x44, 0x42, 0x44, 0x42, 0x44, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, + 0x4c, 0x7f, 0x27, 0x4d, 0x2f, 0x46, 0x33, 0x44, 0x36, 0x43, 0x37, 0x43, 0x39, 0x42, 0x3a, 0x41, + 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, 0x3b, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, + 0x3c, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x8e, 0x7f, 0x47, 0x4d, 0x44, 0x46, 0x43, 0x44, + 0x43, 0x43, 0x42, 0x43, 0x42, 0x42, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, + 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x85, 0x1b, 0x60, 0x27, 0x54, 0x2d, 0x4e, 0x30, + 0x4b, 0x33, 0x49, 0x34, 0x47, 0x35, 0x46, 0x37, 0x45, 0x38, 0x45, 0x38, 0x44, 0x39, 0x43, 0x39, + 0x43, 0x3a, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, + 0x09, 0x43, 0x16, 0x3f, 0x1f, 0x3e, 0x25, 0x3e, 0x29, 0x3e, 0x2c, 0x3e, 0x2f, 0x3e, 0x30, 0x3e, + 0x32, 0x3e, 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x36, 0x3e, 0x37, 0x3e, 0x37, 0x3e, + 0x38, 0x3e, 0x38, 0x3e, 0x38, 0x3e, 0x39, 0x3e, 0x59, 0x31, 0x4f, 0x38, 0x4b, 0x3a, 0x48, 0x3b, + 0x47, 0x3c, 0x46, 0x3c, 0x45, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x4f, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x96, 0x05, 0x74, 0x0f, 0x63, 0x17, 0x5a, 0x1d, + 0x54, 0x21, 0x51, 0x24, 0x4e, 0x27, 0x4c, 0x29, 0x4a, 0x2b, 0x49, 0x2d, 0x49, 0x2e, 0x48, 0x2f, + 0x48, 0x31, 0x47, 0x31, 0x46, 0x32, 0x46, 0x33, 0x44, 0x33, 0x44, 0x34, 0x43, 0x34, 0x43, 0x35, + 0x09, 0x43, 0x11, 0x3e, 0x18, 0x3c, 0x1d, 0x3c, 0x22, 0x3c, 0x25, 0x3c, 0x27, 0x3c, 0x2a, 0x3c, + 0x2c, 0x3d, 0x2d, 0x3d, 0x2f, 0x3d, 0x30, 0x3e, 0x31, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, + 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x5b, 0x2a, 0x53, 0x31, 0x4f, 0x34, 0x4c, 0x36, + 0x4b, 0x38, 0x4a, 0x39, 0x48, 0x39, 0x47, 0x3a, 0x46, 0x3b, 0x46, 0x3b, 0x46, 0x3c, 0x46, 0x3c, + 0x46, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x56, 0x00, 0x45, 0x00, 0x22, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8f, 0x00, 0x73, 0x00, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x9e, 0x01, 0x81, 0x06, 0x6f, 0x0d, 0x64, 0x12, + 0x5d, 0x16, 0x58, 0x1a, 0x54, 0x1d, 0x53, 0x20, 0x50, 0x22, 0x4e, 0x24, 0x4c, 0x26, 0x4b, 0x27, + 0x4a, 0x29, 0x49, 0x2a, 0x49, 0x2b, 0x49, 0x2c, 0x48, 0x2d, 0x48, 0x2e, 0x48, 0x2f, 0x47, 0x2f, + 0x09, 0x44, 0x0f, 0x3e, 0x14, 0x3c, 0x19, 0x3c, 0x1d, 0x3b, 0x20, 0x3b, 0x23, 0x3b, 0x25, 0x3c, + 0x27, 0x3c, 0x28, 0x3c, 0x2a, 0x3b, 0x2c, 0x3b, 0x2c, 0x3b, 0x2d, 0x3b, 0x2f, 0x3c, 0x2f, 0x3c, + 0x30, 0x3d, 0x31, 0x3e, 0x32, 0x3e, 0x32, 0x3e, 0x5c, 0x28, 0x56, 0x2d, 0x52, 0x31, 0x4f, 0x33, + 0x4e, 0x35, 0x4b, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x49, 0x39, 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, + 0x46, 0x39, 0x46, 0x3a, 0x46, 0x3a, 0x46, 0x3b, 0x46, 0x3c, 0x46, 0x3c, 0x46, 0x3d, 0x45, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x59, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x84, 0x00, 0x5b, 0x00, 0x2a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa4, 0x00, 0x8a, 0x03, 0x78, 0x07, 0x6d, 0x0c, + 0x65, 0x10, 0x60, 0x13, 0x5b, 0x16, 0x57, 0x19, 0x55, 0x1b, 0x53, 0x1e, 0x52, 0x1f, 0x4f, 0x22, + 0x4d, 0x23, 0x4c, 0x24, 0x4b, 0x25, 0x4b, 0x27, 0x4a, 0x28, 0x49, 0x29, 0x49, 0x29, 0x49, 0x2b, + 0x09, 0x45, 0x0d, 0x40, 0x11, 0x3d, 0x16, 0x3c, 0x19, 0x3b, 0x1d, 0x3b, 0x1f, 0x3b, 0x21, 0x3a, + 0x23, 0x3b, 0x25, 0x3b, 0x26, 0x3c, 0x28, 0x3c, 0x29, 0x3c, 0x2a, 0x3b, 0x2b, 0x3b, 0x2c, 0x3b, + 0x2d, 0x3b, 0x2e, 0x3b, 0x2e, 0x3b, 0x2f, 0x3b, 0x5d, 0x27, 0x57, 0x2c, 0x54, 0x2f, 0x52, 0x31, + 0x4f, 0x32, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x48, 0x39, + 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x3a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5b, 0x00, 0x55, 0x00, 0x42, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x8d, 0x00, 0x6f, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa7, 0x00, 0x91, 0x01, 0x80, 0x04, 0x75, 0x08, + 0x6c, 0x0b, 0x65, 0x0e, 0x61, 0x11, 0x5d, 0x14, 0x59, 0x16, 0x56, 0x19, 0x55, 0x1a, 0x54, 0x1c, + 0x53, 0x1e, 0x51, 0x1f, 0x4f, 0x21, 0x4d, 0x22, 0x4c, 0x23, 0x4b, 0x24, 0x4b, 0x25, 0x4b, 0x26, + 0x09, 0x45, 0x0d, 0x41, 0x10, 0x3e, 0x14, 0x3d, 0x17, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1e, 0x3b, + 0x20, 0x3b, 0x22, 0x3a, 0x23, 0x3a, 0x25, 0x3b, 0x26, 0x3c, 0x28, 0x3c, 0x28, 0x3c, 0x29, 0x3c, + 0x2a, 0x3c, 0x2b, 0x3b, 0x2c, 0x3b, 0x2d, 0x3b, 0x5d, 0x26, 0x59, 0x2a, 0x56, 0x2d, 0x53, 0x2f, + 0x51, 0x31, 0x4f, 0x32, 0x4f, 0x33, 0x4d, 0x35, 0x4b, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x37, + 0x4a, 0x38, 0x49, 0x39, 0x48, 0x39, 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa9, 0x00, 0x96, 0x00, 0x86, 0x02, 0x7a, 0x05, + 0x72, 0x08, 0x6b, 0x0b, 0x65, 0x0e, 0x62, 0x10, 0x5f, 0x12, 0x5b, 0x14, 0x58, 0x16, 0x56, 0x18, + 0x55, 0x1a, 0x54, 0x1b, 0x53, 0x1d, 0x52, 0x1e, 0x50, 0x1f, 0x4f, 0x20, 0x4d, 0x21, 0x4c, 0x22, + 0x09, 0x46, 0x0c, 0x41, 0x0f, 0x3f, 0x12, 0x3d, 0x15, 0x3c, 0x17, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, + 0x1e, 0x3b, 0x1f, 0x3b, 0x21, 0x3a, 0x22, 0x3a, 0x24, 0x3a, 0x25, 0x3a, 0x26, 0x3b, 0x27, 0x3c, + 0x28, 0x3c, 0x29, 0x3c, 0x2a, 0x3c, 0x2a, 0x3c, 0x5e, 0x26, 0x5a, 0x29, 0x57, 0x2c, 0x53, 0x2e, + 0x53, 0x30, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x48, 0x39, 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x95, 0x00, 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xab, 0x00, 0x9a, 0x00, 0x8b, 0x01, 0x7f, 0x03, + 0x77, 0x06, 0x71, 0x08, 0x6a, 0x0b, 0x66, 0x0d, 0x63, 0x0f, 0x60, 0x11, 0x5d, 0x13, 0x59, 0x14, + 0x57, 0x16, 0x56, 0x18, 0x55, 0x19, 0x54, 0x1b, 0x53, 0x1c, 0x53, 0x1d, 0x51, 0x1e, 0x50, 0x1f, + 0x0a, 0x46, 0x0c, 0x42, 0x0e, 0x3f, 0x11, 0x3f, 0x13, 0x3c, 0x16, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, + 0x1c, 0x3a, 0x1e, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3a, 0x23, 0x3a, 0x24, 0x3a, 0x25, 0x39, + 0x26, 0x3a, 0x27, 0x3c, 0x27, 0x3c, 0x28, 0x3c, 0x5e, 0x26, 0x5a, 0x29, 0x57, 0x2b, 0x55, 0x2d, + 0x53, 0x2e, 0x53, 0x30, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x32, 0x4f, 0x34, 0x4d, 0x35, 0x4b, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x38, 0x4a, 0x39, 0x48, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xac, 0x00, 0x9d, 0x00, 0x8f, 0x01, 0x85, 0x02, + 0x7b, 0x04, 0x75, 0x06, 0x6f, 0x08, 0x69, 0x0b, 0x66, 0x0d, 0x63, 0x0e, 0x61, 0x10, 0x5e, 0x12, + 0x5b, 0x13, 0x58, 0x15, 0x57, 0x16, 0x56, 0x18, 0x55, 0x19, 0x55, 0x1a, 0x54, 0x1b, 0x53, 0x1c, + 0x0a, 0x47, 0x0b, 0x42, 0x0e, 0x40, 0x10, 0x3f, 0x12, 0x3d, 0x15, 0x3c, 0x17, 0x3c, 0x18, 0x3c, + 0x1a, 0x3b, 0x1c, 0x3a, 0x1d, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x23, 0x3a, + 0x24, 0x3a, 0x25, 0x39, 0x26, 0x3a, 0x27, 0x3b, 0x5e, 0x26, 0x5b, 0x28, 0x57, 0x2a, 0x57, 0x2c, + 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x35, + 0x4b, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, 0x3c, 0x00, 0x26, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xad, 0x00, 0xa0, 0x00, 0x92, 0x00, 0x89, 0x02, + 0x7f, 0x03, 0x78, 0x05, 0x73, 0x07, 0x6e, 0x09, 0x69, 0x0b, 0x66, 0x0c, 0x64, 0x0e, 0x62, 0x0f, + 0x60, 0x11, 0x5c, 0x12, 0x59, 0x14, 0x58, 0x15, 0x57, 0x16, 0x56, 0x18, 0x55, 0x18, 0x55, 0x1a, + 0x0a, 0x47, 0x0b, 0x43, 0x0d, 0x41, 0x0f, 0x3f, 0x11, 0x3f, 0x13, 0x3c, 0x15, 0x3b, 0x17, 0x3c, + 0x18, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, 0x1d, 0x3a, 0x1f, 0x3b, 0x1f, 0x3b, 0x21, 0x3b, 0x21, 0x3b, + 0x22, 0x3a, 0x24, 0x3a, 0x24, 0x3a, 0x24, 0x39, 0x5e, 0x26, 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2b, + 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2f, 0x52, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x32, + 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x20, + 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xae, 0x00, 0xa2, 0x00, 0x95, 0x00, 0x8b, 0x01, + 0x83, 0x02, 0x7b, 0x04, 0x76, 0x05, 0x73, 0x07, 0x6e, 0x09, 0x69, 0x0a, 0x66, 0x0c, 0x64, 0x0d, + 0x62, 0x0f, 0x61, 0x10, 0x5e, 0x11, 0x5b, 0x13, 0x58, 0x14, 0x58, 0x15, 0x56, 0x16, 0x56, 0x18, + 0x0a, 0x47, 0x0b, 0x43, 0x0d, 0x42, 0x0f, 0x3f, 0x11, 0x3f, 0x12, 0x3e, 0x14, 0x3c, 0x16, 0x3c, + 0x17, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, 0x1d, 0x39, 0x1f, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, + 0x21, 0x3b, 0x21, 0x3a, 0x22, 0x3a, 0x24, 0x3a, 0x5e, 0x26, 0x5c, 0x27, 0x59, 0x2a, 0x57, 0x2a, + 0x56, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x32, 0x4f, 0x33, 0x4d, 0x35, 0x4b, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, + 0x00, 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, + 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, + 0x87, 0x02, 0x7f, 0x03, 0x79, 0x04, 0x75, 0x06, 0x71, 0x07, 0x6c, 0x09, 0x68, 0x0a, 0x66, 0x0c, + 0x64, 0x0d, 0x63, 0x0e, 0x61, 0x10, 0x5f, 0x11, 0x5c, 0x12, 0x59, 0x13, 0x58, 0x14, 0x58, 0x15, + 0x0a, 0x47, 0x0b, 0x43, 0x0d, 0x42, 0x0e, 0x40, 0x10, 0x3e, 0x11, 0x3f, 0x13, 0x3c, 0x15, 0x3b, + 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x3a, 0x1f, 0x3b, + 0x1f, 0x3b, 0x21, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x5e, 0x25, 0x5c, 0x27, 0x5a, 0x29, 0x57, 0x2a, + 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2f, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, + 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, 0x5f, 0x00, 0x4e, 0x00, + 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xaf, 0x00, 0xa5, 0x00, 0x9a, 0x00, 0x90, 0x00, + 0x8a, 0x01, 0x83, 0x02, 0x7c, 0x03, 0x77, 0x05, 0x74, 0x06, 0x70, 0x07, 0x6c, 0x09, 0x68, 0x0a, + 0x66, 0x0b, 0x64, 0x0c, 0x63, 0x0e, 0x61, 0x0f, 0x60, 0x11, 0x5d, 0x11, 0x5b, 0x13, 0x59, 0x13, + 0x0a, 0x47, 0x0a, 0x44, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3e, 0x11, 0x3f, 0x13, 0x3e, 0x14, 0x3c, + 0x15, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x3a, + 0x1f, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3b, 0x5e, 0x25, 0x5c, 0x27, 0x5a, 0x29, 0x57, 0x2a, + 0x57, 0x2a, 0x56, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x30, 0x50, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x32, 0x4f, 0x33, 0x4d, 0x35, 0x4b, 0x35, 0x4a, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x3d, 0x00, 0x34, + 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, + 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x92, 0x00, + 0x8c, 0x01, 0x86, 0x02, 0x7e, 0x02, 0x7a, 0x04, 0x76, 0x05, 0x73, 0x06, 0x70, 0x07, 0x6b, 0x09, + 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0e, 0x62, 0x0e, 0x61, 0x10, 0x5e, 0x11, 0x5c, 0x12, + 0x0a, 0x47, 0x0a, 0x44, 0x0c, 0x42, 0x0d, 0x41, 0x0f, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x13, 0x3d, + 0x15, 0x3c, 0x16, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x39, + 0x1e, 0x39, 0x1f, 0x3b, 0x1f, 0x3b, 0x1f, 0x3b, 0x5f, 0x25, 0x5c, 0x27, 0x5b, 0x28, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x34, 0x4c, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, + 0x00, 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, + 0x50, 0x00, 0x41, 0x00, 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb0, 0x00, 0xa7, 0x00, 0x9f, 0x00, 0x95, 0x00, + 0x8e, 0x00, 0x88, 0x01, 0x82, 0x02, 0x7c, 0x03, 0x78, 0x05, 0x75, 0x06, 0x72, 0x07, 0x6f, 0x08, + 0x6a, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0d, 0x62, 0x0e, 0x61, 0x0f, 0x60, 0x11, + 0x0a, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x10, 0x3d, 0x11, 0x3f, 0x13, 0x3e, + 0x14, 0x3c, 0x15, 0x3b, 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, + 0x1c, 0x3a, 0x1d, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x58, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x32, 0x4f, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, + 0x00, 0x35, 0x00, 0x2c, 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x66, 0x00, + 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb1, 0x00, 0xa7, 0x00, 0xa0, 0x00, 0x97, 0x00, + 0x90, 0x00, 0x8a, 0x01, 0x85, 0x02, 0x7e, 0x02, 0x7a, 0x03, 0x77, 0x05, 0x74, 0x06, 0x72, 0x07, + 0x6e, 0x08, 0x6a, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0d, 0x63, 0x0e, 0x61, 0x0f, + 0x0a, 0x47, 0x0a, 0x45, 0x0b, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x3e, 0x11, 0x3f, 0x12, 0x3f, + 0x13, 0x3d, 0x15, 0x3c, 0x15, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, + 0x1c, 0x3b, 0x1c, 0x3a, 0x1d, 0x39, 0x1f, 0x39, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x59, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2f, + 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x00, 0x17, 0x00, 0x2f, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x5d, + 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, + 0x00, 0x6c, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x62, 0x00, 0x62, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5d, + 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2c, 0x14, 0x01, 0x29, 0x00, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, + 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, + 0x31, 0x11, 0x03, 0x23, 0x00, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, + 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb1, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x99, 0x00, + 0x91, 0x00, 0x8c, 0x00, 0x88, 0x01, 0x82, 0x02, 0x7c, 0x03, 0x79, 0x04, 0x76, 0x05, 0x74, 0x06, + 0x71, 0x07, 0x6d, 0x08, 0x6a, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x64, 0x0c, 0x63, 0x0e, + 0x0a, 0x47, 0x0a, 0x45, 0x0b, 0x42, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3e, 0x11, 0x3f, + 0x13, 0x3e, 0x13, 0x3c, 0x15, 0x3c, 0x16, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, 0x19, 0x3c, + 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3a, 0x1d, 0x39, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5a, 0x29, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x45, 0x00, 0x4f, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, + 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, + 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x48, 0x00, 0x55, 0x00, 0x58, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, + 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x16, 0x04, 0x00, 0x27, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x53, 0x00, 0x56, 0x00, 0x59, + 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, + 0x5f, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, + 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb1, 0x00, 0xa9, 0x00, 0xa2, 0x00, 0x9b, 0x00, + 0x93, 0x00, 0x8e, 0x00, 0x89, 0x01, 0x84, 0x02, 0x7e, 0x02, 0x7a, 0x03, 0x78, 0x05, 0x75, 0x05, + 0x73, 0x06, 0x71, 0x07, 0x6c, 0x08, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x64, 0x0c, + 0x0a, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x42, 0x0e, 0x42, 0x0f, 0x40, 0x10, 0x3d, 0x11, 0x3f, + 0x12, 0x3f, 0x13, 0x3d, 0x15, 0x3c, 0x15, 0x3b, 0x17, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, + 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3b, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x28, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x30, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x22, 0x00, 0x36, 0x00, 0x42, 0x00, 0x4a, 0x00, 0x4f, + 0x00, 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5b, + 0x00, 0x5f, 0x00, 0x48, 0x00, 0x1b, 0x00, 0x34, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4f, + 0x00, 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x57, 0x00, 0x12, 0x01, 0x00, 0x17, 0x00, 0x2f, 0x00, 0x3e, 0x00, 0x47, 0x00, 0x4d, + 0x00, 0x51, 0x00, 0x53, 0x00, 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, + 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, + 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb2, 0x00, 0xa9, 0x00, 0xa3, 0x00, 0x9d, 0x00, + 0x94, 0x00, 0x8f, 0x00, 0x8b, 0x01, 0x87, 0x02, 0x81, 0x02, 0x7c, 0x03, 0x79, 0x03, 0x77, 0x05, + 0x75, 0x06, 0x72, 0x06, 0x70, 0x07, 0x6c, 0x08, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, + 0x0a, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x3e, 0x11, 0x3e, + 0x11, 0x3f, 0x13, 0x3f, 0x13, 0x3c, 0x15, 0x3c, 0x15, 0x3a, 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, + 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x5f, 0x24, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x28, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x19, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, + 0x00, 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, + 0x00, 0x62, 0x00, 0x55, 0x00, 0x34, 0x00, 0x0b, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, + 0x00, 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x91, 0x00, 0x78, 0x00, 0x43, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x23, 0x00, 0x31, 0x00, 0x3b, + 0x00, 0x42, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, + 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, + 0x00, 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x00, 0x9e, 0x00, + 0x96, 0x00, 0x90, 0x00, 0x8c, 0x00, 0x88, 0x01, 0x84, 0x02, 0x7e, 0x02, 0x7b, 0x03, 0x78, 0x04, + 0x76, 0x05, 0x74, 0x06, 0x72, 0x07, 0x6f, 0x07, 0x6b, 0x09, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, + 0x0a, 0x47, 0x0a, 0x46, 0x0b, 0x44, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x3f, 0x10, 0x3d, + 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3c, 0x16, 0x3a, 0x17, 0x3b, 0x17, 0x3c, + 0x18, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x5f, 0x24, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x58, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x24, 0x00, 0x2f, + 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, + 0x00, 0x62, 0x00, 0x58, 0x00, 0x40, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x14, 0x00, 0x24, 0x00, 0x2f, + 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x87, 0x00, 0x61, 0x00, 0x36, 0x00, 0x10, 0x00, 0x02, 0x0a, 0x00, 0x1a, 0x00, 0x27, + 0x00, 0x32, 0x00, 0x3a, 0x00, 0x40, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4b, 0x00, 0x4e, 0x00, 0x50, + 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, + 0x28, 0x3c, 0x16, 0x41, 0x11, 0x43, 0x0f, 0x44, 0x0e, 0x45, 0x0d, 0x46, 0x0c, 0x46, 0x0c, 0x46, + 0x0b, 0x47, 0x0b, 0x47, 0x0b, 0x47, 0x0b, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x6e, 0x1c, 0x3a, 0x29, 0x27, 0x31, 0x1e, 0x36, + 0x19, 0x3a, 0x17, 0x3b, 0x14, 0x3d, 0x13, 0x3f, 0x11, 0x40, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x42, + 0x0f, 0x43, 0x0f, 0x43, 0x0e, 0x43, 0x0e, 0x44, 0x0d, 0x44, 0x0d, 0x44, 0x0c, 0x44, 0x0c, 0x44, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x3c, 0x32, 0x22, 0x38, 0x18, 0x3c, 0x14, 0x3f, + 0x11, 0x40, 0x10, 0x41, 0x0f, 0x42, 0x0e, 0x43, 0x0e, 0x44, 0x0d, 0x44, 0x0d, 0x44, 0x0c, 0x44, + 0x0c, 0x45, 0x0c, 0x45, 0x0c, 0x45, 0x0c, 0x45, 0x0b, 0x45, 0x0b, 0x45, 0x0b, 0x46, 0x0b, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, + 0x00, 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4c, + 0x00, 0x5f, 0x00, 0x57, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, + 0x00, 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x8f, 0x00, 0x74, 0x00, 0x51, 0x00, 0x2e, 0x00, 0x10, 0x00, 0x04, 0x08, 0x00, 0x13, + 0x00, 0x20, 0x00, 0x2a, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x48, + 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x00, 0x00, 0x08, + 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, + 0x30, 0x38, 0x20, 0x3a, 0x18, 0x3d, 0x14, 0x3e, 0x12, 0x40, 0x10, 0x41, 0x0f, 0x42, 0x0e, 0x42, + 0x0e, 0x42, 0x0d, 0x43, 0x0d, 0x43, 0x0d, 0x44, 0x0d, 0x44, 0x0c, 0x45, 0x0c, 0x45, 0x0b, 0x45, + 0x0b, 0x46, 0x0b, 0x46, 0x0b, 0x46, 0x0b, 0x47, 0x8a, 0x03, 0x59, 0x0f, 0x40, 0x18, 0x32, 0x1f, + 0x2a, 0x24, 0x24, 0x28, 0x20, 0x2c, 0x1c, 0x2f, 0x1b, 0x31, 0x18, 0x33, 0x17, 0x34, 0x16, 0x36, + 0x15, 0x37, 0x14, 0x38, 0x13, 0x39, 0x12, 0x3a, 0x12, 0x3a, 0x12, 0x3b, 0x11, 0x3c, 0x10, 0x3c, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x4a, 0x25, 0x31, 0x2b, 0x25, 0x30, 0x1e, 0x33, + 0x1a, 0x36, 0x17, 0x38, 0x15, 0x39, 0x13, 0x3b, 0x12, 0x3c, 0x11, 0x3d, 0x10, 0x3e, 0x10, 0x3f, + 0x0f, 0x3f, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x40, 0x0e, 0x41, 0x0e, 0x41, 0x0d, 0x42, 0x0d, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, + 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, + 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, + 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x94, 0x00, 0x7f, 0x00, 0x64, 0x00, 0x46, 0x00, 0x29, 0x00, 0x10, 0x00, 0x06, 0x07, + 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x40, + 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, 0x00, + 0x00, 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, + 0x34, 0x38, 0x25, 0x39, 0x1e, 0x3b, 0x19, 0x3c, 0x16, 0x3d, 0x14, 0x3d, 0x13, 0x3f, 0x11, 0x40, + 0x10, 0x41, 0x0f, 0x41, 0x0f, 0x42, 0x0e, 0x42, 0x0e, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, + 0x0d, 0x43, 0x0d, 0x43, 0x0d, 0x44, 0x0d, 0x44, 0x97, 0x00, 0x6c, 0x06, 0x52, 0x0d, 0x43, 0x12, + 0x38, 0x18, 0x30, 0x1c, 0x2a, 0x20, 0x27, 0x23, 0x23, 0x26, 0x20, 0x28, 0x1e, 0x2a, 0x1c, 0x2c, + 0x1b, 0x2e, 0x19, 0x2f, 0x18, 0x31, 0x17, 0x32, 0x16, 0x33, 0x16, 0x34, 0x16, 0x35, 0x15, 0x35, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x50, 0x24, 0x3b, 0x27, 0x2e, 0x2a, 0x26, 0x2d, + 0x21, 0x2f, 0x1d, 0x32, 0x1a, 0x33, 0x18, 0x35, 0x16, 0x36, 0x15, 0x38, 0x14, 0x39, 0x13, 0x3a, + 0x12, 0x3a, 0x11, 0x3b, 0x11, 0x3c, 0x11, 0x3c, 0x10, 0x3d, 0x10, 0x3e, 0x10, 0x3e, 0x0f, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, + 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x96, 0x00, 0x87, 0x00, 0x71, 0x00, 0x57, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x10, 0x00, + 0x07, 0x06, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x26, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37, + 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, + 0x36, 0x39, 0x2a, 0x39, 0x22, 0x39, 0x1d, 0x3a, 0x1a, 0x3b, 0x17, 0x3d, 0x15, 0x3d, 0x13, 0x3d, + 0x13, 0x3e, 0x12, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x41, 0x0f, 0x42, 0x0e, 0x41, 0x0e, 0x42, + 0x0e, 0x42, 0x0e, 0x41, 0x0e, 0x42, 0x0d, 0x42, 0x9e, 0x00, 0x79, 0x02, 0x61, 0x07, 0x50, 0x0b, + 0x44, 0x10, 0x3b, 0x14, 0x34, 0x18, 0x2f, 0x1b, 0x2b, 0x1e, 0x28, 0x20, 0x25, 0x23, 0x23, 0x25, + 0x21, 0x27, 0x1f, 0x28, 0x1d, 0x2a, 0x1c, 0x2b, 0x1b, 0x2c, 0x19, 0x2e, 0x19, 0x2f, 0x19, 0x30, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x54, 0x23, 0x42, 0x25, 0x35, 0x27, 0x2d, 0x29, + 0x27, 0x2c, 0x22, 0x2e, 0x1f, 0x2f, 0x1c, 0x31, 0x1a, 0x33, 0x19, 0x34, 0x17, 0x35, 0x16, 0x36, + 0x15, 0x37, 0x14, 0x38, 0x13, 0x38, 0x13, 0x39, 0x12, 0x3a, 0x11, 0x3a, 0x11, 0x3b, 0x11, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, + 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, 0x37, 0x00, 0x22, 0x00, + 0x10, 0x00, 0x08, 0x05, 0x02, 0x0a, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x22, 0x00, 0x28, 0x00, 0x2d, + 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, 0x00, + 0x1f, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x07, 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, + 0x38, 0x39, 0x2d, 0x39, 0x25, 0x39, 0x20, 0x3a, 0x1d, 0x3b, 0x1a, 0x3b, 0x18, 0x3c, 0x16, 0x3d, + 0x14, 0x3d, 0x13, 0x3d, 0x13, 0x3d, 0x12, 0x3e, 0x11, 0x3f, 0x11, 0x40, 0x10, 0x40, 0x0f, 0x41, + 0x0f, 0x42, 0x0f, 0x42, 0x0e, 0x42, 0x0e, 0x42, 0xa2, 0x00, 0x84, 0x00, 0x6c, 0x04, 0x5a, 0x07, + 0x4e, 0x0b, 0x45, 0x0f, 0x3e, 0x12, 0x38, 0x15, 0x32, 0x18, 0x2f, 0x1a, 0x2c, 0x1d, 0x29, 0x1f, + 0x26, 0x21, 0x24, 0x22, 0x22, 0x24, 0x21, 0x26, 0x1f, 0x27, 0x1e, 0x28, 0x1d, 0x2a, 0x1c, 0x2b, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x56, 0x23, 0x47, 0x24, 0x3b, 0x25, 0x32, 0x27, + 0x2c, 0x29, 0x27, 0x2b, 0x24, 0x2d, 0x21, 0x2e, 0x1e, 0x30, 0x1c, 0x31, 0x1b, 0x32, 0x19, 0x33, + 0x18, 0x34, 0x17, 0x35, 0x16, 0x36, 0x15, 0x37, 0x14, 0x37, 0x14, 0x38, 0x13, 0x38, 0x13, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x9a, 0x00, 0x90, 0x00, 0x81, 0x00, 0x6f, 0x00, 0x5b, 0x00, 0x46, 0x00, 0x32, 0x00, + 0x20, 0x00, 0x10, 0x00, 0x09, 0x04, 0x03, 0x09, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x24, + 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, + 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, + 0x39, 0x3a, 0x2f, 0x39, 0x28, 0x39, 0x23, 0x39, 0x20, 0x3a, 0x1c, 0x3b, 0x1a, 0x3a, 0x18, 0x3b, + 0x17, 0x3c, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, 0x13, 0x3d, 0x12, 0x3d, 0x11, 0x3e, 0x11, 0x3f, + 0x11, 0x40, 0x10, 0x40, 0x0f, 0x40, 0x0f, 0x41, 0xa6, 0x00, 0x8b, 0x00, 0x75, 0x02, 0x63, 0x05, + 0x57, 0x07, 0x4d, 0x0b, 0x45, 0x0e, 0x3e, 0x11, 0x39, 0x13, 0x35, 0x16, 0x32, 0x18, 0x2e, 0x1a, + 0x2c, 0x1c, 0x29, 0x1d, 0x27, 0x1f, 0x26, 0x22, 0x24, 0x22, 0x21, 0x23, 0x21, 0x25, 0x20, 0x26, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x58, 0x23, 0x4a, 0x23, 0x3f, 0x24, 0x36, 0x26, + 0x30, 0x27, 0x2b, 0x29, 0x27, 0x2a, 0x24, 0x2c, 0x22, 0x2d, 0x20, 0x2e, 0x1e, 0x30, 0x1c, 0x30, + 0x1b, 0x32, 0x1a, 0x32, 0x18, 0x33, 0x18, 0x34, 0x17, 0x34, 0x16, 0x35, 0x16, 0x36, 0x15, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x20, + 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x86, 0x00, 0x76, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, + 0x2f, 0x00, 0x1e, 0x00, 0x10, 0x00, 0x09, 0x04, 0x04, 0x08, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1a, + 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, 0x00, + 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, + 0x3a, 0x3a, 0x31, 0x3a, 0x2a, 0x39, 0x25, 0x3a, 0x22, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1a, 0x3a, + 0x19, 0x3a, 0x17, 0x3c, 0x16, 0x3c, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, 0x13, 0x3d, 0x12, 0x3d, + 0x11, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x10, 0x40, 0xa8, 0x00, 0x90, 0x00, 0x7c, 0x01, 0x6b, 0x02, + 0x5f, 0x05, 0x55, 0x07, 0x4c, 0x0a, 0x45, 0x0d, 0x40, 0x0f, 0x3c, 0x12, 0x37, 0x14, 0x34, 0x16, + 0x31, 0x18, 0x2e, 0x19, 0x2b, 0x1b, 0x2a, 0x1d, 0x28, 0x1e, 0x26, 0x20, 0x25, 0x22, 0x23, 0x22, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x59, 0x23, 0x4d, 0x23, 0x43, 0x24, 0x3b, 0x25, + 0x34, 0x26, 0x2f, 0x27, 0x2b, 0x29, 0x28, 0x2a, 0x25, 0x2b, 0x23, 0x2d, 0x21, 0x2e, 0x1f, 0x2e, + 0x1d, 0x30, 0x1c, 0x30, 0x1b, 0x31, 0x1a, 0x32, 0x19, 0x32, 0x18, 0x33, 0x17, 0x34, 0x16, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0x16, 0x00, 0x1c, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, + 0x00, 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0x16, 0x00, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9b, 0x00, 0x95, 0x00, 0x8a, 0x00, 0x7d, 0x00, 0x6d, 0x00, 0x5d, 0x00, 0x4c, 0x00, + 0x3b, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x10, 0x00, 0x0a, 0x04, 0x05, 0x07, 0x00, 0x0b, 0x00, 0x11, + 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, 0x00, + 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, + 0x3a, 0x3b, 0x32, 0x3a, 0x2c, 0x39, 0x27, 0x3a, 0x23, 0x39, 0x21, 0x39, 0x1e, 0x3b, 0x1c, 0x3c, + 0x1a, 0x3a, 0x19, 0x3a, 0x17, 0x3b, 0x17, 0x3c, 0x15, 0x3d, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, + 0x13, 0x3d, 0x13, 0x3d, 0x11, 0x3d, 0x11, 0x3e, 0xaa, 0x00, 0x94, 0x00, 0x82, 0x00, 0x72, 0x01, + 0x65, 0x04, 0x5b, 0x05, 0x53, 0x07, 0x4b, 0x0a, 0x45, 0x0c, 0x41, 0x0f, 0x3d, 0x11, 0x39, 0x12, + 0x36, 0x15, 0x33, 0x16, 0x30, 0x18, 0x2e, 0x19, 0x2b, 0x1a, 0x2b, 0x1d, 0x29, 0x1d, 0x26, 0x1e, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5a, 0x23, 0x4f, 0x23, 0x46, 0x24, 0x3e, 0x24, + 0x37, 0x25, 0x32, 0x26, 0x2e, 0x27, 0x2b, 0x28, 0x28, 0x2a, 0x25, 0x2b, 0x23, 0x2c, 0x21, 0x2d, + 0x20, 0x2e, 0x1e, 0x2e, 0x1d, 0x30, 0x1c, 0x30, 0x1b, 0x31, 0x1a, 0x32, 0x19, 0x32, 0x18, 0x33, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, + 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x81, 0x00, 0x74, 0x00, 0x65, 0x00, 0x56, 0x00, + 0x46, 0x00, 0x37, 0x00, 0x29, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x0a, 0x03, 0x06, 0x07, 0x01, 0x0a, + 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, 0x00, + 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, 0x00, + 0x3b, 0x3b, 0x33, 0x3b, 0x2d, 0x39, 0x29, 0x39, 0x25, 0x3a, 0x22, 0x39, 0x20, 0x39, 0x1e, 0x3b, + 0x1c, 0x3c, 0x1a, 0x3a, 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3c, 0x16, 0x3c, 0x15, 0x3d, 0x15, 0x3e, + 0x13, 0x3f, 0x13, 0x3d, 0x13, 0x3d, 0x13, 0x3d, 0xaa, 0x00, 0x98, 0x00, 0x87, 0x00, 0x78, 0x01, + 0x6b, 0x02, 0x61, 0x04, 0x58, 0x06, 0x51, 0x08, 0x4b, 0x0a, 0x46, 0x0c, 0x42, 0x0e, 0x3e, 0x0f, + 0x3a, 0x12, 0x37, 0x12, 0x35, 0x16, 0x32, 0x16, 0x30, 0x18, 0x2e, 0x19, 0x2b, 0x1a, 0x2b, 0x1c, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5a, 0x23, 0x51, 0x23, 0x48, 0x23, 0x41, 0x24, + 0x3a, 0x25, 0x35, 0x25, 0x31, 0x26, 0x2e, 0x27, 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2b, 0x24, 0x2b, + 0x22, 0x2d, 0x21, 0x2d, 0x1f, 0x2e, 0x1e, 0x2e, 0x1d, 0x30, 0x1c, 0x30, 0x1b, 0x30, 0x1a, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, + 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x3d, 0x00, 0x34, + 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x90, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x5e, 0x00, + 0x50, 0x00, 0x42, 0x00, 0x34, 0x00, 0x27, 0x00, 0x1b, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x06, 0x06, + 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, + 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, 0x00, + 0x3b, 0x3c, 0x34, 0x3a, 0x2f, 0x3a, 0x2a, 0x39, 0x26, 0x3a, 0x23, 0x3a, 0x21, 0x39, 0x1f, 0x3a, + 0x1d, 0x3b, 0x1c, 0x3c, 0x1a, 0x3b, 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3b, 0x17, 0x3c, 0x15, 0x3c, + 0x15, 0x3d, 0x15, 0x3f, 0x13, 0x3f, 0x13, 0x3e, 0xac, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7c, 0x00, + 0x71, 0x01, 0x67, 0x03, 0x5e, 0x05, 0x57, 0x07, 0x51, 0x08, 0x4b, 0x0a, 0x46, 0x0c, 0x42, 0x0d, + 0x3f, 0x0f, 0x3b, 0x11, 0x39, 0x12, 0x35, 0x14, 0x34, 0x16, 0x31, 0x16, 0x30, 0x18, 0x2e, 0x19, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5b, 0x23, 0x52, 0x23, 0x4a, 0x23, 0x43, 0x24, + 0x3d, 0x24, 0x38, 0x25, 0x34, 0x26, 0x30, 0x27, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2a, + 0x24, 0x2b, 0x22, 0x2c, 0x21, 0x2d, 0x20, 0x2d, 0x1f, 0x2e, 0x1d, 0x2e, 0x1d, 0x30, 0x1c, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, + 0x00, 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, + 0x58, 0x00, 0x4b, 0x00, 0x3e, 0x00, 0x31, 0x00, 0x25, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x0b, 0x03, + 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, 0x00, + 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, 0x00, + 0x3b, 0x3c, 0x35, 0x3a, 0x30, 0x3b, 0x2c, 0x39, 0x28, 0x3a, 0x25, 0x3b, 0x22, 0x39, 0x21, 0x39, + 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3c, 0x1a, 0x3b, 0x19, 0x3a, 0x19, 0x3a, 0x17, 0x3a, 0x17, 0x3c, + 0x16, 0x3c, 0x15, 0x3c, 0x15, 0x3e, 0x14, 0x3f, 0xad, 0x00, 0x9d, 0x00, 0x8e, 0x00, 0x81, 0x00, + 0x76, 0x01, 0x6b, 0x02, 0x63, 0x04, 0x5c, 0x05, 0x56, 0x07, 0x50, 0x08, 0x4b, 0x0a, 0x47, 0x0c, + 0x43, 0x0c, 0x40, 0x0f, 0x3c, 0x0f, 0x3a, 0x12, 0x37, 0x12, 0x35, 0x14, 0x33, 0x16, 0x30, 0x16, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5b, 0x23, 0x53, 0x23, 0x4c, 0x23, 0x45, 0x23, + 0x40, 0x24, 0x3b, 0x25, 0x36, 0x25, 0x33, 0x26, 0x30, 0x27, 0x2d, 0x28, 0x2a, 0x28, 0x28, 0x2a, + 0x26, 0x2a, 0x25, 0x2b, 0x23, 0x2b, 0x22, 0x2d, 0x20, 0x2d, 0x20, 0x2e, 0x1e, 0x2e, 0x1d, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, + 0x00, 0x35, 0x00, 0x2c, 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6b, 0x00, + 0x5f, 0x00, 0x53, 0x00, 0x46, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x00, 0x0f, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, + 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, 0x00, + 0x3c, 0x3d, 0x36, 0x3a, 0x31, 0x3b, 0x2c, 0x3a, 0x29, 0x39, 0x26, 0x3a, 0x24, 0x3b, 0x21, 0x39, + 0x20, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3a, 0x19, 0x3a, 0x17, 0x3a, + 0x17, 0x3b, 0x17, 0x3c, 0x15, 0x3c, 0x15, 0x3c, 0xad, 0x00, 0x9f, 0x00, 0x91, 0x00, 0x85, 0x00, + 0x79, 0x00, 0x6f, 0x01, 0x67, 0x02, 0x60, 0x04, 0x59, 0x05, 0x54, 0x07, 0x4f, 0x08, 0x4b, 0x0a, + 0x47, 0x0c, 0x43, 0x0c, 0x41, 0x0f, 0x3d, 0x0f, 0x3b, 0x11, 0x38, 0x12, 0x35, 0x12, 0x35, 0x15, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5b, 0x23, 0x54, 0x23, 0x4e, 0x23, 0x47, 0x23, + 0x42, 0x24, 0x3c, 0x24, 0x38, 0x25, 0x35, 0x25, 0x32, 0x26, 0x2f, 0x27, 0x2d, 0x28, 0x2a, 0x28, + 0x28, 0x2a, 0x26, 0x2a, 0x25, 0x2b, 0x23, 0x2b, 0x22, 0x2c, 0x21, 0x2d, 0x20, 0x2d, 0x20, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3d, 0x36, 0x3a, 0x32, 0x3b, 0x2d, 0x3b, 0x2b, 0x39, 0x28, 0x3a, 0x25, 0x3a, 0x23, 0x3a, + 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3a, 0x19, 0x3a, + 0x18, 0x3a, 0x17, 0x3a, 0x17, 0x3c, 0x16, 0x3c, 0xad, 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, + 0x7e, 0x00, 0x74, 0x01, 0x6c, 0x02, 0x65, 0x03, 0x5e, 0x04, 0x58, 0x05, 0x53, 0x07, 0x4f, 0x08, + 0x4b, 0x0a, 0x47, 0x0b, 0x44, 0x0c, 0x41, 0x0e, 0x3e, 0x0f, 0x3b, 0x0f, 0x3a, 0x12, 0x37, 0x12, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x55, 0x23, 0x4f, 0x23, 0x49, 0x23, + 0x44, 0x24, 0x3f, 0x24, 0x3b, 0x25, 0x37, 0x25, 0x34, 0x26, 0x31, 0x26, 0x2e, 0x27, 0x2c, 0x28, + 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2b, 0x24, 0x2b, 0x22, 0x2b, 0x22, 0x2d, 0x20, 0x2d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x37, 0x3b, 0x33, 0x3b, 0x2f, 0x3b, 0x2b, 0x39, 0x29, 0x39, 0x26, 0x3a, 0x24, 0x3b, + 0x22, 0x3a, 0x21, 0x39, 0x1f, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, + 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3a, 0x17, 0x3b, 0xae, 0x00, 0xa2, 0x00, 0x97, 0x00, 0x8b, 0x00, + 0x80, 0x00, 0x77, 0x00, 0x6f, 0x01, 0x67, 0x02, 0x61, 0x04, 0x5c, 0x05, 0x57, 0x05, 0x52, 0x07, + 0x4e, 0x08, 0x4b, 0x0a, 0x47, 0x0b, 0x44, 0x0c, 0x41, 0x0d, 0x3f, 0x0f, 0x3c, 0x0f, 0x3b, 0x11, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x56, 0x23, 0x50, 0x23, 0x4a, 0x23, + 0x45, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x35, 0x25, 0x33, 0x26, 0x30, 0x26, 0x2e, 0x27, + 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2a, 0x24, 0x2b, 0x23, 0x2b, 0x22, 0x2c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x37, 0x3b, 0x33, 0x3a, 0x2f, 0x3b, 0x2c, 0x3a, 0x2a, 0x39, 0x27, 0x3a, 0x25, 0x3a, + 0x23, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, + 0x19, 0x3b, 0x19, 0x3a, 0x19, 0x3a, 0x17, 0x3a, 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, + 0x84, 0x00, 0x7a, 0x00, 0x72, 0x01, 0x6c, 0x02, 0x65, 0x02, 0x5f, 0x04, 0x5a, 0x05, 0x56, 0x05, + 0x52, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0b, 0x45, 0x0c, 0x41, 0x0c, 0x40, 0x0f, 0x3d, 0x0f, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x56, 0x23, 0x51, 0x23, 0x4c, 0x23, + 0x47, 0x23, 0x42, 0x24, 0x3e, 0x24, 0x3b, 0x25, 0x37, 0x25, 0x34, 0x25, 0x32, 0x26, 0x30, 0x26, + 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2a, 0x25, 0x2b, 0x23, 0x2b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x38, 0x3c, 0x34, 0x3a, 0x30, 0x3b, 0x2d, 0x3b, 0x2a, 0x39, 0x28, 0x39, 0x26, 0x3a, + 0x24, 0x3b, 0x22, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, + 0x1a, 0x3c, 0x19, 0x3b, 0x19, 0x3a, 0x19, 0x3a, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x90, 0x00, + 0x87, 0x00, 0x7e, 0x00, 0x76, 0x00, 0x6f, 0x01, 0x68, 0x02, 0x62, 0x04, 0x5e, 0x04, 0x59, 0x05, + 0x55, 0x06, 0x51, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x45, 0x0c, 0x41, 0x0c, 0x41, 0x0f, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x57, 0x23, 0x52, 0x23, 0x4d, 0x23, + 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x36, 0x25, 0x34, 0x25, 0x31, 0x26, + 0x2f, 0x27, 0x2d, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2a, 0x25, 0x2b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3e, 0x38, 0x3c, 0x34, 0x3a, 0x31, 0x3b, 0x2e, 0x3c, 0x2b, 0x39, 0x29, 0x39, 0x27, 0x3a, + 0x25, 0x3a, 0x24, 0x3b, 0x21, 0x3a, 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, + 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3a, 0xaf, 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x91, 0x00, + 0x88, 0x00, 0x80, 0x00, 0x78, 0x00, 0x71, 0x01, 0x6b, 0x02, 0x66, 0x02, 0x61, 0x04, 0x5c, 0x04, + 0x58, 0x05, 0x55, 0x07, 0x50, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0c, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4d, 0x23, + 0x49, 0x23, 0x45, 0x23, 0x41, 0x24, 0x3d, 0x24, 0x3b, 0x24, 0x38, 0x25, 0x35, 0x25, 0x33, 0x26, + 0x31, 0x26, 0x2f, 0x27, 0x2d, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x26, 0x2a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3e, 0x38, 0x3d, 0x35, 0x3a, 0x32, 0x3b, 0x2e, 0x3b, 0x2c, 0x3a, 0x2a, 0x39, 0x27, 0x39, + 0x26, 0x3a, 0x24, 0x3a, 0x23, 0x3b, 0x21, 0x39, 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, + 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3c, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x93, 0x00, + 0x8b, 0x00, 0x83, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6e, 0x01, 0x69, 0x02, 0x63, 0x03, 0x5f, 0x04, + 0x5b, 0x05, 0x56, 0x05, 0x54, 0x07, 0x4f, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x46, 0x0c, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5d, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4f, 0x23, + 0x4a, 0x23, 0x46, 0x23, 0x43, 0x24, 0x3f, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x36, 0x25, 0x34, 0x25, + 0x33, 0x26, 0x30, 0x26, 0x2f, 0x27, 0x2d, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3e, 0x39, 0x3e, 0x35, 0x3a, 0x32, 0x3a, 0x2f, 0x3b, 0x2d, 0x3c, 0x2a, 0x39, 0x28, 0x39, + 0x27, 0x3a, 0x24, 0x3a, 0x24, 0x3b, 0x22, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x39, 0x1f, 0x3a, + 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0xb0, 0x00, 0xa7, 0x00, 0x9e, 0x00, 0x96, 0x00, + 0x8d, 0x00, 0x86, 0x00, 0x7e, 0x00, 0x77, 0x00, 0x71, 0x01, 0x6b, 0x01, 0x66, 0x02, 0x62, 0x04, + 0x5d, 0x04, 0x5a, 0x05, 0x55, 0x05, 0x53, 0x07, 0x4f, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5d, 0x23, 0x58, 0x23, 0x54, 0x23, 0x50, 0x23, + 0x4c, 0x23, 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, 0x3d, 0x24, 0x3a, 0x24, 0x38, 0x25, 0x36, 0x25, + 0x33, 0x25, 0x32, 0x26, 0x2f, 0x26, 0x2f, 0x27, 0x2c, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3e, 0x39, 0x3e, 0x35, 0x3b, 0x32, 0x3a, 0x30, 0x3b, 0x2e, 0x3c, 0x2b, 0x3a, 0x2a, 0x39, + 0x27, 0x39, 0x26, 0x3a, 0x24, 0x3a, 0x23, 0x3b, 0x21, 0x3a, 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x39, + 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0xb1, 0x00, 0xa7, 0x00, 0x9f, 0x00, 0x97, 0x00, + 0x8f, 0x00, 0x87, 0x00, 0x80, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6e, 0x01, 0x69, 0x02, 0x64, 0x02, + 0x60, 0x04, 0x5c, 0x04, 0x59, 0x05, 0x55, 0x05, 0x53, 0x07, 0x4e, 0x07, 0x4e, 0x09, 0x4a, 0x0a, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5d, 0x23, 0x58, 0x23, 0x54, 0x23, 0x50, 0x23, + 0x4c, 0x23, 0x48, 0x23, 0x45, 0x23, 0x42, 0x24, 0x3f, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x37, 0x25, + 0x35, 0x25, 0x33, 0x25, 0x31, 0x26, 0x2f, 0x26, 0x2e, 0x27, 0x2c, 0x27, 0x2c, 0x28, 0x2a, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x94, 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xac, 0x00, + 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, + 0xb1, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x21, 0x00, 0x6e, 0x00, 0x8a, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa8, 0x00, + 0xaa, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xad, 0x00, 0xad, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, + 0xaf, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0x6c, 0x00, 0x92, 0x00, 0xa1, 0x00, 0xa7, 0x00, + 0xaa, 0x00, 0xac, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, 0x00, + 0xb2, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x04, 0x70, 0x01, 0x7f, 0x00, 0x88, 0x00, 0x90, 0x00, 0x95, 0x00, 0x99, 0x00, 0x9c, 0x00, + 0x9f, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xa8, 0x00, + 0xa9, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x1c, 0x3a, 0x03, 0x59, 0x00, 0x6c, 0x00, 0x79, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x90, 0x00, + 0x94, 0x00, 0x98, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, + 0xa5, 0x00, 0xa6, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x60, 0x0e, 0x79, 0x01, 0x88, 0x00, 0x92, 0x00, + 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, + 0xab, 0x00, 0xac, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4e, 0x0f, 0x60, 0x07, 0x6d, 0x03, 0x77, 0x01, 0x7f, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x8f, 0x00, + 0x92, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0, 0x00, 0xa1, 0x00, + 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa4, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x29, 0x27, 0x0f, 0x40, 0x06, 0x52, 0x02, 0x61, 0x00, 0x6c, 0x00, 0x75, 0x00, 0x7c, 0x00, + 0x82, 0x00, 0x87, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x97, 0x00, 0x98, 0x00, + 0x99, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x60, 0x14, 0x6f, 0x07, 0x7b, 0x03, 0x84, 0x01, + 0x8c, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa2, 0x00, + 0xa4, 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x17, 0x56, 0x0d, 0x62, 0x07, 0x6b, 0x04, 0x73, 0x02, 0x79, 0x01, 0x7e, 0x01, 0x83, 0x00, + 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, + 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0x9f, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x31, 0x1e, 0x18, 0x32, 0x0d, 0x43, 0x07, 0x50, 0x04, 0x5a, 0x02, 0x63, 0x01, 0x6b, 0x00, + 0x72, 0x00, 0x78, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, + 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x60, 0x18, 0x6b, 0x0c, 0x74, 0x06, 0x7d, 0x03, + 0x83, 0x02, 0x89, 0x01, 0x8d, 0x00, 0x91, 0x00, 0x95, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, + 0x9e, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x1d, 0x51, 0x12, 0x5b, 0x0c, 0x63, 0x08, 0x6a, 0x05, 0x70, 0x03, 0x77, 0x02, 0x7b, 0x02, + 0x7e, 0x01, 0x82, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8c, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x91, 0x00, + 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x97, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x36, 0x19, 0x1f, 0x2a, 0x12, 0x38, 0x0b, 0x44, 0x07, 0x4e, 0x05, 0x57, 0x02, 0x5f, 0x01, + 0x65, 0x01, 0x6b, 0x00, 0x71, 0x00, 0x76, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x84, 0x00, + 0x87, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x60, 0x1b, 0x68, 0x0f, 0x70, 0x09, 0x77, 0x05, + 0x7d, 0x03, 0x82, 0x02, 0x87, 0x01, 0x8b, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x96, 0x00, + 0x98, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x21, 0x4e, 0x16, 0x56, 0x10, 0x5d, 0x0b, 0x65, 0x08, 0x69, 0x06, 0x6f, 0x04, 0x74, 0x03, + 0x78, 0x02, 0x7b, 0x02, 0x7e, 0x01, 0x81, 0x01, 0x85, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c, 0x00, + 0x8e, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x92, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x3a, 0x17, 0x24, 0x24, 0x18, 0x30, 0x10, 0x3b, 0x0b, 0x45, 0x07, 0x4d, 0x05, 0x55, 0x04, + 0x5b, 0x02, 0x61, 0x01, 0x67, 0x01, 0x6b, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x77, 0x00, 0x7a, 0x00, + 0x7e, 0x00, 0x80, 0x00, 0x83, 0x00, 0x86, 0x00, 0x60, 0x1d, 0x67, 0x12, 0x6d, 0x0c, 0x73, 0x08, + 0x79, 0x05, 0x7e, 0x03, 0x82, 0x02, 0x86, 0x02, 0x89, 0x01, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, + 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x24, 0x4c, 0x1a, 0x53, 0x13, 0x59, 0x0e, 0x5f, 0x0b, 0x65, 0x08, 0x69, 0x06, 0x6d, 0x05, + 0x73, 0x04, 0x76, 0x03, 0x79, 0x02, 0x7c, 0x02, 0x7e, 0x01, 0x81, 0x01, 0x84, 0x00, 0x87, 0x00, + 0x89, 0x00, 0x8b, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x3b, 0x14, 0x28, 0x20, 0x1c, 0x2a, 0x14, 0x34, 0x0f, 0x3e, 0x0b, 0x45, 0x07, 0x4c, 0x05, + 0x53, 0x04, 0x58, 0x03, 0x5e, 0x02, 0x63, 0x01, 0x67, 0x01, 0x6c, 0x00, 0x6f, 0x00, 0x72, 0x00, + 0x76, 0x00, 0x78, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x60, 0x1d, 0x66, 0x14, 0x6b, 0x0e, 0x71, 0x0a, + 0x76, 0x07, 0x7a, 0x05, 0x7e, 0x03, 0x81, 0x02, 0x85, 0x02, 0x88, 0x01, 0x8b, 0x01, 0x8d, 0x00, + 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x27, 0x4a, 0x1d, 0x4f, 0x16, 0x57, 0x11, 0x5b, 0x0e, 0x61, 0x0b, 0x66, 0x08, 0x69, 0x07, + 0x6d, 0x05, 0x71, 0x04, 0x75, 0x03, 0x77, 0x02, 0x7a, 0x02, 0x7c, 0x02, 0x7e, 0x01, 0x80, 0x01, + 0x83, 0x01, 0x86, 0x00, 0x88, 0x00, 0x89, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x3d, 0x13, 0x2c, 0x1c, 0x20, 0x27, 0x18, 0x2f, 0x12, 0x38, 0x0e, 0x3e, 0x0a, 0x45, 0x07, + 0x4b, 0x06, 0x51, 0x05, 0x57, 0x04, 0x5c, 0x02, 0x60, 0x02, 0x65, 0x01, 0x67, 0x01, 0x6c, 0x00, + 0x6f, 0x00, 0x71, 0x00, 0x75, 0x00, 0x77, 0x00, 0x60, 0x1e, 0x65, 0x16, 0x6a, 0x10, 0x6f, 0x0c, + 0x73, 0x09, 0x77, 0x07, 0x7b, 0x05, 0x7e, 0x03, 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, 0x89, 0x01, + 0x8b, 0x01, 0x8e, 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, + 0x27, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x8f, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, 0x00, + 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, + 0x2c, 0x14, 0x57, 0x00, 0x82, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8c, 0x00, 0x63, 0x00, 0x7f, 0x00, 0x89, 0x00, 0x90, 0x00, 0x96, 0x00, 0x9b, 0x00, 0x9c, 0x00, + 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, + 0x31, 0x11, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, + 0x43, 0x29, 0x48, 0x20, 0x4d, 0x19, 0x54, 0x14, 0x58, 0x10, 0x5c, 0x0d, 0x62, 0x0b, 0x66, 0x09, + 0x69, 0x07, 0x6c, 0x06, 0x70, 0x05, 0x74, 0x04, 0x76, 0x03, 0x78, 0x02, 0x7a, 0x02, 0x7c, 0x02, + 0x7e, 0x02, 0x80, 0x01, 0x82, 0x01, 0x85, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x3f, 0x11, 0x2f, 0x1b, 0x23, 0x23, 0x1b, 0x2b, 0x15, 0x32, 0x11, 0x39, 0x0d, 0x40, 0x0a, + 0x45, 0x08, 0x4b, 0x07, 0x51, 0x05, 0x56, 0x04, 0x59, 0x03, 0x5e, 0x02, 0x61, 0x02, 0x65, 0x01, + 0x68, 0x01, 0x6b, 0x00, 0x6e, 0x00, 0x71, 0x00, 0x60, 0x1f, 0x64, 0x17, 0x69, 0x11, 0x6d, 0x0d, + 0x71, 0x0a, 0x75, 0x08, 0x78, 0x06, 0x7b, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x84, 0x02, 0x86, 0x02, + 0x88, 0x01, 0x8a, 0x01, 0x8c, 0x01, 0x8e, 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x4f, 0x00, 0x73, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x95, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, + 0x01, 0x29, 0x16, 0x04, 0x57, 0x00, 0x78, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x94, 0x00, 0x96, 0x00, + 0x98, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x4d, 0x00, 0x60, 0x00, 0x77, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x92, 0x00, 0x95, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, + 0x03, 0x23, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, 0x00, + 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x43, 0x2b, 0x47, 0x22, 0x4c, 0x1b, 0x51, 0x16, 0x56, 0x12, 0x59, 0x0f, 0x5e, 0x0d, 0x63, 0x0b, + 0x66, 0x09, 0x69, 0x07, 0x6b, 0x06, 0x6f, 0x05, 0x73, 0x05, 0x75, 0x03, 0x77, 0x03, 0x79, 0x02, + 0x7a, 0x02, 0x7c, 0x02, 0x7e, 0x02, 0x7f, 0x01, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x40, 0x11, 0x31, 0x18, 0x26, 0x20, 0x1e, 0x28, 0x18, 0x2f, 0x13, 0x35, 0x0f, 0x3c, 0x0c, + 0x41, 0x0a, 0x46, 0x08, 0x4b, 0x07, 0x50, 0x05, 0x54, 0x04, 0x58, 0x04, 0x5c, 0x02, 0x5f, 0x02, + 0x62, 0x02, 0x66, 0x01, 0x69, 0x01, 0x6b, 0x00, 0x60, 0x20, 0x64, 0x18, 0x68, 0x13, 0x6b, 0x0f, + 0x6f, 0x0c, 0x73, 0x09, 0x76, 0x07, 0x79, 0x06, 0x7c, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, + 0x85, 0x02, 0x88, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8e, 0x00, 0x90, 0x00, 0x91, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x38, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7c, 0x00, 0x84, 0x00, + 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, + 0x00, 0x49, 0x00, 0x27, 0x12, 0x01, 0x43, 0x00, 0x61, 0x00, 0x74, 0x00, 0x7f, 0x00, 0x87, 0x00, + 0x8c, 0x00, 0x90, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x60, 0x00, 0x24, 0x00, 0x48, 0x00, 0x60, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x84, 0x00, + 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, + 0x00, 0x46, 0x00, 0x1f, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, 0x00, + 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, + 0x43, 0x2d, 0x46, 0x24, 0x4b, 0x1e, 0x4f, 0x19, 0x55, 0x14, 0x58, 0x11, 0x5b, 0x0e, 0x5f, 0x0c, + 0x64, 0x0a, 0x66, 0x09, 0x68, 0x07, 0x6a, 0x06, 0x6e, 0x06, 0x72, 0x05, 0x74, 0x04, 0x76, 0x03, + 0x78, 0x03, 0x79, 0x02, 0x7b, 0x02, 0x7c, 0x02, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x40, 0x10, 0x33, 0x17, 0x28, 0x1e, 0x20, 0x25, 0x1a, 0x2c, 0x16, 0x32, 0x12, 0x37, 0x0f, + 0x3d, 0x0c, 0x42, 0x0a, 0x46, 0x08, 0x4b, 0x07, 0x4f, 0x05, 0x53, 0x05, 0x57, 0x04, 0x5a, 0x04, + 0x5e, 0x02, 0x61, 0x02, 0x63, 0x01, 0x66, 0x01, 0x60, 0x20, 0x63, 0x19, 0x67, 0x14, 0x6a, 0x10, + 0x6e, 0x0d, 0x71, 0x0b, 0x74, 0x09, 0x77, 0x07, 0x7a, 0x06, 0x7c, 0x05, 0x7f, 0x04, 0x81, 0x03, + 0x83, 0x02, 0x85, 0x02, 0x87, 0x02, 0x88, 0x02, 0x8a, 0x01, 0x8c, 0x01, 0x8d, 0x00, 0x8e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x48, 0x00, 0x5d, 0x00, 0x6c, 0x00, + 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, + 0x00, 0x54, 0x00, 0x41, 0x00, 0x17, 0x10, 0x00, 0x36, 0x00, 0x51, 0x00, 0x64, 0x00, 0x71, 0x00, + 0x7a, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8d, 0x00, 0x90, 0x00, 0x92, 0x00, 0x93, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x89, 0x00, 0x77, 0x00, 0x48, 0x00, 0x0f, 0x00, 0x30, 0x00, 0x48, 0x00, 0x5d, 0x00, 0x6c, 0x00, + 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, + 0x00, 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, 0x00, + 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, + 0x42, 0x2e, 0x45, 0x26, 0x4b, 0x1f, 0x4d, 0x1a, 0x52, 0x16, 0x56, 0x13, 0x59, 0x10, 0x5c, 0x0e, + 0x61, 0x0c, 0x64, 0x0a, 0x66, 0x09, 0x68, 0x07, 0x6a, 0x07, 0x6d, 0x06, 0x71, 0x05, 0x74, 0x05, + 0x75, 0x03, 0x77, 0x03, 0x78, 0x02, 0x7a, 0x02, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x41, 0x0f, 0x34, 0x16, 0x2a, 0x1c, 0x23, 0x23, 0x1d, 0x29, 0x18, 0x2e, 0x14, 0x34, 0x11, + 0x39, 0x0e, 0x3e, 0x0c, 0x42, 0x0a, 0x47, 0x08, 0x4b, 0x07, 0x4f, 0x05, 0x52, 0x05, 0x56, 0x04, + 0x59, 0x04, 0x5c, 0x03, 0x5f, 0x02, 0x62, 0x02, 0x60, 0x20, 0x63, 0x1a, 0x66, 0x15, 0x6a, 0x11, + 0x6d, 0x0e, 0x70, 0x0c, 0x73, 0x0a, 0x75, 0x08, 0x78, 0x07, 0x7a, 0x06, 0x7d, 0x05, 0x7f, 0x04, + 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, 0x86, 0x02, 0x88, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8c, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, + 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x86, 0x00, 0x89, 0x00, + 0x00, 0x59, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x0e, 0x10, 0x00, 0x2e, 0x00, 0x46, 0x00, 0x57, 0x00, + 0x64, 0x00, 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, 0x00, 0x89, 0x00, 0x8b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x00, 0x83, 0x00, 0x60, 0x00, 0x30, 0x00, 0x02, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, + 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x86, 0x00, 0x89, 0x00, + 0x00, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, 0x00, + 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, + 0x42, 0x2f, 0x44, 0x27, 0x4a, 0x22, 0x4c, 0x1c, 0x50, 0x18, 0x55, 0x14, 0x57, 0x12, 0x5a, 0x0f, + 0x5d, 0x0d, 0x62, 0x0c, 0x64, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x6a, 0x07, 0x6c, 0x06, 0x70, 0x05, + 0x73, 0x05, 0x75, 0x04, 0x76, 0x03, 0x78, 0x03, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x42, 0x0f, 0x36, 0x15, 0x2c, 0x1b, 0x25, 0x21, 0x1f, 0x26, 0x1a, 0x2c, 0x16, 0x31, 0x12, + 0x36, 0x0f, 0x3a, 0x0d, 0x3f, 0x0c, 0x43, 0x0a, 0x47, 0x08, 0x4b, 0x07, 0x4e, 0x05, 0x52, 0x05, + 0x55, 0x04, 0x58, 0x04, 0x5b, 0x04, 0x5d, 0x02, 0x60, 0x21, 0x63, 0x1b, 0x66, 0x16, 0x69, 0x12, + 0x6c, 0x0f, 0x6e, 0x0d, 0x71, 0x0b, 0x74, 0x09, 0x76, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7d, 0x05, + 0x7f, 0x04, 0x81, 0x03, 0x82, 0x02, 0x84, 0x02, 0x86, 0x02, 0x87, 0x02, 0x89, 0x02, 0x8a, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x33, 0x00, + 0x45, 0x00, 0x53, 0x00, 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, + 0x00, 0x5b, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x23, 0x02, 0x0a, 0x10, 0x00, 0x29, 0x00, 0x3d, 0x00, + 0x4e, 0x00, 0x5b, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x74, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x8c, 0x00, 0x70, 0x00, 0x48, 0x00, 0x22, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x33, 0x00, + 0x45, 0x00, 0x53, 0x00, 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, + 0x00, 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, 0x00, + 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, 0x00, + 0x42, 0x31, 0x44, 0x29, 0x49, 0x23, 0x4c, 0x1e, 0x4e, 0x1a, 0x53, 0x16, 0x56, 0x13, 0x58, 0x11, + 0x5a, 0x0f, 0x5f, 0x0d, 0x63, 0x0b, 0x64, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x6a, 0x07, 0x6b, 0x06, + 0x6f, 0x06, 0x72, 0x05, 0x74, 0x05, 0x75, 0x03, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x43, 0x0f, 0x37, 0x14, 0x2e, 0x19, 0x27, 0x1f, 0x21, 0x24, 0x1c, 0x29, 0x18, 0x2e, 0x15, + 0x33, 0x12, 0x37, 0x0f, 0x3b, 0x0c, 0x40, 0x0c, 0x43, 0x0a, 0x47, 0x08, 0x4b, 0x07, 0x4e, 0x06, + 0x51, 0x05, 0x55, 0x05, 0x56, 0x04, 0x5a, 0x04, 0x60, 0x21, 0x63, 0x1b, 0x65, 0x17, 0x68, 0x13, + 0x6b, 0x10, 0x6e, 0x0e, 0x70, 0x0c, 0x73, 0x0a, 0x75, 0x09, 0x77, 0x07, 0x79, 0x06, 0x7b, 0x06, + 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x82, 0x03, 0x84, 0x02, 0x86, 0x02, 0x87, 0x02, 0x88, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, + 0x00, 0x5c, 0x00, 0x56, 0x00, 0x47, 0x00, 0x31, 0x00, 0x1a, 0x04, 0x08, 0x10, 0x00, 0x25, 0x00, + 0x37, 0x00, 0x46, 0x00, 0x52, 0x00, 0x5d, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x77, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x18, 0x00, + 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, + 0x00, 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x09, 0x00, 0x1f, 0x00, 0x33, 0x00, + 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, 0x00, + 0x42, 0x31, 0x44, 0x2a, 0x48, 0x24, 0x4b, 0x1f, 0x4d, 0x1b, 0x51, 0x18, 0x55, 0x15, 0x57, 0x12, + 0x59, 0x10, 0x5b, 0x0e, 0x5f, 0x0c, 0x63, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x6a, 0x07, + 0x6b, 0x06, 0x6e, 0x06, 0x71, 0x05, 0x73, 0x05, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x43, 0x0e, 0x38, 0x13, 0x2f, 0x18, 0x28, 0x1d, 0x22, 0x22, 0x1d, 0x27, 0x19, 0x2b, 0x16, + 0x30, 0x12, 0x35, 0x11, 0x39, 0x0f, 0x3c, 0x0c, 0x41, 0x0b, 0x44, 0x0a, 0x47, 0x09, 0x4a, 0x07, + 0x4e, 0x07, 0x50, 0x05, 0x54, 0x05, 0x55, 0x04, 0x60, 0x21, 0x63, 0x1c, 0x65, 0x17, 0x68, 0x14, + 0x6a, 0x11, 0x6c, 0x0e, 0x6f, 0x0c, 0x71, 0x0b, 0x73, 0x09, 0x76, 0x08, 0x78, 0x07, 0x79, 0x06, + 0x7c, 0x05, 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x82, 0x03, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, 0x5f, 0x00, 0x66, 0x00, + 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4d, 0x00, 0x3b, 0x00, 0x27, 0x00, 0x13, 0x06, 0x07, 0x10, 0x00, + 0x22, 0x00, 0x32, 0x00, 0x40, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x95, 0x00, 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, 0x5f, 0x00, 0x66, 0x00, + 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, 0x0c, 0x00, 0x1f, 0x00, + 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, 0x00, + 0x42, 0x32, 0x44, 0x2b, 0x47, 0x25, 0x4b, 0x21, 0x4c, 0x1d, 0x4f, 0x19, 0x54, 0x16, 0x56, 0x14, + 0x58, 0x11, 0x5a, 0x10, 0x5d, 0x0e, 0x61, 0x0c, 0x63, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, + 0x69, 0x07, 0x6b, 0x07, 0x6d, 0x06, 0x71, 0x05, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x43, 0x0e, 0x39, 0x12, 0x31, 0x17, 0x2a, 0x1c, 0x24, 0x21, 0x1f, 0x26, 0x1b, 0x2a, 0x18, + 0x2e, 0x16, 0x32, 0x12, 0x35, 0x0f, 0x3a, 0x0f, 0x3d, 0x0c, 0x41, 0x0b, 0x44, 0x0a, 0x47, 0x09, + 0x4a, 0x07, 0x4e, 0x07, 0x4f, 0x05, 0x53, 0x05, 0x60, 0x21, 0x62, 0x1c, 0x65, 0x18, 0x67, 0x15, + 0x6a, 0x12, 0x6c, 0x0f, 0x6e, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x09, 0x76, 0x07, 0x79, 0x07, + 0x7a, 0x06, 0x7c, 0x05, 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x82, 0x03, 0x83, 0x02, 0x85, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, + 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x42, 0x00, 0x32, 0x00, 0x20, 0x00, 0x0e, 0x07, 0x06, + 0x10, 0x00, 0x20, 0x00, 0x2f, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x58, 0x00, 0x5f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, + 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, 0x03, 0x0f, 0x00, + 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, 0x00, + 0x42, 0x33, 0x43, 0x2c, 0x46, 0x27, 0x4a, 0x22, 0x4c, 0x1e, 0x4d, 0x1b, 0x52, 0x18, 0x55, 0x15, + 0x57, 0x13, 0x58, 0x11, 0x5a, 0x0f, 0x5e, 0x0e, 0x62, 0x0c, 0x63, 0x0b, 0x65, 0x0a, 0x66, 0x09, + 0x68, 0x08, 0x69, 0x07, 0x6b, 0x07, 0x6c, 0x06, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x44, 0x0d, 0x3a, 0x12, 0x32, 0x16, 0x2b, 0x1b, 0x26, 0x1f, 0x22, 0x24, 0x1d, 0x28, 0x19, + 0x2b, 0x16, 0x30, 0x14, 0x34, 0x12, 0x37, 0x0f, 0x3b, 0x0e, 0x3e, 0x0c, 0x41, 0x0b, 0x45, 0x0a, + 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x4f, 0x05, 0x60, 0x22, 0x62, 0x1d, 0x65, 0x19, 0x66, 0x15, + 0x69, 0x13, 0x6b, 0x11, 0x6d, 0x0e, 0x6f, 0x0c, 0x71, 0x0b, 0x73, 0x0a, 0x76, 0x09, 0x77, 0x07, + 0x79, 0x07, 0x7a, 0x06, 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, 0x80, 0x03, 0x82, 0x03, 0x83, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, + 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2a, 0x00, 0x1a, 0x00, 0x0b, + 0x08, 0x05, 0x10, 0x00, 0x1e, 0x00, 0x2c, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, 0x3c, 0x00, 0x26, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, + 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, + 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, 0x00, + 0x42, 0x33, 0x43, 0x2d, 0x45, 0x28, 0x49, 0x23, 0x4b, 0x1f, 0x4d, 0x1c, 0x50, 0x19, 0x55, 0x16, + 0x56, 0x14, 0x58, 0x12, 0x59, 0x11, 0x5b, 0x0e, 0x5f, 0x0d, 0x62, 0x0c, 0x63, 0x0b, 0x65, 0x0a, + 0x66, 0x09, 0x68, 0x09, 0x69, 0x07, 0x6b, 0x07, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x44, 0x0d, 0x3a, 0x12, 0x33, 0x16, 0x2c, 0x19, 0x27, 0x1e, 0x22, 0x21, 0x1e, 0x26, 0x1a, + 0x2b, 0x18, 0x2e, 0x16, 0x31, 0x12, 0x35, 0x11, 0x38, 0x0f, 0x3b, 0x0d, 0x3f, 0x0c, 0x41, 0x0a, + 0x45, 0x0a, 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x60, 0x22, 0x62, 0x1d, 0x64, 0x19, 0x66, 0x16, + 0x68, 0x13, 0x6a, 0x11, 0x6c, 0x0f, 0x6e, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x09, 0x76, 0x08, + 0x78, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, 0x80, 0x03, 0x82, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x33, 0x00, 0x3d, 0x00, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x32, 0x00, 0x24, 0x00, 0x15, + 0x02, 0x0a, 0x09, 0x04, 0x10, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x34, 0x00, 0x3e, 0x00, 0x46, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x33, 0x00, 0x3d, 0x00, + 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, + 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, 0x00, + 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, 0x49, 0x24, 0x4b, 0x20, 0x4c, 0x1d, 0x4e, 0x1a, 0x53, 0x18, + 0x55, 0x15, 0x56, 0x13, 0x58, 0x11, 0x5a, 0x10, 0x5c, 0x0e, 0x60, 0x0d, 0x63, 0x0c, 0x64, 0x0b, + 0x65, 0x0a, 0x66, 0x09, 0x68, 0x09, 0x69, 0x07, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x44, 0x0c, 0x3b, 0x11, 0x34, 0x16, 0x2e, 0x19, 0x28, 0x1d, 0x23, 0x21, 0x20, 0x25, 0x1d, + 0x29, 0x19, 0x2b, 0x16, 0x30, 0x14, 0x33, 0x12, 0x35, 0x0f, 0x3a, 0x0f, 0x3c, 0x0c, 0x40, 0x0c, + 0x41, 0x0a, 0x45, 0x0a, 0x47, 0x09, 0x4a, 0x07, 0x60, 0x22, 0x62, 0x1d, 0x64, 0x1a, 0x66, 0x17, + 0x68, 0x14, 0x6a, 0x11, 0x6c, 0x10, 0x6e, 0x0e, 0x70, 0x0c, 0x71, 0x0b, 0x73, 0x0a, 0x75, 0x09, + 0x76, 0x07, 0x78, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, 0x80, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1f, + 0x00, 0x12, 0x03, 0x09, 0x09, 0x04, 0x10, 0x00, 0x1c, 0x00, 0x27, 0x00, 0x31, 0x00, 0x3a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, + 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, + 0x00, 0x07, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, 0x00, + 0x42, 0x34, 0x43, 0x2f, 0x44, 0x29, 0x49, 0x25, 0x4b, 0x21, 0x4c, 0x1e, 0x4d, 0x1b, 0x51, 0x18, + 0x55, 0x16, 0x56, 0x14, 0x58, 0x13, 0x59, 0x11, 0x5a, 0x0f, 0x5d, 0x0e, 0x61, 0x0c, 0x63, 0x0c, + 0x64, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x09, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x44, 0x0c, 0x3c, 0x10, 0x35, 0x15, 0x2f, 0x19, 0x2a, 0x1c, 0x25, 0x20, 0x22, 0x23, 0x1d, + 0x26, 0x1a, 0x2b, 0x18, 0x2e, 0x16, 0x30, 0x12, 0x35, 0x12, 0x37, 0x0f, 0x3b, 0x0f, 0x3d, 0x0c, + 0x41, 0x0c, 0x42, 0x0a, 0x46, 0x0a, 0x47, 0x09, 0x60, 0x22, 0x62, 0x1e, 0x64, 0x1a, 0x66, 0x17, + 0x68, 0x15, 0x6a, 0x12, 0x6b, 0x11, 0x6d, 0x0e, 0x6e, 0x0d, 0x71, 0x0c, 0x72, 0x0b, 0x74, 0x09, + 0x76, 0x09, 0x77, 0x07, 0x79, 0x07, 0x7a, 0x06, 0x7c, 0x06, 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x26, + 0x00, 0x1a, 0x00, 0x0f, 0x04, 0x08, 0x0a, 0x04, 0x10, 0x00, 0x1b, 0x00, 0x25, 0x00, 0x2f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, 0x5f, 0x00, 0x4e, 0x00, + 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, + 0x00, 0x10, 0x00, 0x03, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, 0x00, + 0x4f, 0x2a, 0x56, 0x28, 0x59, 0x27, 0x5a, 0x26, 0x5c, 0x26, 0x5c, 0x26, 0x5c, 0x26, 0x5d, 0x26, + 0x5d, 0x26, 0x5d, 0x25, 0x5d, 0x25, 0x5d, 0x25, 0x5d, 0x25, 0x5d, 0x25, 0x5e, 0x25, 0x5e, 0x25, + 0x5e, 0x24, 0x5e, 0x24, 0x5e, 0x24, 0x5e, 0x24, 0x92, 0x0e, 0x79, 0x14, 0x6f, 0x18, 0x6b, 0x1b, + 0x68, 0x1d, 0x67, 0x1d, 0x66, 0x1e, 0x65, 0x1f, 0x64, 0x20, 0x64, 0x20, 0x63, 0x20, 0x63, 0x21, + 0x63, 0x21, 0x63, 0x21, 0x63, 0x21, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, + 0x16, 0x23, 0x3c, 0x23, 0x4a, 0x23, 0x50, 0x23, 0x54, 0x23, 0x56, 0x23, 0x58, 0x23, 0x59, 0x23, + 0x5a, 0x23, 0x5a, 0x23, 0x5b, 0x23, 0x5b, 0x23, 0x5b, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, + 0x5c, 0x23, 0x5c, 0x23, 0x5d, 0x23, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x38, 0x00, 0x2d, + 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x05, 0x07, 0x0a, 0x03, 0x0f, 0x00, 0x1a, 0x00, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, + 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, + 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, 0x00, + 0x48, 0x31, 0x4f, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x29, 0x58, 0x29, 0x59, 0x28, 0x59, 0x27, + 0x5a, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0xa1, 0x01, 0x88, 0x07, 0x7b, 0x0c, 0x74, 0x0f, + 0x70, 0x12, 0x6d, 0x14, 0x6b, 0x16, 0x6a, 0x17, 0x69, 0x18, 0x68, 0x19, 0x67, 0x1a, 0x66, 0x1b, + 0x66, 0x1b, 0x65, 0x1c, 0x65, 0x1c, 0x65, 0x1d, 0x65, 0x1d, 0x64, 0x1d, 0x64, 0x1e, 0x64, 0x1e, + 0x0a, 0x32, 0x22, 0x25, 0x31, 0x24, 0x3b, 0x23, 0x42, 0x23, 0x47, 0x23, 0x4a, 0x23, 0x4d, 0x23, + 0x4f, 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x54, 0x23, 0x55, 0x23, 0x56, 0x23, 0x56, 0x23, + 0x57, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x32, + 0x00, 0x28, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x0b, 0x06, 0x07, 0x0b, 0x03, 0x0f, 0x00, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, + 0x50, 0x00, 0x41, 0x00, 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, + 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, 0x00, + 0x45, 0x34, 0x4b, 0x31, 0x4f, 0x2f, 0x52, 0x2d, 0x54, 0x2c, 0x55, 0x2b, 0x57, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x29, 0x58, 0x29, 0x59, 0x28, 0x59, 0x27, 0x5a, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0xa7, 0x00, 0x92, 0x03, 0x84, 0x06, 0x7d, 0x09, + 0x77, 0x0c, 0x73, 0x0e, 0x71, 0x10, 0x6f, 0x11, 0x6d, 0x13, 0x6b, 0x14, 0x6a, 0x15, 0x6a, 0x16, + 0x69, 0x17, 0x68, 0x17, 0x68, 0x18, 0x67, 0x19, 0x66, 0x19, 0x66, 0x1a, 0x66, 0x1a, 0x66, 0x1a, + 0x0a, 0x38, 0x18, 0x2b, 0x25, 0x27, 0x2e, 0x25, 0x35, 0x24, 0x3b, 0x23, 0x3f, 0x23, 0x43, 0x23, + 0x46, 0x23, 0x48, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4e, 0x23, 0x4f, 0x23, 0x50, 0x23, 0x51, 0x23, + 0x52, 0x23, 0x53, 0x23, 0x53, 0x23, 0x54, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x48, 0x00, 0x40, 0x00, 0x37, + 0x00, 0x2d, 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x01, 0x0a, 0x06, 0x06, 0x0b, 0x03, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x66, 0x00, + 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, + 0x00, 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, 0x00, + 0x44, 0x36, 0x49, 0x33, 0x4d, 0x31, 0x4f, 0x2f, 0x51, 0x2e, 0x53, 0x2d, 0x53, 0x2c, 0x55, 0x2b, + 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x29, 0x58, 0x29, + 0x59, 0x28, 0x5a, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0xaa, 0x00, 0x98, 0x01, 0x8c, 0x03, 0x83, 0x05, + 0x7d, 0x08, 0x79, 0x0a, 0x76, 0x0c, 0x73, 0x0d, 0x71, 0x0f, 0x6f, 0x10, 0x6e, 0x11, 0x6d, 0x12, + 0x6c, 0x13, 0x6b, 0x14, 0x6a, 0x15, 0x6a, 0x15, 0x69, 0x16, 0x68, 0x17, 0x68, 0x17, 0x68, 0x18, + 0x0a, 0x3c, 0x14, 0x30, 0x1e, 0x2a, 0x26, 0x27, 0x2d, 0x25, 0x32, 0x24, 0x36, 0x24, 0x3b, 0x24, + 0x3e, 0x23, 0x41, 0x23, 0x43, 0x23, 0x45, 0x23, 0x47, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, + 0x4d, 0x23, 0x4d, 0x23, 0x4f, 0x23, 0x50, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x11, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x11, 0x03, 0x23, 0x00, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, + 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x38, 0x48, 0x35, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2c, 0x54, 0x2b, 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x29, 0xac, 0x00, 0x9d, 0x00, 0x91, 0x02, 0x89, 0x03, + 0x82, 0x05, 0x7e, 0x07, 0x7a, 0x09, 0x77, 0x0a, 0x75, 0x0c, 0x73, 0x0d, 0x71, 0x0e, 0x70, 0x0f, + 0x6e, 0x10, 0x6e, 0x11, 0x6c, 0x12, 0x6c, 0x13, 0x6b, 0x13, 0x6a, 0x14, 0x6a, 0x15, 0x6a, 0x15, + 0x09, 0x3f, 0x11, 0x33, 0x1a, 0x2d, 0x21, 0x29, 0x27, 0x27, 0x2c, 0x26, 0x30, 0x25, 0x34, 0x24, + 0x37, 0x24, 0x3a, 0x24, 0x3d, 0x23, 0x40, 0x23, 0x42, 0x23, 0x44, 0x23, 0x45, 0x23, 0x47, 0x23, + 0x48, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x23, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, 0x00, + 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, + 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x39, 0x46, 0x36, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x54, 0x2b, 0x55, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0xae, 0x00, 0xa1, 0x00, 0x96, 0x01, 0x8d, 0x02, + 0x87, 0x03, 0x82, 0x05, 0x7e, 0x07, 0x7b, 0x08, 0x78, 0x09, 0x76, 0x0b, 0x74, 0x0c, 0x73, 0x0d, + 0x71, 0x0e, 0x70, 0x0e, 0x6f, 0x0f, 0x6e, 0x11, 0x6d, 0x11, 0x6c, 0x11, 0x6c, 0x12, 0x6b, 0x13, + 0x09, 0x40, 0x10, 0x36, 0x17, 0x2f, 0x1d, 0x2c, 0x22, 0x29, 0x27, 0x27, 0x2b, 0x26, 0x2f, 0x25, + 0x32, 0x25, 0x35, 0x24, 0x38, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3f, 0x23, 0x40, 0x23, 0x42, 0x23, + 0x44, 0x23, 0x45, 0x23, 0x46, 0x23, 0x48, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x46, 0x00, 0x1f, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, 0x00, + 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, + 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x39, 0x45, 0x37, 0x48, 0x35, 0x4a, 0x33, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, + 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x54, 0x2c, 0x55, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0xaf, 0x00, 0xa3, 0x00, 0x99, 0x00, 0x91, 0x01, + 0x8b, 0x02, 0x86, 0x03, 0x81, 0x05, 0x7e, 0x06, 0x7b, 0x07, 0x79, 0x09, 0x77, 0x0a, 0x75, 0x0b, + 0x74, 0x0c, 0x73, 0x0c, 0x71, 0x0d, 0x71, 0x0e, 0x6f, 0x0f, 0x6e, 0x10, 0x6e, 0x11, 0x6d, 0x11, + 0x09, 0x41, 0x0f, 0x38, 0x15, 0x32, 0x1a, 0x2e, 0x1f, 0x2b, 0x24, 0x29, 0x27, 0x27, 0x2b, 0x26, + 0x2e, 0x25, 0x31, 0x25, 0x34, 0x25, 0x36, 0x24, 0x38, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3e, 0x23, + 0x40, 0x23, 0x41, 0x23, 0x43, 0x23, 0x44, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, 0x00, + 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, + 0x00, 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3a, 0x45, 0x38, 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x2f, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x53, 0x2c, + 0x55, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0xb0, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x95, 0x00, + 0x8e, 0x02, 0x89, 0x02, 0x85, 0x03, 0x81, 0x05, 0x7e, 0x06, 0x7c, 0x07, 0x7a, 0x08, 0x78, 0x09, + 0x76, 0x0a, 0x75, 0x0b, 0x73, 0x0c, 0x73, 0x0c, 0x71, 0x0d, 0x71, 0x0e, 0x70, 0x0e, 0x6e, 0x0f, + 0x09, 0x42, 0x0e, 0x39, 0x13, 0x33, 0x18, 0x2f, 0x1c, 0x2d, 0x21, 0x2a, 0x24, 0x29, 0x28, 0x27, + 0x2b, 0x26, 0x2e, 0x26, 0x30, 0x25, 0x33, 0x25, 0x35, 0x25, 0x37, 0x24, 0x39, 0x24, 0x3b, 0x24, + 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x23, 0x40, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, 0x00, + 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3b, 0x44, 0x39, 0x46, 0x36, 0x49, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x2f, 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2c, 0x54, 0x2b, 0x56, 0x2a, 0xb1, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x98, 0x00, + 0x91, 0x01, 0x8c, 0x02, 0x88, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, 0x7c, 0x07, 0x7a, 0x07, + 0x79, 0x09, 0x77, 0x09, 0x76, 0x0b, 0x74, 0x0b, 0x73, 0x0c, 0x73, 0x0c, 0x71, 0x0d, 0x71, 0x0e, + 0x09, 0x43, 0x0e, 0x3b, 0x12, 0x35, 0x16, 0x31, 0x1a, 0x2e, 0x1e, 0x2c, 0x22, 0x2a, 0x25, 0x28, + 0x28, 0x27, 0x2a, 0x27, 0x2d, 0x26, 0x30, 0x25, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x37, 0x24, + 0x39, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, 0x00, + 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x00, 0x00, 0x08, + 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3b, 0x44, 0x39, 0x46, 0x37, 0x48, 0x35, 0x4a, 0x35, 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0xb1, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9a, 0x00, + 0x94, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x87, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7d, 0x06, + 0x7b, 0x07, 0x79, 0x08, 0x78, 0x09, 0x76, 0x0a, 0x76, 0x0b, 0x74, 0x0b, 0x73, 0x0c, 0x72, 0x0c, + 0x09, 0x44, 0x0d, 0x3c, 0x11, 0x36, 0x15, 0x33, 0x19, 0x30, 0x1c, 0x2d, 0x20, 0x2b, 0x23, 0x2a, + 0x25, 0x28, 0x28, 0x27, 0x2a, 0x27, 0x2d, 0x26, 0x2f, 0x26, 0x31, 0x25, 0x33, 0x25, 0x34, 0x25, + 0x36, 0x24, 0x38, 0x24, 0x39, 0x24, 0x3a, 0x24, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x09, 0x00, 0x1f, 0x00, 0x33, 0x00, + 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, 0x00, + 0x00, 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3c, 0x43, 0x39, 0x46, 0x38, 0x46, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0xb2, 0x00, 0xaa, 0x00, 0xa2, 0x00, 0x9c, 0x00, + 0x96, 0x00, 0x91, 0x01, 0x8d, 0x02, 0x89, 0x02, 0x86, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, + 0x7d, 0x06, 0x7b, 0x07, 0x79, 0x07, 0x79, 0x09, 0x77, 0x09, 0x76, 0x0a, 0x75, 0x0b, 0x74, 0x0b, + 0x0a, 0x44, 0x0d, 0x3d, 0x10, 0x38, 0x14, 0x34, 0x17, 0x31, 0x1b, 0x2e, 0x1e, 0x2d, 0x21, 0x2b, + 0x23, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2d, 0x26, 0x2e, 0x26, 0x30, 0x25, 0x32, 0x25, + 0x34, 0x25, 0x35, 0x25, 0x36, 0x24, 0x38, 0x24, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, 0x0c, 0x00, 0x1f, 0x00, + 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3c, 0x43, 0x39, 0x46, 0x39, 0x46, 0x37, 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, 0x32, + 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, 0x52, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0xb2, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9e, 0x00, + 0x98, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x88, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, + 0x7f, 0x06, 0x7d, 0x06, 0x7c, 0x07, 0x7a, 0x07, 0x79, 0x08, 0x78, 0x09, 0x76, 0x09, 0x76, 0x0a, + 0x0a, 0x44, 0x0c, 0x3e, 0x10, 0x39, 0x13, 0x35, 0x16, 0x32, 0x19, 0x30, 0x1c, 0x2e, 0x1f, 0x2c, + 0x21, 0x2b, 0x24, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x26, 0x2e, 0x26, 0x30, 0x25, + 0x31, 0x25, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, 0x03, 0x0f, 0x00, + 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, 0x00, + 0x1f, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x07, 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x39, 0x46, 0x39, 0x46, 0x38, 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x34, + 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, 0x51, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0xb2, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9f, 0x00, + 0x9a, 0x00, 0x96, 0x00, 0x91, 0x01, 0x8e, 0x01, 0x8a, 0x02, 0x88, 0x02, 0x85, 0x03, 0x83, 0x04, + 0x81, 0x05, 0x7f, 0x05, 0x7d, 0x06, 0x7c, 0x07, 0x7a, 0x07, 0x79, 0x07, 0x78, 0x09, 0x77, 0x09, + 0x0a, 0x44, 0x0c, 0x3f, 0x0f, 0x3a, 0x12, 0x36, 0x15, 0x33, 0x18, 0x30, 0x1b, 0x2e, 0x1d, 0x2d, + 0x20, 0x2b, 0x22, 0x2a, 0x24, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x26, 0x2e, 0x26, + 0x2f, 0x26, 0x31, 0x25, 0x33, 0x25, 0x33, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, + 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, + 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3a, 0x45, 0x39, 0x46, 0x39, 0x46, 0x36, 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, + 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0xb3, 0x00, 0xac, 0x00, 0xa7, 0x00, 0xa1, 0x00, + 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8c, 0x02, 0x89, 0x02, 0x87, 0x02, 0x85, 0x03, + 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7d, 0x06, 0x7c, 0x06, 0x7b, 0x07, 0x79, 0x07, 0x79, 0x08, + 0x0a, 0x45, 0x0c, 0x3f, 0x0f, 0x3a, 0x11, 0x37, 0x14, 0x34, 0x17, 0x32, 0x1a, 0x30, 0x1c, 0x2e, + 0x1e, 0x2d, 0x21, 0x2b, 0x22, 0x2a, 0x25, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, + 0x2d, 0x26, 0x2f, 0x26, 0x30, 0x25, 0x32, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, + 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, 0x00, + 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3a, 0x44, 0x39, 0x46, 0x39, 0x46, 0x37, 0x48, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0xb3, 0x00, 0xad, 0x00, 0xa7, 0x00, 0xa3, 0x00, + 0x9d, 0x00, 0x99, 0x00, 0x95, 0x00, 0x91, 0x01, 0x8e, 0x01, 0x8b, 0x02, 0x88, 0x02, 0x86, 0x02, + 0x84, 0x03, 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, 0x7b, 0x07, 0x7a, 0x07, + 0x0a, 0x45, 0x0c, 0x40, 0x0e, 0x3b, 0x11, 0x38, 0x13, 0x35, 0x16, 0x32, 0x18, 0x30, 0x1b, 0x2e, + 0x1d, 0x2d, 0x1f, 0x2c, 0x21, 0x2b, 0x23, 0x2a, 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, + 0x2c, 0x27, 0x2d, 0x26, 0x2f, 0x26, 0x2f, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, + 0x00, 0x07, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, 0x00, + 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x39, 0x46, 0x38, 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2f, 0x52, 0x2e, 0xb3, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa3, 0x00, + 0x9f, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8d, 0x02, 0x8a, 0x02, 0x88, 0x02, + 0x86, 0x03, 0x84, 0x03, 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, 0x7c, 0x07, + 0x0a, 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x38, 0x13, 0x36, 0x15, 0x33, 0x18, 0x31, 0x1a, 0x30, + 0x1c, 0x2e, 0x1e, 0x2d, 0x20, 0x2b, 0x22, 0x2b, 0x23, 0x2a, 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, + 0x2a, 0x27, 0x2c, 0x27, 0x2d, 0x26, 0x2f, 0x26, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, + 0x00, 0x10, 0x00, 0x03, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, 0x00, + 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3c, 0x43, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x36, 0x49, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, 0xb3, 0x00, 0xae, 0x00, 0xa9, 0x00, 0xa4, 0x00, + 0xa0, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x94, 0x00, 0x91, 0x01, 0x8e, 0x01, 0x8c, 0x02, 0x89, 0x02, + 0x87, 0x02, 0x86, 0x03, 0x84, 0x03, 0x82, 0x04, 0x80, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, + 0x0a, 0x45, 0x0b, 0x40, 0x0e, 0x3c, 0x10, 0x39, 0x12, 0x37, 0x14, 0x34, 0x17, 0x32, 0x19, 0x30, + 0x1b, 0x2e, 0x1d, 0x2d, 0x1f, 0x2d, 0x20, 0x2b, 0x22, 0x2b, 0x24, 0x2a, 0x25, 0x29, 0x27, 0x28, + 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, 0x2c, 0x26, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, + 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, + 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3c, 0x43, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x37, 0x48, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0xb3, 0x00, 0xae, 0x00, 0xaa, 0x00, 0xa5, 0x00, + 0xa1, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x96, 0x00, 0x93, 0x00, 0x90, 0x01, 0x8d, 0x01, 0x8b, 0x02, + 0x89, 0x02, 0x87, 0x02, 0x85, 0x03, 0x83, 0x03, 0x82, 0x04, 0x80, 0x05, 0x7f, 0x05, 0x7e, 0x06, + 0x0a, 0x45, 0x0b, 0x41, 0x0e, 0x3d, 0x10, 0x3a, 0x11, 0x37, 0x14, 0x34, 0x16, 0x32, 0x18, 0x31, + 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2d, 0x20, 0x2c, 0x21, 0x2b, 0x22, 0x2a, 0x24, 0x2a, 0x25, 0x29, + 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, + 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, 0x00, + 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x36, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa6, 0x00, + 0xa2, 0x00, 0x9e, 0x00, 0x9a, 0x00, 0x97, 0x00, 0x94, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, 0x02, + 0x8a, 0x02, 0x88, 0x02, 0x86, 0x02, 0x85, 0x03, 0x83, 0x03, 0x82, 0x04, 0x80, 0x05, 0x7f, 0x05, + 0x0a, 0x45, 0x0b, 0x41, 0x0d, 0x3e, 0x10, 0x3a, 0x11, 0x38, 0x13, 0x35, 0x16, 0x33, 0x17, 0x32, + 0x19, 0x30, 0x1b, 0x2e, 0x1d, 0x2e, 0x1e, 0x2d, 0x20, 0x2b, 0x22, 0x2b, 0x23, 0x2a, 0x25, 0x2a, + 0x25, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, + 0x00, 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, + 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3a, 0x45, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x37, 0x49, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0xb4, 0x00, 0xaf, 0x00, 0xab, 0x00, 0xa7, 0x00, + 0xa3, 0x00, 0x9f, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x95, 0x00, 0x92, 0x00, 0x90, 0x01, 0x8d, 0x01, + 0x8b, 0x02, 0x89, 0x02, 0x88, 0x02, 0x86, 0x02, 0x85, 0x03, 0x82, 0x03, 0x82, 0x04, 0x80, 0x05, + 0x0a, 0x46, 0x0b, 0x42, 0x0d, 0x3e, 0x0f, 0x3b, 0x11, 0x38, 0x13, 0x36, 0x15, 0x34, 0x16, 0x32, + 0x18, 0x30, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2d, 0x20, 0x2d, 0x20, 0x2b, 0x22, 0x2b, 0x23, 0x2a, + 0x25, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x28, 0x28, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x32, 0x74, 0x38, 0x57, 0x3a, 0x4f, 0x3c, 0x4b, + 0x3c, 0x49, 0x3d, 0x47, 0x3d, 0x46, 0x3d, 0x45, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, + 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, + 0x41, 0x09, 0x1b, 0x31, 0x27, 0x38, 0x2d, 0x3a, 0x31, 0x3b, 0x33, 0x3c, 0x35, 0x3c, 0x36, 0x3d, + 0x37, 0x3d, 0x38, 0x3d, 0x38, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, + 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x73, 0x00, 0x38, 0x31, 0x3a, 0x38, 0x3c, 0x3a, + 0x3c, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x3d, 0x28, 0x38, 0x30, 0x38, 0x34, 0x39, 0x36, + 0x39, 0x38, 0x3a, 0x39, 0x3a, 0x39, 0x3b, 0x3a, 0x3b, 0x3b, 0x3c, 0x3b, 0x3c, 0x3b, 0x3d, 0x3c, + 0x3d, 0x3c, 0x3e, 0x3c, 0x3e, 0x3c, 0x3e, 0x3c, 0x3e, 0x3c, 0x3e, 0x3d, 0x3e, 0x3d, 0x3e, 0x3d, + 0x00, 0x7e, 0x05, 0x58, 0x0f, 0x4e, 0x17, 0x49, 0x1d, 0x47, 0x21, 0x45, 0x24, 0x44, 0x27, 0x43, + 0x29, 0x43, 0x2b, 0x43, 0x2d, 0x43, 0x2e, 0x42, 0x2f, 0x42, 0x31, 0x42, 0x31, 0x42, 0x32, 0x42, + 0x33, 0x42, 0x33, 0x42, 0x34, 0x42, 0x34, 0x42, 0x2b, 0x4e, 0x31, 0x47, 0x34, 0x45, 0x36, 0x44, + 0x38, 0x43, 0x39, 0x42, 0x39, 0x42, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, 0x3c, 0x41, 0x3c, 0x41, + 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x00, 0x4f, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x16, 0x3a, 0x20, 0x39, 0x25, 0x39, 0x29, + 0x39, 0x2d, 0x39, 0x2f, 0x3a, 0x31, 0x3a, 0x32, 0x3b, 0x33, 0x3a, 0x34, 0x3a, 0x35, 0x3a, 0x36, + 0x3a, 0x36, 0x3b, 0x37, 0x3b, 0x37, 0x3c, 0x38, 0x3c, 0x38, 0x3d, 0x38, 0x3e, 0x39, 0x3e, 0x39, + 0x00, 0x93, 0x01, 0x70, 0x06, 0x60, 0x0d, 0x56, 0x12, 0x52, 0x16, 0x4e, 0x1a, 0x4c, 0x1d, 0x4a, + 0x20, 0x48, 0x22, 0x47, 0x24, 0x46, 0x26, 0x45, 0x27, 0x44, 0x29, 0x44, 0x2a, 0x44, 0x2b, 0x44, + 0x2c, 0x43, 0x2d, 0x43, 0x2e, 0x43, 0x2f, 0x43, 0x28, 0x56, 0x2e, 0x4f, 0x31, 0x4b, 0x33, 0x49, + 0x35, 0x48, 0x36, 0x46, 0x37, 0x45, 0x38, 0x45, 0x39, 0x44, 0x39, 0x44, 0x39, 0x43, 0x39, 0x43, + 0x39, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3d, 0x42, 0x3d, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x8f, 0x00, 0x73, 0x00, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x56, 0x00, 0x45, 0x00, 0x22, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x43, 0x11, 0x3d, 0x18, 0x3b, 0x1e, 0x3a, 0x22, + 0x39, 0x25, 0x39, 0x28, 0x39, 0x2a, 0x39, 0x2c, 0x39, 0x2d, 0x3a, 0x2f, 0x3b, 0x30, 0x3b, 0x31, + 0x3b, 0x32, 0x3b, 0x33, 0x3a, 0x33, 0x3a, 0x34, 0x3a, 0x34, 0x3a, 0x35, 0x3a, 0x35, 0x3b, 0x35, + 0x00, 0x9d, 0x00, 0x7f, 0x03, 0x6d, 0x07, 0x62, 0x0c, 0x5b, 0x10, 0x56, 0x13, 0x52, 0x16, 0x50, + 0x19, 0x4d, 0x1c, 0x4c, 0x1e, 0x4b, 0x1f, 0x4b, 0x22, 0x4a, 0x23, 0x49, 0x24, 0x48, 0x25, 0x47, + 0x27, 0x46, 0x28, 0x45, 0x29, 0x44, 0x29, 0x44, 0x27, 0x59, 0x2c, 0x53, 0x2f, 0x4f, 0x31, 0x4d, + 0x32, 0x4b, 0x34, 0x4a, 0x35, 0x48, 0x35, 0x47, 0x36, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, + 0x39, 0x46, 0x39, 0x45, 0x39, 0x44, 0x39, 0x44, 0x39, 0x43, 0x39, 0x43, 0x39, 0x42, 0x3a, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x96, 0x00, 0x84, 0x00, 0x5b, 0x00, 0x2a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x44, 0x0f, 0x3e, 0x14, 0x3c, 0x19, 0x3a, 0x1d, + 0x3a, 0x20, 0x39, 0x23, 0x3a, 0x25, 0x3a, 0x27, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2c, 0x3a, 0x2c, + 0x3b, 0x2d, 0x3b, 0x2f, 0x3b, 0x2f, 0x3b, 0x30, 0x3b, 0x31, 0x3b, 0x32, 0x3a, 0x32, 0x3a, 0x33, + 0x00, 0xa3, 0x00, 0x88, 0x01, 0x77, 0x04, 0x6b, 0x08, 0x63, 0x0b, 0x5d, 0x0e, 0x59, 0x11, 0x57, + 0x14, 0x54, 0x16, 0x51, 0x19, 0x4f, 0x1a, 0x4d, 0x1c, 0x4c, 0x1e, 0x4c, 0x1f, 0x4b, 0x21, 0x4b, + 0x22, 0x4a, 0x23, 0x49, 0x24, 0x49, 0x25, 0x49, 0x27, 0x5a, 0x2a, 0x55, 0x2d, 0x52, 0x2f, 0x4f, + 0x31, 0x4e, 0x32, 0x4b, 0x33, 0x4a, 0x35, 0x4a, 0x35, 0x49, 0x35, 0x48, 0x35, 0x46, 0x37, 0x46, + 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x45, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x99, 0x00, 0x8d, 0x00, 0x6f, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5b, 0x00, 0x55, 0x00, 0x42, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x45, 0x0e, 0x40, 0x12, 0x3d, 0x16, 0x3b, 0x1a, + 0x3b, 0x1d, 0x3a, 0x20, 0x39, 0x22, 0x39, 0x23, 0x3a, 0x25, 0x3a, 0x26, 0x3a, 0x28, 0x39, 0x29, + 0x39, 0x2a, 0x39, 0x2b, 0x3a, 0x2c, 0x3b, 0x2d, 0x3c, 0x2e, 0x3b, 0x2e, 0x3b, 0x2f, 0x3b, 0x30, + 0x00, 0xa7, 0x00, 0x90, 0x00, 0x7e, 0x02, 0x73, 0x05, 0x6a, 0x08, 0x65, 0x0b, 0x5f, 0x0d, 0x5b, + 0x10, 0x58, 0x12, 0x56, 0x14, 0x55, 0x16, 0x52, 0x18, 0x50, 0x1a, 0x4e, 0x1b, 0x4d, 0x1d, 0x4c, + 0x1e, 0x4c, 0x1f, 0x4b, 0x20, 0x4b, 0x21, 0x4b, 0x26, 0x5b, 0x2a, 0x57, 0x2c, 0x54, 0x2e, 0x51, + 0x30, 0x4f, 0x31, 0x4e, 0x31, 0x4c, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x49, + 0x35, 0x47, 0x36, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x0d, 0x41, 0x11, 0x3d, 0x14, 0x3d, 0x17, + 0x3b, 0x1a, 0x3b, 0x1c, 0x3b, 0x1e, 0x39, 0x21, 0x39, 0x22, 0x3a, 0x23, 0x3a, 0x25, 0x3a, 0x26, + 0x3a, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2a, 0x39, 0x2b, 0x3b, 0x2c, 0x3c, 0x2d, 0x3c, 0x2e, + 0x00, 0xa9, 0x00, 0x95, 0x00, 0x85, 0x01, 0x79, 0x03, 0x70, 0x06, 0x69, 0x08, 0x65, 0x0b, 0x61, + 0x0d, 0x5c, 0x0f, 0x59, 0x11, 0x58, 0x13, 0x56, 0x14, 0x55, 0x16, 0x53, 0x18, 0x51, 0x19, 0x4f, + 0x1b, 0x4d, 0x1c, 0x4d, 0x1d, 0x4c, 0x1e, 0x4c, 0x26, 0x5c, 0x29, 0x58, 0x2b, 0x55, 0x2d, 0x53, + 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x49, 0x35, 0x48, 0x35, 0x47, 0x36, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9c, 0x00, 0x95, 0x00, 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x0c, 0x42, 0x0f, 0x3f, 0x13, 0x3d, 0x15, + 0x3c, 0x18, 0x3a, 0x1a, 0x3b, 0x1c, 0x3b, 0x1e, 0x39, 0x20, 0x39, 0x21, 0x39, 0x22, 0x3b, 0x24, + 0x3a, 0x25, 0x3a, 0x26, 0x3a, 0x27, 0x39, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2a, 0x3a, 0x2b, + 0x00, 0xab, 0x00, 0x99, 0x00, 0x8b, 0x01, 0x7e, 0x02, 0x77, 0x04, 0x6f, 0x06, 0x69, 0x08, 0x66, + 0x0b, 0x62, 0x0d, 0x5e, 0x0e, 0x5b, 0x10, 0x59, 0x12, 0x57, 0x13, 0x56, 0x15, 0x55, 0x16, 0x54, + 0x18, 0x52, 0x19, 0x50, 0x1a, 0x4e, 0x1b, 0x4d, 0x26, 0x5c, 0x28, 0x59, 0x2a, 0x57, 0x2c, 0x53, + 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4c, 0x33, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x49, 0x35, 0x48, 0x36, 0x46, 0x37, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x0c, 0x42, 0x0e, 0x40, 0x11, 0x3d, 0x13, + 0x3d, 0x16, 0x3b, 0x18, 0x3b, 0x1a, 0x3c, 0x1c, 0x3b, 0x1e, 0x3a, 0x1f, 0x39, 0x21, 0x39, 0x21, + 0x3a, 0x23, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, 0x3a, 0x27, 0x39, 0x27, 0x39, 0x28, 0x39, 0x2a, + 0x00, 0xac, 0x00, 0x9c, 0x00, 0x8f, 0x00, 0x83, 0x02, 0x7b, 0x03, 0x74, 0x05, 0x6d, 0x07, 0x69, + 0x09, 0x66, 0x0b, 0x63, 0x0c, 0x5f, 0x0e, 0x5c, 0x0f, 0x5a, 0x11, 0x58, 0x12, 0x57, 0x14, 0x56, + 0x15, 0x55, 0x16, 0x55, 0x18, 0x53, 0x18, 0x51, 0x26, 0x5d, 0x27, 0x59, 0x2a, 0x57, 0x2b, 0x55, + 0x2e, 0x53, 0x2e, 0x53, 0x2f, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, + 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x49, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, 0x3c, 0x00, 0x26, + 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x42, 0x0d, 0x41, 0x10, 0x3e, 0x13, + 0x3d, 0x15, 0x3c, 0x17, 0x3a, 0x19, 0x3b, 0x1a, 0x3c, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x20, + 0x39, 0x21, 0x3a, 0x22, 0x3b, 0x23, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, 0x3a, 0x27, 0x39, 0x27, + 0x00, 0xad, 0x00, 0x9f, 0x00, 0x92, 0x00, 0x88, 0x01, 0x7e, 0x02, 0x78, 0x04, 0x72, 0x05, 0x6d, + 0x07, 0x69, 0x09, 0x66, 0x0a, 0x64, 0x0c, 0x61, 0x0d, 0x5d, 0x0f, 0x5a, 0x10, 0x59, 0x11, 0x58, + 0x13, 0x57, 0x14, 0x56, 0x15, 0x55, 0x16, 0x55, 0x26, 0x5d, 0x27, 0x5a, 0x2a, 0x57, 0x2a, 0x56, + 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4e, + 0x32, 0x4b, 0x33, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, + 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x20, 0x00, + 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x43, 0x0d, 0x41, 0x0f, 0x3f, 0x12, + 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x17, 0x3a, 0x19, 0x3a, 0x1a, 0x3c, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, + 0x39, 0x1f, 0x39, 0x21, 0x39, 0x21, 0x3b, 0x22, 0x3b, 0x24, 0x3a, 0x24, 0x3a, 0x24, 0x3a, 0x26, + 0x00, 0xae, 0x00, 0xa2, 0x00, 0x94, 0x00, 0x8b, 0x00, 0x82, 0x02, 0x7b, 0x03, 0x76, 0x05, 0x71, + 0x06, 0x6c, 0x07, 0x69, 0x09, 0x66, 0x0a, 0x64, 0x0c, 0x62, 0x0d, 0x5f, 0x0e, 0x5b, 0x10, 0x5a, + 0x10, 0x58, 0x12, 0x58, 0x13, 0x56, 0x14, 0x56, 0x25, 0x5d, 0x27, 0x5b, 0x29, 0x57, 0x2a, 0x57, + 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4e, 0x31, 0x4d, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, + 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, 0x00, + 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x43, 0x0d, 0x42, 0x0f, 0x40, 0x11, + 0x3d, 0x13, 0x3e, 0x14, 0x3c, 0x16, 0x3b, 0x17, 0x3a, 0x19, 0x3b, 0x1a, 0x3c, 0x1c, 0x3b, 0x1d, + 0x3b, 0x1f, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, 0x3a, 0x21, 0x3b, 0x22, 0x3b, 0x24, 0x3a, 0x24, + 0x00, 0xae, 0x00, 0xa3, 0x00, 0x97, 0x00, 0x8e, 0x00, 0x86, 0x01, 0x7e, 0x02, 0x79, 0x03, 0x75, + 0x05, 0x70, 0x06, 0x6b, 0x07, 0x68, 0x09, 0x66, 0x0a, 0x64, 0x0b, 0x63, 0x0c, 0x5f, 0x0e, 0x5d, + 0x0f, 0x5a, 0x10, 0x59, 0x11, 0x58, 0x13, 0x58, 0x25, 0x5d, 0x27, 0x5c, 0x29, 0x58, 0x2a, 0x57, + 0x2a, 0x56, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x33, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, 0x5f, 0x00, 0x4e, + 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, + 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x44, 0x0d, 0x42, 0x0e, 0x41, 0x10, + 0x3e, 0x12, 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x17, 0x3a, 0x18, 0x3b, 0x19, 0x3b, 0x1a, 0x3c, 0x1c, + 0x3b, 0x1c, 0x3b, 0x1e, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, 0x39, 0x21, 0x3b, 0x22, 0x3b, 0x23, + 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x90, 0x00, 0x8a, 0x01, 0x81, 0x02, 0x7c, 0x02, 0x77, + 0x04, 0x74, 0x05, 0x6f, 0x06, 0x6a, 0x07, 0x68, 0x09, 0x66, 0x0a, 0x64, 0x0b, 0x63, 0x0c, 0x61, + 0x0e, 0x5e, 0x0e, 0x5b, 0x10, 0x5a, 0x10, 0x59, 0x25, 0x5d, 0x27, 0x5c, 0x28, 0x59, 0x2a, 0x57, + 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4c, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, + 0x00, 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x3d, 0x00, 0x34, 0x00, + 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x44, 0x0d, 0x42, 0x0e, 0x41, 0x0f, + 0x3f, 0x11, 0x3d, 0x13, 0x3e, 0x14, 0x3d, 0x15, 0x3c, 0x17, 0x3a, 0x18, 0x3a, 0x19, 0x3c, 0x1a, + 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1e, 0x39, 0x1f, 0x39, 0x1f, 0x39, 0x21, 0x39, 0x21, 0x3a, 0x21, + 0x00, 0xaf, 0x00, 0xa6, 0x00, 0x9c, 0x00, 0x92, 0x00, 0x8c, 0x00, 0x85, 0x01, 0x7e, 0x02, 0x7a, + 0x03, 0x76, 0x05, 0x73, 0x06, 0x6e, 0x07, 0x6a, 0x08, 0x68, 0x09, 0x66, 0x0a, 0x65, 0x0b, 0x63, + 0x0c, 0x62, 0x0d, 0x5f, 0x0e, 0x5c, 0x0f, 0x5a, 0x25, 0x5d, 0x27, 0x5c, 0x27, 0x59, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x50, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x34, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, + 0x00, 0x50, 0x00, 0x41, 0x00, 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, 0x00, + 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, 0x0f, + 0x40, 0x11, 0x3d, 0x12, 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x16, 0x3b, 0x17, 0x3a, 0x19, 0x3a, 0x19, + 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1e, 0x3a, 0x1f, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, + 0x00, 0xb0, 0x00, 0xa7, 0x00, 0x9e, 0x00, 0x94, 0x00, 0x8e, 0x00, 0x88, 0x01, 0x81, 0x02, 0x7c, + 0x02, 0x78, 0x03, 0x75, 0x05, 0x72, 0x06, 0x6d, 0x07, 0x6a, 0x08, 0x68, 0x09, 0x66, 0x0a, 0x65, + 0x0b, 0x63, 0x0c, 0x62, 0x0d, 0x60, 0x0e, 0x5d, 0x25, 0x5d, 0x27, 0x5c, 0x27, 0x5a, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x66, + 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, + 0x35, 0x00, 0x2c, 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, 0x0e, + 0x40, 0x10, 0x3e, 0x11, 0x3d, 0x13, 0x3e, 0x14, 0x3d, 0x15, 0x3c, 0x17, 0x3a, 0x17, 0x3a, 0x19, + 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x1f, 0x39, 0x1f, + 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xa0, 0x00, 0x96, 0x00, 0x90, 0x00, 0x8a, 0x00, 0x84, 0x01, 0x7e, + 0x02, 0x7a, 0x03, 0x77, 0x04, 0x74, 0x05, 0x71, 0x06, 0x6c, 0x07, 0x6a, 0x08, 0x68, 0x09, 0x66, + 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x63, 0x0c, 0x61, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x57, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, + 0x2f, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x00, 0x27, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x8f, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, + 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, + 0x00, 0x8c, 0x00, 0x63, 0x00, 0x7f, 0x00, 0x89, 0x00, 0x90, 0x00, 0x96, 0x00, 0x9b, 0x00, 0x9c, + 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x2c, 0x00, 0x57, 0x00, 0x82, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, + 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, + 0x11, 0x31, 0x00, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, + 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x45, 0x0b, 0x42, 0x0d, 0x42, 0x0e, + 0x41, 0x0f, 0x3f, 0x11, 0x3d, 0x12, 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x15, 0x3c, 0x17, 0x3a, 0x17, + 0x3a, 0x19, 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x1f, + 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x98, 0x00, 0x91, 0x00, 0x8c, 0x00, 0x87, 0x01, 0x80, + 0x02, 0x7c, 0x02, 0x79, 0x03, 0x76, 0x05, 0x74, 0x05, 0x70, 0x06, 0x6b, 0x07, 0x6a, 0x08, 0x68, + 0x09, 0x66, 0x0a, 0x65, 0x0b, 0x64, 0x0c, 0x63, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x58, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x00, 0x00, 0x00, 0x0d, 0x00, 0x4f, 0x00, 0x73, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x95, + 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, + 0x00, 0x63, 0x00, 0x4d, 0x00, 0x60, 0x00, 0x77, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x92, 0x00, 0x95, + 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x29, 0x01, 0x04, 0x16, 0x00, 0x57, 0x00, 0x78, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x94, 0x00, 0x96, + 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, + 0x23, 0x03, 0x00, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, + 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x41, 0x0e, + 0x42, 0x0f, 0x40, 0x11, 0x3d, 0x11, 0x3d, 0x13, 0x3f, 0x13, 0x3d, 0x15, 0x3c, 0x16, 0x3b, 0x17, + 0x3a, 0x18, 0x3a, 0x19, 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3b, 0x1e, + 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa2, 0x00, 0x9a, 0x00, 0x93, 0x00, 0x8d, 0x00, 0x89, 0x01, 0x83, + 0x02, 0x7e, 0x02, 0x7a, 0x03, 0x78, 0x03, 0x75, 0x05, 0x73, 0x06, 0x6f, 0x06, 0x6b, 0x07, 0x69, + 0x08, 0x68, 0x09, 0x66, 0x0a, 0x66, 0x0b, 0x64, 0x24, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x59, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x38, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7c, 0x00, 0x84, + 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, + 0x00, 0x7f, 0x00, 0x60, 0x00, 0x24, 0x00, 0x48, 0x00, 0x60, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x84, + 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x00, 0x27, 0x00, 0x01, 0x12, 0x00, 0x43, 0x00, 0x61, 0x00, 0x74, 0x00, 0x7f, 0x00, 0x87, + 0x00, 0x8c, 0x00, 0x90, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, + 0x46, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, + 0x00, 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x41, 0x0e, + 0x42, 0x0f, 0x40, 0x10, 0x3e, 0x11, 0x3d, 0x13, 0x3d, 0x13, 0x3f, 0x15, 0x3c, 0x15, 0x3c, 0x17, + 0x3a, 0x17, 0x3a, 0x18, 0x3b, 0x19, 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1c, + 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa3, 0x00, 0x9c, 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8b, 0x00, 0x86, + 0x01, 0x80, 0x02, 0x7c, 0x02, 0x79, 0x03, 0x77, 0x04, 0x75, 0x05, 0x72, 0x06, 0x6e, 0x07, 0x6b, + 0x07, 0x69, 0x09, 0x68, 0x09, 0x66, 0x0a, 0x66, 0x24, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5a, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x48, 0x00, 0x5d, 0x00, 0x6c, + 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, + 0x00, 0x89, 0x00, 0x77, 0x00, 0x48, 0x00, 0x0f, 0x00, 0x30, 0x00, 0x48, 0x00, 0x5d, 0x00, 0x6c, + 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x41, 0x00, 0x17, 0x00, 0x00, 0x10, 0x00, 0x36, 0x00, 0x51, 0x00, 0x64, 0x00, 0x71, + 0x00, 0x7a, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8d, 0x00, 0x90, 0x00, 0x92, 0x00, 0x93, + 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x00, 0x00, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, + 0x00, 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x46, 0x0b, 0x44, 0x0d, 0x42, 0x0d, + 0x42, 0x0e, 0x40, 0x0f, 0x40, 0x11, 0x3d, 0x11, 0x3d, 0x13, 0x3f, 0x13, 0x3e, 0x15, 0x3c, 0x15, + 0x3c, 0x17, 0x3a, 0x17, 0x3a, 0x18, 0x3b, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, + 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x95, 0x00, 0x90, 0x00, 0x8c, 0x00, 0x88, + 0x01, 0x82, 0x02, 0x7e, 0x02, 0x7b, 0x02, 0x78, 0x03, 0x76, 0x05, 0x74, 0x05, 0x71, 0x06, 0x6d, + 0x07, 0x6b, 0x07, 0x69, 0x09, 0x68, 0x09, 0x66, 0x24, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5b, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, + 0x00, 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x86, 0x00, 0x89, + 0x00, 0x90, 0x00, 0x83, 0x00, 0x60, 0x00, 0x30, 0x00, 0x02, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, + 0x00, 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x86, 0x00, 0x89, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00, 0x2e, 0x00, 0x46, 0x00, 0x57, + 0x00, 0x64, 0x00, 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, 0x00, 0x89, 0x00, 0x8b, + 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x00, 0x00, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, + 0x00, 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, + 0x1b, 0x85, 0x05, 0x96, 0x01, 0x9e, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xac, + 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, + 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb2, 0x00, 0xb2, 0x1c, 0x6e, 0x03, 0x8a, 0x00, 0x97, 0x00, 0x9e, + 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xad, 0x00, 0xad, + 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x0e, 0x92, 0x01, 0xa1, 0x00, 0xa7, 0x00, 0xaa, + 0x00, 0xac, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb2, + 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x33, + 0x00, 0x45, 0x00, 0x53, 0x00, 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, + 0x00, 0x96, 0x00, 0x8c, 0x00, 0x70, 0x00, 0x48, 0x00, 0x22, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x33, + 0x00, 0x45, 0x00, 0x53, 0x00, 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5b, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x23, 0x00, 0x0a, 0x02, 0x00, 0x10, 0x00, 0x29, 0x00, 0x3d, + 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x74, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, + 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x00, 0x00, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, + 0x00, 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, + 0x27, 0x60, 0x0f, 0x73, 0x07, 0x81, 0x03, 0x8a, 0x01, 0x91, 0x00, 0x95, 0x00, 0x99, 0x00, 0x9d, + 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xa8, + 0x00, 0xa9, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x29, 0x3a, 0x0f, 0x59, 0x06, 0x6c, 0x02, 0x79, + 0x00, 0x84, 0x00, 0x8b, 0x00, 0x90, 0x00, 0x94, 0x00, 0x98, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9f, + 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0xa7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x14, 0x79, 0x07, 0x88, 0x03, 0x92, 0x01, 0x98, + 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, + 0x00, 0xac, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, + 0x00, 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x18, + 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x56, 0x00, 0x47, 0x00, 0x31, 0x00, 0x1a, 0x00, 0x08, 0x04, 0x00, 0x10, 0x00, 0x25, + 0x00, 0x37, 0x00, 0x46, 0x00, 0x52, 0x00, 0x5d, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x77, + 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x33, + 0x00, 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, + 0x2d, 0x54, 0x17, 0x63, 0x0d, 0x6f, 0x07, 0x78, 0x04, 0x80, 0x02, 0x86, 0x01, 0x8b, 0x01, 0x8f, + 0x00, 0x92, 0x00, 0x95, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, + 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa4, 0x31, 0x27, 0x18, 0x40, 0x0d, 0x52, 0x07, 0x61, + 0x04, 0x6c, 0x02, 0x75, 0x01, 0x7c, 0x00, 0x82, 0x00, 0x87, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x91, + 0x00, 0x94, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x18, 0x6f, 0x0c, 0x7b, 0x06, 0x84, 0x03, 0x8c, + 0x02, 0x91, 0x01, 0x96, 0x00, 0x99, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa4, + 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xab, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, 0x5f, 0x00, 0x66, + 0x00, 0x9c, 0x00, 0x95, 0x00, 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, 0x5f, 0x00, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x59, 0x00, 0x4d, 0x00, 0x3b, 0x00, 0x27, 0x00, 0x13, 0x00, 0x07, 0x06, 0x00, 0x10, + 0x00, 0x22, 0x00, 0x32, 0x00, 0x40, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6b, + 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, 0x00, 0x00, 0x0c, 0x00, 0x1f, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, + 0x30, 0x4e, 0x1d, 0x5a, 0x12, 0x64, 0x0c, 0x6d, 0x08, 0x75, 0x05, 0x7a, 0x03, 0x7f, 0x02, 0x85, + 0x02, 0x89, 0x01, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x94, 0x00, 0x97, 0x00, 0x99, + 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0x9f, 0x36, 0x1e, 0x1f, 0x32, 0x12, 0x43, 0x0b, 0x50, + 0x07, 0x5a, 0x05, 0x63, 0x02, 0x6b, 0x01, 0x72, 0x01, 0x78, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x85, + 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1b, 0x6b, 0x0f, 0x74, 0x09, 0x7d, 0x05, 0x83, + 0x03, 0x89, 0x02, 0x8d, 0x01, 0x91, 0x00, 0x95, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, + 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, + 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x42, 0x00, 0x32, 0x00, 0x20, 0x00, 0x0e, 0x00, 0x06, 0x07, + 0x00, 0x10, 0x00, 0x20, 0x00, 0x2f, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x58, 0x00, 0x5f, + 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, 0x03, 0x00, 0x00, 0x0f, + 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, + 0x33, 0x4b, 0x21, 0x54, 0x16, 0x5d, 0x10, 0x65, 0x0b, 0x6c, 0x08, 0x72, 0x06, 0x77, 0x04, 0x7b, + 0x03, 0x7f, 0x02, 0x83, 0x02, 0x87, 0x01, 0x8a, 0x01, 0x8c, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x91, + 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x98, 0x3a, 0x19, 0x24, 0x2a, 0x18, 0x38, 0x10, 0x44, + 0x0b, 0x4e, 0x07, 0x57, 0x05, 0x5f, 0x04, 0x65, 0x02, 0x6b, 0x01, 0x71, 0x01, 0x76, 0x00, 0x79, + 0x00, 0x7e, 0x00, 0x80, 0x00, 0x84, 0x00, 0x87, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x8f, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1d, 0x68, 0x12, 0x70, 0x0c, 0x77, 0x08, 0x7d, + 0x05, 0x82, 0x03, 0x87, 0x02, 0x8b, 0x02, 0x8e, 0x01, 0x91, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, + 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, 0x3c, 0x00, 0x26, + 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2a, 0x00, 0x1a, 0x00, 0x0b, 0x00, + 0x05, 0x08, 0x00, 0x10, 0x00, 0x1e, 0x00, 0x2c, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, + 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, + 0x34, 0x49, 0x25, 0x51, 0x1a, 0x58, 0x13, 0x60, 0x0e, 0x65, 0x0b, 0x6b, 0x08, 0x71, 0x06, 0x75, + 0x05, 0x78, 0x04, 0x7b, 0x03, 0x7f, 0x02, 0x83, 0x02, 0x86, 0x01, 0x88, 0x01, 0x8a, 0x00, 0x8c, + 0x00, 0x8d, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x92, 0x3b, 0x17, 0x28, 0x24, 0x1c, 0x30, 0x14, 0x3b, + 0x0f, 0x45, 0x0b, 0x4d, 0x07, 0x55, 0x05, 0x5b, 0x04, 0x61, 0x03, 0x67, 0x02, 0x6b, 0x01, 0x6f, + 0x01, 0x74, 0x00, 0x77, 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x83, 0x00, 0x86, 0x00, 0x87, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1d, 0x67, 0x14, 0x6d, 0x0e, 0x73, 0x0a, 0x79, + 0x07, 0x7e, 0x05, 0x82, 0x03, 0x86, 0x02, 0x89, 0x02, 0x8c, 0x01, 0x8f, 0x01, 0x91, 0x00, 0x93, + 0x00, 0x96, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x33, 0x00, 0x3d, + 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, + 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x33, 0x00, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x32, 0x00, 0x24, 0x00, 0x15, 0x00, + 0x0a, 0x02, 0x04, 0x09, 0x00, 0x10, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x34, 0x00, 0x3e, 0x00, 0x46, + 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x00, + 0x00, 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, + 0x36, 0x47, 0x27, 0x4e, 0x1d, 0x54, 0x16, 0x5b, 0x11, 0x61, 0x0e, 0x65, 0x0b, 0x6a, 0x08, 0x6f, + 0x07, 0x73, 0x05, 0x76, 0x05, 0x79, 0x03, 0x7c, 0x02, 0x7e, 0x02, 0x82, 0x02, 0x85, 0x01, 0x88, + 0x01, 0x89, 0x01, 0x8b, 0x00, 0x8c, 0x00, 0x8d, 0x3d, 0x14, 0x2c, 0x20, 0x20, 0x2a, 0x18, 0x34, + 0x12, 0x3e, 0x0e, 0x45, 0x0a, 0x4c, 0x07, 0x53, 0x06, 0x58, 0x05, 0x5e, 0x04, 0x63, 0x02, 0x67, + 0x02, 0x6c, 0x01, 0x6f, 0x01, 0x72, 0x00, 0x76, 0x00, 0x78, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1e, 0x66, 0x16, 0x6b, 0x10, 0x71, 0x0c, 0x76, + 0x09, 0x7a, 0x07, 0x7e, 0x05, 0x81, 0x03, 0x85, 0x03, 0x88, 0x02, 0x8b, 0x02, 0x8d, 0x01, 0x8f, + 0x01, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, + 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, + 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1f, 0x00, + 0x12, 0x00, 0x09, 0x03, 0x04, 0x09, 0x00, 0x10, 0x00, 0x1c, 0x00, 0x27, 0x00, 0x31, 0x00, 0x3a, + 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, 0x00, + 0x07, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, + 0x37, 0x46, 0x29, 0x4c, 0x20, 0x52, 0x19, 0x57, 0x14, 0x5d, 0x10, 0x62, 0x0d, 0x66, 0x0b, 0x69, + 0x09, 0x6e, 0x07, 0x73, 0x06, 0x75, 0x05, 0x77, 0x04, 0x7a, 0x03, 0x7c, 0x02, 0x7e, 0x02, 0x82, + 0x02, 0x84, 0x02, 0x87, 0x01, 0x88, 0x01, 0x89, 0x3f, 0x13, 0x2f, 0x1c, 0x23, 0x27, 0x1b, 0x2f, + 0x15, 0x38, 0x10, 0x3e, 0x0d, 0x45, 0x0a, 0x4b, 0x08, 0x51, 0x07, 0x57, 0x05, 0x5c, 0x04, 0x60, + 0x03, 0x65, 0x02, 0x67, 0x02, 0x6c, 0x01, 0x6f, 0x01, 0x71, 0x00, 0x75, 0x00, 0x77, 0x00, 0x79, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1f, 0x65, 0x17, 0x6a, 0x11, 0x6f, 0x0d, 0x73, + 0x0a, 0x77, 0x08, 0x7b, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x87, 0x02, 0x89, 0x02, 0x8b, + 0x01, 0x8e, 0x01, 0x8f, 0x01, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, 0x5f, 0x00, 0x4e, + 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x26, 0x00, + 0x1a, 0x00, 0x0f, 0x00, 0x08, 0x04, 0x04, 0x0a, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x25, 0x00, 0x2f, + 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, 0x00, + 0x10, 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, + 0x38, 0x45, 0x2b, 0x4a, 0x22, 0x50, 0x1c, 0x55, 0x16, 0x59, 0x12, 0x5f, 0x0f, 0x63, 0x0d, 0x66, + 0x0b, 0x69, 0x09, 0x6e, 0x07, 0x71, 0x06, 0x74, 0x05, 0x76, 0x05, 0x78, 0x03, 0x7a, 0x03, 0x7c, + 0x02, 0x7e, 0x02, 0x81, 0x02, 0x84, 0x02, 0x86, 0x40, 0x11, 0x31, 0x1b, 0x26, 0x23, 0x1e, 0x2b, + 0x18, 0x33, 0x13, 0x39, 0x0f, 0x40, 0x0c, 0x45, 0x0a, 0x4b, 0x08, 0x51, 0x07, 0x56, 0x05, 0x59, + 0x04, 0x5e, 0x04, 0x61, 0x02, 0x65, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6e, 0x01, 0x71, 0x00, 0x73, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x20, 0x64, 0x18, 0x69, 0x13, 0x6d, 0x0f, 0x71, + 0x0c, 0x75, 0x09, 0x78, 0x07, 0x7b, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x86, 0x02, 0x88, + 0x02, 0x8a, 0x02, 0x8c, 0x01, 0x8e, 0x01, 0x8f, 0x01, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, + 0x00, 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x38, 0x00, 0x2d, 0x00, + 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x07, 0x05, 0x03, 0x0a, 0x00, 0x0f, 0x00, 0x1a, 0x00, 0x24, + 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, + 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, + 0x38, 0x45, 0x2d, 0x49, 0x24, 0x4e, 0x1e, 0x53, 0x19, 0x56, 0x14, 0x5b, 0x11, 0x60, 0x0e, 0x63, + 0x0c, 0x66, 0x0a, 0x69, 0x09, 0x6c, 0x07, 0x70, 0x06, 0x73, 0x06, 0x75, 0x05, 0x77, 0x04, 0x79, + 0x03, 0x7a, 0x03, 0x7c, 0x02, 0x7e, 0x02, 0x80, 0x40, 0x11, 0x32, 0x18, 0x28, 0x20, 0x20, 0x28, + 0x1a, 0x2f, 0x16, 0x35, 0x12, 0x3c, 0x0f, 0x41, 0x0c, 0x46, 0x0a, 0x4b, 0x08, 0x50, 0x07, 0x54, + 0x05, 0x58, 0x05, 0x5c, 0x04, 0x5f, 0x04, 0x62, 0x02, 0x66, 0x02, 0x69, 0x01, 0x6b, 0x01, 0x6e, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x20, 0x64, 0x19, 0x68, 0x14, 0x6b, 0x10, 0x6f, + 0x0d, 0x73, 0x0b, 0x76, 0x09, 0x79, 0x07, 0x7c, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, + 0x02, 0x88, 0x02, 0x89, 0x02, 0x8b, 0x02, 0x8d, 0x01, 0x8e, 0x01, 0x90, 0x00, 0x91, 0x00, 0x92, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, + 0x00, 0x50, 0x00, 0x41, 0x00, 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x32, 0x00, + 0x28, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x0b, 0x00, 0x07, 0x06, 0x03, 0x0b, 0x00, 0x0f, 0x00, 0x19, + 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, 0x00, + 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, + 0x39, 0x44, 0x2e, 0x49, 0x26, 0x4c, 0x1f, 0x52, 0x1a, 0x55, 0x16, 0x58, 0x13, 0x5d, 0x10, 0x61, + 0x0e, 0x64, 0x0c, 0x66, 0x0a, 0x68, 0x09, 0x6c, 0x07, 0x70, 0x07, 0x72, 0x06, 0x74, 0x05, 0x76, + 0x05, 0x78, 0x03, 0x79, 0x03, 0x7b, 0x02, 0x7c, 0x41, 0x10, 0x34, 0x17, 0x2a, 0x1e, 0x23, 0x25, + 0x1d, 0x2c, 0x18, 0x32, 0x14, 0x37, 0x10, 0x3d, 0x0e, 0x42, 0x0c, 0x46, 0x0a, 0x4b, 0x08, 0x4f, + 0x07, 0x53, 0x05, 0x57, 0x05, 0x5a, 0x04, 0x5e, 0x04, 0x61, 0x03, 0x63, 0x02, 0x66, 0x02, 0x69, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x20, 0x63, 0x1a, 0x67, 0x15, 0x6a, 0x11, 0x6e, + 0x0e, 0x71, 0x0c, 0x74, 0x0a, 0x77, 0x08, 0x7a, 0x07, 0x7c, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, + 0x03, 0x85, 0x02, 0x87, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x90, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x66, + 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x48, 0x00, 0x40, 0x00, 0x37, 0x00, + 0x2d, 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, 0x0a, 0x01, 0x06, 0x06, 0x03, 0x0b, 0x00, 0x0f, + 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, 0x00, + 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, + 0x39, 0x43, 0x2f, 0x48, 0x27, 0x4b, 0x22, 0x4f, 0x1c, 0x54, 0x18, 0x56, 0x14, 0x59, 0x12, 0x5e, + 0x0f, 0x62, 0x0d, 0x64, 0x0c, 0x66, 0x0a, 0x68, 0x09, 0x6b, 0x08, 0x6f, 0x07, 0x72, 0x06, 0x74, + 0x05, 0x75, 0x05, 0x77, 0x04, 0x78, 0x03, 0x7a, 0x42, 0x0f, 0x36, 0x16, 0x2c, 0x1c, 0x25, 0x23, + 0x1f, 0x29, 0x1a, 0x2e, 0x16, 0x34, 0x12, 0x39, 0x0f, 0x3e, 0x0d, 0x42, 0x0c, 0x47, 0x0a, 0x4b, + 0x08, 0x4f, 0x07, 0x52, 0x05, 0x56, 0x05, 0x59, 0x04, 0x5c, 0x04, 0x5f, 0x04, 0x62, 0x02, 0x64, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1b, 0x66, 0x16, 0x6a, 0x12, 0x6d, + 0x0f, 0x70, 0x0d, 0x73, 0x0b, 0x75, 0x09, 0x78, 0x07, 0x7a, 0x06, 0x7d, 0x06, 0x7f, 0x05, 0x81, + 0x04, 0x83, 0x03, 0x85, 0x02, 0x86, 0x02, 0x88, 0x02, 0x89, 0x02, 0x8b, 0x02, 0x8c, 0x01, 0x8d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x43, 0x31, 0x48, 0x29, 0x4a, 0x23, 0x4d, 0x1e, 0x53, 0x1a, 0x55, 0x16, 0x57, 0x13, 0x5b, + 0x11, 0x60, 0x0f, 0x62, 0x0d, 0x64, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x6a, 0x08, 0x6e, 0x07, 0x71, + 0x06, 0x73, 0x06, 0x75, 0x05, 0x76, 0x05, 0x78, 0x43, 0x0f, 0x37, 0x15, 0x2e, 0x1b, 0x27, 0x21, + 0x21, 0x26, 0x1c, 0x2c, 0x18, 0x31, 0x15, 0x36, 0x12, 0x3a, 0x0f, 0x3f, 0x0c, 0x43, 0x0c, 0x47, + 0x0a, 0x4b, 0x08, 0x4e, 0x07, 0x52, 0x06, 0x55, 0x05, 0x58, 0x05, 0x5b, 0x04, 0x5d, 0x04, 0x60, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1b, 0x66, 0x17, 0x69, 0x13, 0x6c, + 0x10, 0x6e, 0x0e, 0x71, 0x0c, 0x74, 0x0a, 0x76, 0x09, 0x79, 0x07, 0x7b, 0x06, 0x7d, 0x06, 0x7f, + 0x05, 0x81, 0x04, 0x82, 0x03, 0x84, 0x03, 0x86, 0x02, 0x87, 0x02, 0x89, 0x02, 0x8a, 0x02, 0x8b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x42, 0x31, 0x47, 0x2a, 0x49, 0x24, 0x4c, 0x1f, 0x51, 0x1b, 0x54, 0x18, 0x56, 0x15, 0x58, + 0x12, 0x5c, 0x10, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x6a, 0x08, 0x6d, + 0x07, 0x71, 0x06, 0x72, 0x06, 0x74, 0x05, 0x75, 0x43, 0x0f, 0x38, 0x14, 0x2f, 0x19, 0x28, 0x1f, + 0x22, 0x24, 0x1d, 0x29, 0x19, 0x2e, 0x16, 0x33, 0x12, 0x37, 0x11, 0x3b, 0x0f, 0x40, 0x0c, 0x43, + 0x0b, 0x47, 0x0a, 0x4b, 0x09, 0x4e, 0x07, 0x51, 0x07, 0x55, 0x05, 0x56, 0x05, 0x5a, 0x04, 0x5c, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1c, 0x65, 0x17, 0x68, 0x14, 0x6b, + 0x11, 0x6e, 0x0e, 0x70, 0x0c, 0x73, 0x0b, 0x75, 0x09, 0x77, 0x08, 0x79, 0x07, 0x7b, 0x06, 0x7d, + 0x05, 0x7f, 0x05, 0x81, 0x04, 0x82, 0x03, 0x84, 0x03, 0x86, 0x02, 0x87, 0x02, 0x88, 0x02, 0x89, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x42, 0x32, 0x46, 0x2b, 0x49, 0x25, 0x4b, 0x21, 0x4f, 0x1d, 0x53, 0x19, 0x55, 0x16, 0x57, + 0x14, 0x59, 0x11, 0x5e, 0x10, 0x61, 0x0e, 0x63, 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x6a, + 0x08, 0x6c, 0x07, 0x70, 0x07, 0x72, 0x06, 0x73, 0x43, 0x0e, 0x39, 0x13, 0x31, 0x18, 0x2a, 0x1d, + 0x24, 0x22, 0x1f, 0x27, 0x1b, 0x2b, 0x18, 0x30, 0x16, 0x35, 0x12, 0x39, 0x0f, 0x3c, 0x0f, 0x41, + 0x0c, 0x44, 0x0b, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x50, 0x07, 0x54, 0x05, 0x55, 0x05, 0x59, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1c, 0x65, 0x18, 0x68, 0x15, 0x6a, + 0x12, 0x6c, 0x0f, 0x6f, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x76, 0x09, 0x78, 0x07, 0x79, 0x07, 0x7c, + 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, 0x04, 0x82, 0x03, 0x84, 0x03, 0x85, 0x02, 0x86, 0x02, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x42, 0x33, 0x45, 0x2c, 0x49, 0x27, 0x4b, 0x22, 0x4d, 0x1e, 0x52, 0x1b, 0x54, 0x18, 0x56, + 0x15, 0x58, 0x13, 0x5b, 0x10, 0x5f, 0x0f, 0x61, 0x0e, 0x63, 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, + 0x09, 0x69, 0x08, 0x6c, 0x07, 0x6f, 0x07, 0x72, 0x44, 0x0e, 0x3a, 0x12, 0x32, 0x17, 0x2b, 0x1c, + 0x26, 0x21, 0x21, 0x26, 0x1d, 0x2a, 0x19, 0x2e, 0x16, 0x32, 0x14, 0x35, 0x12, 0x3a, 0x0f, 0x3d, + 0x0e, 0x41, 0x0c, 0x44, 0x0b, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x4f, 0x07, 0x53, 0x05, 0x55, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1d, 0x65, 0x19, 0x67, 0x15, 0x6a, + 0x13, 0x6c, 0x10, 0x6e, 0x0e, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x0a, 0x76, 0x09, 0x79, 0x07, 0x7a, + 0x07, 0x7c, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, 0x04, 0x82, 0x03, 0x83, 0x03, 0x85, 0x02, 0x86, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x42, 0x33, 0x44, 0x2d, 0x48, 0x28, 0x4a, 0x23, 0x4c, 0x1f, 0x50, 0x1c, 0x53, 0x19, 0x55, + 0x16, 0x57, 0x14, 0x58, 0x12, 0x5c, 0x10, 0x60, 0x0e, 0x62, 0x0d, 0x63, 0x0c, 0x65, 0x0b, 0x66, + 0x0a, 0x68, 0x09, 0x69, 0x09, 0x6b, 0x07, 0x6e, 0x44, 0x0d, 0x3a, 0x12, 0x33, 0x16, 0x2c, 0x1b, + 0x27, 0x1f, 0x21, 0x24, 0x1e, 0x28, 0x1a, 0x2b, 0x18, 0x30, 0x16, 0x34, 0x12, 0x37, 0x11, 0x3b, + 0x0f, 0x3e, 0x0d, 0x41, 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x4f, 0x07, 0x53, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1d, 0x65, 0x19, 0x66, 0x16, 0x69, + 0x13, 0x6b, 0x10, 0x6d, 0x0f, 0x6f, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x76, 0x09, 0x77, 0x08, 0x79, + 0x07, 0x7a, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, 0x04, 0x82, 0x03, 0x83, 0x03, 0x85, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x42, 0x34, 0x44, 0x2e, 0x48, 0x29, 0x49, 0x24, 0x4b, 0x20, 0x4f, 0x1d, 0x53, 0x1a, 0x55, + 0x18, 0x56, 0x15, 0x58, 0x13, 0x59, 0x11, 0x5d, 0x10, 0x61, 0x0e, 0x62, 0x0d, 0x63, 0x0c, 0x65, + 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x09, 0x6b, 0x44, 0x0d, 0x3b, 0x12, 0x34, 0x16, 0x2e, 0x19, + 0x28, 0x1e, 0x23, 0x22, 0x20, 0x26, 0x1d, 0x2b, 0x19, 0x2e, 0x16, 0x31, 0x14, 0x35, 0x12, 0x38, + 0x0f, 0x3b, 0x0f, 0x3f, 0x0c, 0x41, 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x4e, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1d, 0x64, 0x1a, 0x66, 0x17, 0x68, + 0x14, 0x6a, 0x11, 0x6c, 0x10, 0x6e, 0x0e, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x0a, 0x76, 0x09, 0x78, + 0x07, 0x79, 0x07, 0x7b, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, 0x04, 0x82, 0x03, 0x82, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x42, 0x34, 0x43, 0x2f, 0x48, 0x29, 0x49, 0x25, 0x4b, 0x21, 0x4d, 0x1e, 0x51, 0x1b, 0x54, + 0x18, 0x55, 0x16, 0x56, 0x14, 0x58, 0x13, 0x5b, 0x10, 0x5e, 0x0f, 0x61, 0x0e, 0x63, 0x0c, 0x64, + 0x0c, 0x66, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x44, 0x0c, 0x3c, 0x11, 0x35, 0x16, 0x2f, 0x19, + 0x2a, 0x1d, 0x25, 0x22, 0x21, 0x25, 0x1d, 0x29, 0x1a, 0x2b, 0x18, 0x30, 0x16, 0x33, 0x12, 0x35, + 0x12, 0x3a, 0x0f, 0x3c, 0x0f, 0x40, 0x0c, 0x41, 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1e, 0x64, 0x1a, 0x66, 0x17, 0x68, + 0x15, 0x6a, 0x12, 0x6c, 0x10, 0x6e, 0x0e, 0x70, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x75, 0x09, 0x76, + 0x09, 0x78, 0x07, 0x79, 0x07, 0x7b, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, 0x04, 0x82, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x42, 0x35, 0x43, 0x2f, 0x47, 0x2b, 0x49, 0x26, 0x4b, 0x22, 0x4c, 0x1f, 0x4f, 0x1c, 0x53, + 0x1a, 0x55, 0x18, 0x56, 0x15, 0x58, 0x13, 0x59, 0x12, 0x5c, 0x10, 0x60, 0x0f, 0x61, 0x0e, 0x63, + 0x0c, 0x64, 0x0c, 0x66, 0x0b, 0x66, 0x0a, 0x68, 0x44, 0x0c, 0x3c, 0x10, 0x35, 0x15, 0x30, 0x19, + 0x2b, 0x1c, 0x26, 0x20, 0x21, 0x23, 0x1e, 0x26, 0x1c, 0x2b, 0x19, 0x2e, 0x16, 0x30, 0x15, 0x35, + 0x12, 0x37, 0x11, 0x3b, 0x0f, 0x3d, 0x0f, 0x41, 0x0c, 0x42, 0x0c, 0x46, 0x0a, 0x47, 0x0a, 0x4a, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1e, 0x64, 0x1a, 0x66, 0x18, 0x68, + 0x15, 0x6a, 0x13, 0x6b, 0x10, 0x6d, 0x0f, 0x6e, 0x0e, 0x71, 0x0c, 0x72, 0x0b, 0x74, 0x0a, 0x76, + 0x09, 0x77, 0x08, 0x79, 0x07, 0x7a, 0x07, 0x7c, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x09, 0x43, 0x09, 0x44, 0x09, 0x45, 0x09, 0x45, 0x09, 0x46, 0x09, 0x46, 0x09, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0x22, 0x1c, 0x0a, 0x29, 0x0a, 0x31, 0x09, 0x36, 0x09, 0x3a, 0x09, 0x3b, 0x09, 0x3d, 0x09, + 0x3f, 0x09, 0x40, 0x09, 0x40, 0x09, 0x41, 0x09, 0x42, 0x09, 0x43, 0x09, 0x43, 0x0a, 0x43, 0x0a, + 0x44, 0x0a, 0x44, 0x0a, 0x44, 0x0a, 0x44, 0x0a, 0x23, 0x16, 0x32, 0x0a, 0x38, 0x0a, 0x3c, 0x0a, + 0x3f, 0x09, 0x40, 0x09, 0x41, 0x09, 0x42, 0x09, 0x43, 0x09, 0x44, 0x09, 0x44, 0x0a, 0x44, 0x0a, + 0x44, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x46, 0x0a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x16, 0x3e, 0x11, 0x3f, 0x0f, 0x40, 0x0d, 0x41, 0x0d, 0x41, 0x0c, 0x42, 0x0c, 0x43, 0x0b, + 0x43, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x44, 0x0a, 0x44, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, + 0x46, 0x0a, 0x46, 0x0a, 0x46, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0x6e, 0x03, 0x3a, 0x0f, 0x27, 0x18, 0x1e, 0x1f, 0x19, 0x24, 0x17, 0x28, 0x14, 0x2c, 0x13, + 0x2f, 0x11, 0x31, 0x11, 0x32, 0x10, 0x34, 0x0f, 0x36, 0x0f, 0x37, 0x0f, 0x38, 0x0e, 0x39, 0x0e, + 0x3a, 0x0d, 0x3a, 0x0d, 0x3b, 0x0c, 0x3c, 0x0c, 0x23, 0x3c, 0x25, 0x22, 0x2b, 0x18, 0x30, 0x14, + 0x33, 0x11, 0x36, 0x10, 0x38, 0x0f, 0x39, 0x0e, 0x3b, 0x0e, 0x3c, 0x0d, 0x3d, 0x0d, 0x3e, 0x0c, + 0x3f, 0x0c, 0x3f, 0x0c, 0x40, 0x0c, 0x40, 0x0c, 0x40, 0x0b, 0x41, 0x0b, 0x41, 0x0b, 0x42, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x1f, 0x3c, 0x18, 0x3c, 0x14, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x0f, 0x3f, 0x0e, 0x40, 0x0e, + 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0c, 0x42, 0x0c, 0x42, 0x0b, 0x42, 0x0b, + 0x43, 0x0b, 0x43, 0x0b, 0x44, 0x0b, 0x44, 0x0b, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0x8a, 0x00, 0x59, 0x06, 0x40, 0x0d, 0x32, 0x12, 0x2a, 0x18, 0x24, 0x1c, 0x20, 0x20, 0x1c, + 0x23, 0x1b, 0x26, 0x18, 0x28, 0x17, 0x2a, 0x16, 0x2c, 0x15, 0x2e, 0x14, 0x2f, 0x13, 0x31, 0x12, + 0x32, 0x12, 0x33, 0x12, 0x34, 0x11, 0x35, 0x10, 0x23, 0x4a, 0x24, 0x31, 0x27, 0x25, 0x2a, 0x1e, + 0x2d, 0x1a, 0x2f, 0x17, 0x32, 0x15, 0x33, 0x13, 0x35, 0x12, 0x36, 0x11, 0x38, 0x10, 0x39, 0x10, + 0x3a, 0x0f, 0x3a, 0x0f, 0x3b, 0x0e, 0x3c, 0x0e, 0x3c, 0x0e, 0x3d, 0x0e, 0x3e, 0x0d, 0x3e, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x25, 0x3c, 0x1d, 0x3c, 0x19, 0x3c, 0x16, 0x3d, 0x14, 0x3d, 0x12, 0x3f, 0x11, 0x3f, 0x10, + 0x3f, 0x0f, 0x3f, 0x0f, 0x40, 0x0e, 0x41, 0x0e, 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, + 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0c, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0x97, 0x00, 0x6c, 0x02, 0x52, 0x07, 0x43, 0x0b, 0x38, 0x10, 0x30, 0x14, 0x2a, 0x18, 0x27, + 0x1b, 0x23, 0x1e, 0x20, 0x20, 0x1e, 0x23, 0x1c, 0x25, 0x1b, 0x27, 0x19, 0x28, 0x18, 0x2a, 0x17, + 0x2b, 0x16, 0x2c, 0x16, 0x2e, 0x16, 0x2f, 0x15, 0x23, 0x50, 0x23, 0x3b, 0x25, 0x2e, 0x27, 0x26, + 0x29, 0x21, 0x2c, 0x1d, 0x2e, 0x1a, 0x2f, 0x18, 0x31, 0x16, 0x33, 0x15, 0x34, 0x14, 0x35, 0x13, + 0x36, 0x12, 0x37, 0x11, 0x38, 0x11, 0x38, 0x11, 0x39, 0x10, 0x3a, 0x10, 0x3a, 0x10, 0x3b, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x29, 0x3c, 0x22, 0x3b, 0x1d, 0x3b, 0x19, 0x3c, 0x17, 0x3c, 0x15, 0x3c, 0x13, 0x3d, 0x12, + 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x10, 0x3e, 0x0f, 0x3f, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x41, 0x0e, + 0x42, 0x0e, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0x9e, 0x00, 0x79, 0x00, 0x61, 0x04, 0x50, 0x07, 0x44, 0x0b, 0x3b, 0x0f, 0x34, 0x12, 0x2f, + 0x15, 0x2b, 0x18, 0x28, 0x1a, 0x25, 0x1d, 0x23, 0x1f, 0x21, 0x21, 0x1f, 0x22, 0x1d, 0x24, 0x1c, + 0x26, 0x1b, 0x27, 0x19, 0x28, 0x19, 0x2a, 0x19, 0x23, 0x54, 0x23, 0x42, 0x24, 0x35, 0x25, 0x2d, + 0x27, 0x27, 0x29, 0x22, 0x2b, 0x1f, 0x2d, 0x1c, 0x2e, 0x1a, 0x30, 0x19, 0x31, 0x17, 0x32, 0x16, + 0x33, 0x15, 0x34, 0x14, 0x35, 0x13, 0x36, 0x13, 0x37, 0x12, 0x37, 0x11, 0x38, 0x11, 0x38, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x2c, 0x3c, 0x25, 0x3b, 0x20, 0x3b, 0x1d, 0x3b, 0x1a, 0x3c, 0x17, 0x3c, 0x16, 0x3c, 0x14, + 0x3c, 0x13, 0x3e, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x11, 0x3d, 0x10, 0x3e, 0x0f, 0x3f, 0x0f, + 0x40, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x41, 0x0e, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xa2, 0x00, 0x84, 0x00, 0x6c, 0x02, 0x5a, 0x05, 0x4e, 0x07, 0x45, 0x0b, 0x3e, 0x0e, 0x38, + 0x10, 0x33, 0x13, 0x2f, 0x16, 0x2c, 0x18, 0x29, 0x1a, 0x26, 0x1c, 0x24, 0x1d, 0x22, 0x1f, 0x21, + 0x21, 0x1f, 0x21, 0x1e, 0x23, 0x1d, 0x25, 0x1c, 0x23, 0x56, 0x23, 0x47, 0x23, 0x3b, 0x24, 0x32, + 0x26, 0x2c, 0x27, 0x27, 0x29, 0x24, 0x2a, 0x21, 0x2c, 0x1e, 0x2d, 0x1c, 0x2e, 0x1b, 0x30, 0x19, + 0x30, 0x18, 0x32, 0x17, 0x32, 0x16, 0x33, 0x15, 0x34, 0x14, 0x34, 0x14, 0x35, 0x13, 0x36, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x2e, 0x3c, 0x27, 0x3b, 0x23, 0x3b, 0x1f, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x18, 0x3c, 0x17, + 0x3b, 0x15, 0x3c, 0x14, 0x3c, 0x13, 0x3e, 0x13, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, + 0x3d, 0x10, 0x3e, 0x0f, 0x3f, 0x0f, 0x40, 0x0f, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xa6, 0x00, 0x8b, 0x00, 0x75, 0x01, 0x63, 0x02, 0x57, 0x05, 0x4d, 0x07, 0x45, 0x0a, 0x3e, + 0x0d, 0x39, 0x0f, 0x35, 0x12, 0x32, 0x14, 0x2e, 0x16, 0x2c, 0x18, 0x29, 0x19, 0x27, 0x1b, 0x26, + 0x1d, 0x24, 0x1e, 0x22, 0x20, 0x22, 0x21, 0x20, 0x23, 0x58, 0x23, 0x4a, 0x23, 0x3f, 0x24, 0x36, + 0x25, 0x30, 0x26, 0x2b, 0x27, 0x27, 0x29, 0x24, 0x2a, 0x22, 0x2b, 0x20, 0x2d, 0x1e, 0x2e, 0x1c, + 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x18, 0x31, 0x18, 0x32, 0x17, 0x32, 0x16, 0x33, 0x16, 0x34, 0x15, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x30, 0x3c, 0x2a, 0x3c, 0x25, 0x3a, 0x21, 0x3b, 0x1e, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x18, + 0x3c, 0x17, 0x3c, 0x16, 0x3b, 0x15, 0x3c, 0x14, 0x3d, 0x13, 0x3e, 0x13, 0x3f, 0x12, 0x3f, 0x11, + 0x3f, 0x11, 0x3e, 0x11, 0x3d, 0x10, 0x3e, 0x0f, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xa8, 0x00, 0x90, 0x00, 0x7c, 0x00, 0x6b, 0x01, 0x5f, 0x04, 0x55, 0x05, 0x4c, 0x07, 0x45, + 0x0a, 0x40, 0x0c, 0x3c, 0x0f, 0x37, 0x10, 0x34, 0x12, 0x31, 0x15, 0x2e, 0x16, 0x2b, 0x18, 0x2a, + 0x19, 0x28, 0x1a, 0x26, 0x1d, 0x25, 0x1d, 0x23, 0x23, 0x59, 0x23, 0x4d, 0x23, 0x43, 0x24, 0x3b, + 0x24, 0x34, 0x25, 0x2f, 0x26, 0x2b, 0x27, 0x28, 0x28, 0x25, 0x2a, 0x23, 0x2b, 0x21, 0x2c, 0x1f, + 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x19, 0x31, 0x18, 0x32, 0x17, 0x32, 0x16, + 0x17, 0x00, 0x2f, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, 0x00, + 0x14, 0x2c, 0x29, 0x01, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x62, 0x00, 0x62, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, 0x00, + 0x11, 0x31, 0x23, 0x03, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, + 0x3e, 0x32, 0x3d, 0x2c, 0x3c, 0x26, 0x3b, 0x23, 0x3b, 0x20, 0x3b, 0x1e, 0x3a, 0x1c, 0x3b, 0x1a, + 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x14, 0x3d, 0x13, 0x3e, 0x13, + 0x3f, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xaa, 0x00, 0x94, 0x00, 0x82, 0x00, 0x72, 0x01, 0x65, 0x02, 0x5b, 0x04, 0x53, 0x06, 0x4b, + 0x08, 0x45, 0x0a, 0x41, 0x0c, 0x3d, 0x0e, 0x39, 0x0f, 0x36, 0x12, 0x33, 0x12, 0x30, 0x16, 0x2e, + 0x16, 0x2b, 0x18, 0x2b, 0x19, 0x29, 0x1a, 0x26, 0x23, 0x5a, 0x23, 0x4f, 0x23, 0x46, 0x23, 0x3e, + 0x24, 0x37, 0x25, 0x32, 0x25, 0x2e, 0x26, 0x2b, 0x27, 0x28, 0x28, 0x25, 0x2a, 0x23, 0x2b, 0x21, + 0x2b, 0x20, 0x2d, 0x1e, 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x19, 0x30, 0x18, + 0x00, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x45, 0x00, 0x4f, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, + 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x00, 0x57, 0x04, 0x16, 0x27, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x53, 0x00, 0x56, 0x00, 0x59, 0x00, + 0x5a, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x3b, 0x00, 0x48, 0x00, 0x55, 0x00, 0x58, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, + 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x00, 0x5f, 0x00, 0x20, 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, + 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x3e, 0x33, 0x3d, 0x2d, 0x3c, 0x28, 0x3b, 0x25, 0x3a, 0x22, 0x3b, 0x1f, 0x3b, 0x1e, 0x3a, 0x1c, + 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x16, 0x3b, 0x15, 0x3c, 0x15, 0x3c, 0x13, + 0x3d, 0x13, 0x3f, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xaa, 0x00, 0x98, 0x00, 0x87, 0x00, 0x78, 0x00, 0x6b, 0x01, 0x61, 0x03, 0x58, 0x05, 0x51, + 0x07, 0x4b, 0x08, 0x46, 0x0a, 0x42, 0x0c, 0x3e, 0x0d, 0x3a, 0x0f, 0x37, 0x11, 0x35, 0x12, 0x32, + 0x14, 0x30, 0x16, 0x2e, 0x16, 0x2b, 0x18, 0x2b, 0x23, 0x5a, 0x23, 0x51, 0x23, 0x48, 0x23, 0x41, + 0x24, 0x3a, 0x24, 0x35, 0x25, 0x31, 0x26, 0x2e, 0x27, 0x2a, 0x27, 0x28, 0x28, 0x26, 0x2a, 0x24, + 0x2a, 0x22, 0x2b, 0x21, 0x2c, 0x1f, 0x2d, 0x1e, 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x22, 0x00, 0x36, 0x00, 0x42, 0x00, 0x4a, 0x00, 0x4f, 0x00, + 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5b, 0x00, + 0x00, 0x82, 0x00, 0x57, 0x01, 0x12, 0x17, 0x00, 0x2f, 0x00, 0x3e, 0x00, 0x47, 0x00, 0x4d, 0x00, + 0x51, 0x00, 0x53, 0x00, 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x48, 0x00, 0x1b, 0x00, 0x34, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4f, 0x00, + 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5b, 0x00, + 0x00, 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, 0x00, + 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, + 0x3e, 0x34, 0x3d, 0x2f, 0x3b, 0x2a, 0x3c, 0x26, 0x3a, 0x23, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1d, + 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x17, 0x3a, 0x15, 0x3c, 0x15, + 0x3c, 0x15, 0x3c, 0x13, 0x3e, 0x13, 0x3f, 0x13, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xac, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7c, 0x00, 0x71, 0x01, 0x67, 0x02, 0x5e, 0x04, 0x57, + 0x05, 0x51, 0x07, 0x4b, 0x08, 0x46, 0x0a, 0x42, 0x0c, 0x3f, 0x0c, 0x3b, 0x0f, 0x39, 0x0f, 0x35, + 0x12, 0x34, 0x12, 0x31, 0x14, 0x30, 0x16, 0x2e, 0x23, 0x5b, 0x23, 0x52, 0x23, 0x4a, 0x23, 0x43, + 0x23, 0x3d, 0x24, 0x38, 0x25, 0x34, 0x25, 0x30, 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, + 0x2a, 0x24, 0x2a, 0x22, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x1f, 0x2d, 0x1d, 0x2e, 0x1d, 0x2e, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x19, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, + 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x00, 0x91, 0x00, 0x78, 0x00, 0x43, 0x00, 0x10, 0x0e, 0x00, 0x23, 0x00, 0x31, 0x00, 0x3b, 0x00, + 0x42, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x55, 0x00, 0x34, 0x00, 0x0b, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, + 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x00, 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, 0x00, + 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, 0x00, + 0x3e, 0x35, 0x3e, 0x30, 0x3b, 0x2c, 0x3c, 0x28, 0x3b, 0x25, 0x3a, 0x22, 0x3b, 0x20, 0x3b, 0x1f, + 0x3a, 0x1d, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x17, 0x3a, 0x16, + 0x3b, 0x15, 0x3c, 0x15, 0x3c, 0x14, 0x3c, 0x13, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xad, 0x00, 0x9d, 0x00, 0x8e, 0x00, 0x81, 0x00, 0x76, 0x00, 0x6b, 0x01, 0x63, 0x02, 0x5c, + 0x04, 0x56, 0x05, 0x50, 0x07, 0x4b, 0x08, 0x47, 0x0a, 0x43, 0x0c, 0x40, 0x0c, 0x3c, 0x0f, 0x3a, + 0x0f, 0x37, 0x11, 0x35, 0x12, 0x33, 0x12, 0x30, 0x23, 0x5b, 0x23, 0x53, 0x23, 0x4c, 0x23, 0x45, + 0x23, 0x40, 0x24, 0x3b, 0x24, 0x36, 0x25, 0x33, 0x25, 0x30, 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, + 0x28, 0x26, 0x2a, 0x25, 0x2a, 0x23, 0x2b, 0x22, 0x2b, 0x20, 0x2c, 0x20, 0x2d, 0x1e, 0x2d, 0x1d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x24, 0x00, 0x2f, 0x00, + 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, + 0x00, 0x96, 0x00, 0x87, 0x00, 0x61, 0x00, 0x36, 0x00, 0x10, 0x0a, 0x02, 0x1a, 0x00, 0x27, 0x00, + 0x32, 0x00, 0x3a, 0x00, 0x40, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4b, 0x00, 0x4e, 0x00, 0x50, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x58, 0x00, 0x40, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x14, 0x00, 0x24, 0x00, 0x2f, 0x00, + 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, + 0x00, 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x10, 0x00, 0x1f, 0x00, + 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, 0x00, + 0x3e, 0x36, 0x3e, 0x31, 0x3b, 0x2c, 0x3c, 0x29, 0x3c, 0x26, 0x3a, 0x24, 0x3a, 0x21, 0x3b, 0x20, + 0x3b, 0x1f, 0x39, 0x1d, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x17, + 0x3a, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x15, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xad, 0x00, 0x9f, 0x00, 0x91, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6f, 0x01, 0x67, 0x02, 0x60, + 0x03, 0x59, 0x04, 0x54, 0x05, 0x4f, 0x07, 0x4b, 0x08, 0x47, 0x0a, 0x43, 0x0b, 0x41, 0x0c, 0x3d, + 0x0e, 0x3b, 0x0f, 0x38, 0x0f, 0x35, 0x12, 0x35, 0x23, 0x5b, 0x23, 0x54, 0x23, 0x4e, 0x23, 0x47, + 0x23, 0x42, 0x24, 0x3c, 0x24, 0x38, 0x25, 0x35, 0x25, 0x32, 0x26, 0x2f, 0x26, 0x2d, 0x27, 0x2a, + 0x28, 0x28, 0x28, 0x26, 0x29, 0x25, 0x2a, 0x23, 0x2b, 0x22, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, 0x00, + 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4c, 0x00, + 0x00, 0x99, 0x00, 0x8f, 0x00, 0x74, 0x00, 0x51, 0x00, 0x2e, 0x00, 0x10, 0x08, 0x04, 0x13, 0x00, + 0x20, 0x00, 0x2a, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x48, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x57, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, 0x00, + 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4c, 0x00, + 0x00, 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x08, 0x00, + 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, + 0x3e, 0x36, 0x3e, 0x31, 0x3b, 0x2d, 0x3b, 0x2a, 0x3c, 0x27, 0x3a, 0x25, 0x3a, 0x23, 0x3b, 0x21, + 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x17, + 0x3c, 0x17, 0x3b, 0x17, 0x3a, 0x16, 0x3b, 0x15, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xad, 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x74, 0x00, 0x6c, 0x01, 0x65, + 0x02, 0x5e, 0x04, 0x58, 0x05, 0x53, 0x05, 0x4f, 0x07, 0x4b, 0x08, 0x47, 0x0a, 0x44, 0x0b, 0x41, + 0x0c, 0x3e, 0x0d, 0x3b, 0x0f, 0x3a, 0x0f, 0x37, 0x23, 0x5c, 0x23, 0x55, 0x23, 0x4f, 0x23, 0x49, + 0x23, 0x44, 0x23, 0x3f, 0x24, 0x3b, 0x24, 0x37, 0x25, 0x34, 0x25, 0x31, 0x26, 0x2e, 0x26, 0x2c, + 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, 0x29, 0x25, 0x2a, 0x24, 0x2a, 0x22, 0x2b, 0x22, 0x2b, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, 0x00, + 0x00, 0x9b, 0x00, 0x94, 0x00, 0x7f, 0x00, 0x64, 0x00, 0x46, 0x00, 0x29, 0x00, 0x10, 0x07, 0x06, + 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, 0x00, + 0x00, 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, + 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, 0x00, + 0x3e, 0x37, 0x3e, 0x32, 0x3c, 0x2f, 0x3b, 0x2b, 0x3c, 0x28, 0x3b, 0x26, 0x3a, 0x24, 0x3a, 0x22, + 0x3b, 0x21, 0x3b, 0x1f, 0x3a, 0x1e, 0x39, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, + 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x17, 0x3a, 0x17, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xae, 0x00, 0xa2, 0x00, 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x77, 0x00, 0x6f, 0x01, 0x67, + 0x02, 0x61, 0x02, 0x5c, 0x04, 0x57, 0x05, 0x52, 0x05, 0x4e, 0x07, 0x4b, 0x09, 0x47, 0x0a, 0x44, + 0x0b, 0x41, 0x0c, 0x3f, 0x0c, 0x3c, 0x0f, 0x3b, 0x23, 0x5c, 0x23, 0x56, 0x23, 0x50, 0x23, 0x4a, + 0x23, 0x45, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x35, 0x25, 0x33, 0x25, 0x30, 0x26, 0x2e, + 0x26, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, 0x29, 0x25, 0x2a, 0x24, 0x2a, 0x23, 0x2b, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, + 0x00, 0x9c, 0x00, 0x96, 0x00, 0x87, 0x00, 0x71, 0x00, 0x57, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x10, + 0x06, 0x07, 0x0b, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x26, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, + 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, + 0x00, 0x0f, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, 0x00, + 0x3e, 0x37, 0x3e, 0x33, 0x3c, 0x2f, 0x3b, 0x2c, 0x3c, 0x29, 0x3c, 0x27, 0x39, 0x25, 0x3a, 0x23, + 0x3b, 0x21, 0x3b, 0x20, 0x3b, 0x1f, 0x3a, 0x1e, 0x39, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, + 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x3b, 0x17, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, 0x84, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x6c, + 0x01, 0x65, 0x02, 0x5f, 0x04, 0x5a, 0x04, 0x56, 0x05, 0x52, 0x06, 0x4e, 0x07, 0x4a, 0x09, 0x47, + 0x0a, 0x45, 0x0a, 0x41, 0x0c, 0x40, 0x0c, 0x3d, 0x23, 0x5c, 0x23, 0x56, 0x23, 0x51, 0x23, 0x4c, + 0x23, 0x47, 0x23, 0x42, 0x23, 0x3e, 0x24, 0x3b, 0x24, 0x37, 0x25, 0x34, 0x25, 0x32, 0x25, 0x30, + 0x26, 0x2e, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, 0x29, 0x25, 0x2a, 0x25, 0x2a, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, 0x00, + 0x00, 0x9d, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, 0x37, 0x00, 0x22, + 0x00, 0x10, 0x05, 0x08, 0x0a, 0x02, 0x12, 0x00, 0x1a, 0x00, 0x22, 0x00, 0x28, 0x00, 0x2d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, 0x00, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, + 0x00, 0x1f, 0x00, 0x11, 0x00, 0x04, 0x07, 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, + 0x3e, 0x38, 0x3e, 0x33, 0x3d, 0x30, 0x3b, 0x2d, 0x3c, 0x2a, 0x3c, 0x27, 0x3a, 0x26, 0x3a, 0x24, + 0x3a, 0x22, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1e, 0x3a, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, + 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x90, 0x00, 0x87, 0x00, 0x7e, 0x00, 0x76, 0x00, 0x6f, + 0x01, 0x68, 0x02, 0x62, 0x02, 0x5e, 0x04, 0x59, 0x04, 0x55, 0x05, 0x51, 0x07, 0x4e, 0x07, 0x4a, + 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x41, 0x0c, 0x41, 0x23, 0x5c, 0x23, 0x57, 0x23, 0x52, 0x23, 0x4d, + 0x23, 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x24, 0x36, 0x25, 0x34, 0x25, 0x31, + 0x26, 0x2f, 0x26, 0x2d, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, 0x28, 0x25, 0x2a, 0x25, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, 0x00, + 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x90, 0x00, 0x81, 0x00, 0x6f, 0x00, 0x5b, 0x00, 0x46, 0x00, 0x32, + 0x00, 0x20, 0x00, 0x10, 0x04, 0x09, 0x09, 0x03, 0x0f, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, 0x00, + 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, + 0x00, 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, 0x00, + 0x3e, 0x38, 0x3e, 0x34, 0x3e, 0x31, 0x3b, 0x2e, 0x3b, 0x2b, 0x3c, 0x29, 0x3c, 0x27, 0x39, 0x25, + 0x3a, 0x24, 0x3a, 0x21, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1d, 0x3a, 0x1c, 0x3b, 0x1c, + 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x19, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xaf, 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x91, 0x00, 0x88, 0x00, 0x80, 0x00, 0x78, 0x00, 0x71, + 0x00, 0x6b, 0x01, 0x66, 0x02, 0x61, 0x03, 0x5c, 0x04, 0x58, 0x05, 0x55, 0x05, 0x50, 0x07, 0x4e, + 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x42, 0x23, 0x5c, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4d, + 0x23, 0x49, 0x23, 0x45, 0x23, 0x41, 0x24, 0x3d, 0x24, 0x3b, 0x24, 0x38, 0x25, 0x35, 0x25, 0x33, + 0x25, 0x31, 0x26, 0x2f, 0x26, 0x2d, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, + 0x00, 0x9d, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x86, 0x00, 0x76, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, + 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x10, 0x04, 0x09, 0x08, 0x04, 0x0c, 0x00, 0x14, 0x00, 0x1a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x20, 0x00, + 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, + 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, + 0x00, 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, + 0x3e, 0x38, 0x3e, 0x35, 0x3e, 0x32, 0x3b, 0x2e, 0x3b, 0x2c, 0x3c, 0x2a, 0x3c, 0x27, 0x3a, 0x26, + 0x3a, 0x24, 0x3a, 0x23, 0x3b, 0x21, 0x3b, 0x20, 0x3b, 0x1f, 0x3a, 0x1f, 0x39, 0x1d, 0x3a, 0x1c, + 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x83, 0x00, 0x7c, 0x00, 0x75, + 0x00, 0x6e, 0x01, 0x69, 0x01, 0x63, 0x02, 0x5f, 0x04, 0x5b, 0x04, 0x56, 0x05, 0x54, 0x05, 0x4f, + 0x07, 0x4e, 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x46, 0x23, 0x5d, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4f, + 0x23, 0x4a, 0x23, 0x46, 0x23, 0x43, 0x23, 0x3f, 0x24, 0x3c, 0x24, 0x39, 0x24, 0x36, 0x25, 0x34, + 0x25, 0x32, 0x25, 0x30, 0x26, 0x2f, 0x26, 0x2d, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0x16, 0x00, 0x1c, 0x00, + 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x95, 0x00, 0x8a, 0x00, 0x7d, 0x00, 0x6d, 0x00, 0x5d, 0x00, 0x4c, + 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x10, 0x04, 0x0a, 0x07, 0x05, 0x0b, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, 0x00, + 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0x16, 0x00, 0x1c, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, + 0x00, 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, + 0x3e, 0x39, 0x3e, 0x35, 0x3e, 0x32, 0x3b, 0x2f, 0x3b, 0x2d, 0x3c, 0x2a, 0x3c, 0x28, 0x3b, 0x27, + 0x39, 0x24, 0x3a, 0x24, 0x3a, 0x22, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1f, 0x39, 0x1d, + 0x3b, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xb0, 0x00, 0xa7, 0x00, 0x9e, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x86, 0x00, 0x7e, 0x00, 0x77, + 0x00, 0x71, 0x00, 0x6b, 0x01, 0x66, 0x02, 0x62, 0x02, 0x5d, 0x04, 0x5a, 0x04, 0x55, 0x05, 0x53, + 0x05, 0x4f, 0x07, 0x4e, 0x07, 0x4a, 0x09, 0x47, 0x23, 0x5d, 0x23, 0x58, 0x23, 0x54, 0x23, 0x50, + 0x23, 0x4c, 0x23, 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, 0x3d, 0x24, 0x3a, 0x24, 0x38, 0x25, 0x36, + 0x25, 0x33, 0x25, 0x32, 0x25, 0x2f, 0x26, 0x2f, 0x26, 0x2c, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x81, 0x00, 0x74, 0x00, 0x65, 0x00, 0x56, + 0x00, 0x46, 0x00, 0x37, 0x00, 0x29, 0x00, 0x1c, 0x00, 0x10, 0x03, 0x0a, 0x07, 0x06, 0x0a, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, + 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, + 0x00, 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, + 0x32, 0x59, 0x2a, 0x5b, 0x28, 0x5c, 0x27, 0x5d, 0x27, 0x5d, 0x26, 0x5e, 0x26, 0x5e, 0x26, 0x5e, + 0x26, 0x5e, 0x26, 0x5e, 0x25, 0x5e, 0x25, 0x5e, 0x25, 0x5f, 0x25, 0x5f, 0x25, 0x5f, 0x25, 0x5f, + 0x25, 0x5f, 0x24, 0x5f, 0x24, 0x5f, 0x24, 0x60, 0x32, 0x3c, 0x25, 0x4a, 0x24, 0x50, 0x23, 0x54, + 0x23, 0x56, 0x23, 0x58, 0x23, 0x59, 0x23, 0x5a, 0x23, 0x5a, 0x23, 0x5b, 0x23, 0x5b, 0x23, 0x5b, + 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5d, 0x23, 0x5d, 0x23, 0x5d, + 0x00, 0x6c, 0x0e, 0x60, 0x14, 0x60, 0x18, 0x60, 0x1b, 0x60, 0x1d, 0x60, 0x1d, 0x60, 0x1e, 0x60, + 0x1f, 0x60, 0x20, 0x60, 0x20, 0x60, 0x20, 0x60, 0x21, 0x60, 0x21, 0x60, 0x21, 0x60, 0x21, 0x60, + 0x22, 0x60, 0x22, 0x60, 0x22, 0x60, 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x90, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x5e, + 0x00, 0x50, 0x00, 0x42, 0x00, 0x34, 0x00, 0x27, 0x00, 0x1b, 0x00, 0x0f, 0x03, 0x0b, 0x06, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x3d, 0x00, 0x34, 0x00, + 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, + 0x00, 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, + 0x38, 0x4f, 0x31, 0x53, 0x2e, 0x56, 0x2c, 0x57, 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5a, 0x28, 0x5b, + 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x38, 0x22, 0x2b, 0x31, 0x27, 0x3b, 0x25, 0x42, + 0x24, 0x47, 0x23, 0x4a, 0x23, 0x4d, 0x23, 0x4f, 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x54, + 0x23, 0x55, 0x23, 0x56, 0x23, 0x56, 0x23, 0x57, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, + 0x00, 0x92, 0x01, 0x79, 0x07, 0x6f, 0x0c, 0x6b, 0x0f, 0x68, 0x12, 0x67, 0x14, 0x66, 0x16, 0x65, + 0x17, 0x64, 0x18, 0x64, 0x19, 0x63, 0x1a, 0x63, 0x1b, 0x63, 0x1b, 0x63, 0x1c, 0x63, 0x1c, 0x62, + 0x1d, 0x62, 0x1d, 0x62, 0x1d, 0x62, 0x1e, 0x62, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, + 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3e, 0x00, 0x31, 0x00, 0x25, 0x00, 0x1a, 0x00, 0x0f, 0x03, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, 0x00, + 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, + 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, + 0x3a, 0x4b, 0x34, 0x4f, 0x31, 0x52, 0x2f, 0x54, 0x2d, 0x56, 0x2c, 0x57, 0x2b, 0x57, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5a, 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x3c, 0x18, 0x30, 0x25, 0x2a, 0x2e, 0x27, 0x35, + 0x25, 0x3b, 0x24, 0x3f, 0x24, 0x43, 0x24, 0x46, 0x23, 0x48, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4e, + 0x23, 0x4f, 0x23, 0x50, 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x53, 0x23, 0x54, 0x23, 0x54, + 0x00, 0xa1, 0x00, 0x88, 0x03, 0x7b, 0x06, 0x74, 0x09, 0x70, 0x0c, 0x6d, 0x0e, 0x6b, 0x10, 0x6a, + 0x11, 0x69, 0x13, 0x68, 0x14, 0x67, 0x15, 0x66, 0x16, 0x66, 0x17, 0x65, 0x17, 0x65, 0x18, 0x65, + 0x19, 0x65, 0x19, 0x64, 0x1a, 0x64, 0x1a, 0x64, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6b, + 0x00, 0x5f, 0x00, 0x53, 0x00, 0x46, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x00, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, + 0x35, 0x00, 0x2c, 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, + 0x00, 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, + 0x3b, 0x48, 0x36, 0x4c, 0x33, 0x4f, 0x31, 0x52, 0x2f, 0x53, 0x2e, 0x53, 0x2d, 0x55, 0x2c, 0x57, + 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x2a, 0x59, 0x29, 0x5a, + 0x29, 0x5b, 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x3f, 0x14, 0x33, 0x1e, 0x2d, 0x26, 0x29, 0x2d, + 0x27, 0x32, 0x26, 0x36, 0x25, 0x3b, 0x24, 0x3e, 0x24, 0x41, 0x24, 0x43, 0x23, 0x45, 0x23, 0x47, + 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4d, 0x23, 0x4d, 0x23, 0x4f, 0x23, 0x50, 0x23, 0x50, + 0x00, 0xa7, 0x00, 0x92, 0x01, 0x84, 0x03, 0x7d, 0x05, 0x77, 0x08, 0x73, 0x0a, 0x71, 0x0c, 0x6f, + 0x0d, 0x6d, 0x0f, 0x6b, 0x10, 0x6a, 0x11, 0x6a, 0x12, 0x69, 0x13, 0x68, 0x14, 0x68, 0x15, 0x67, + 0x15, 0x66, 0x16, 0x66, 0x17, 0x66, 0x17, 0x66, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x31, 0x23, 0x03, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x31, 0x00, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, + 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x47, 0x38, 0x4b, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x51, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x55, 0x2c, 0x56, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x2a, 0x59, 0x40, 0x11, 0x36, 0x1a, 0x2f, 0x21, 0x2c, 0x27, + 0x29, 0x2c, 0x27, 0x30, 0x26, 0x34, 0x25, 0x37, 0x25, 0x3a, 0x24, 0x3d, 0x24, 0x40, 0x24, 0x42, + 0x24, 0x44, 0x23, 0x45, 0x23, 0x47, 0x23, 0x48, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4c, + 0x00, 0xaa, 0x00, 0x98, 0x00, 0x8c, 0x02, 0x83, 0x03, 0x7d, 0x05, 0x79, 0x07, 0x76, 0x09, 0x73, + 0x0a, 0x71, 0x0c, 0x6f, 0x0d, 0x6e, 0x0e, 0x6d, 0x0f, 0x6c, 0x10, 0x6b, 0x11, 0x6a, 0x12, 0x6a, + 0x13, 0x69, 0x13, 0x68, 0x14, 0x68, 0x15, 0x68, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x20, 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, + 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x03, 0x00, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, + 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x46, 0x39, 0x4a, 0x36, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2d, 0x56, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x41, 0x10, 0x38, 0x17, 0x32, 0x1d, 0x2e, 0x22, + 0x2b, 0x27, 0x29, 0x2b, 0x27, 0x2f, 0x26, 0x32, 0x25, 0x35, 0x25, 0x38, 0x25, 0x3b, 0x24, 0x3c, + 0x24, 0x3f, 0x24, 0x40, 0x24, 0x42, 0x23, 0x44, 0x23, 0x45, 0x23, 0x46, 0x23, 0x48, 0x23, 0x48, + 0x00, 0xac, 0x00, 0x9d, 0x00, 0x91, 0x01, 0x89, 0x02, 0x82, 0x03, 0x7e, 0x05, 0x7a, 0x07, 0x77, + 0x08, 0x75, 0x09, 0x73, 0x0b, 0x71, 0x0c, 0x70, 0x0d, 0x6e, 0x0e, 0x6e, 0x0e, 0x6c, 0x0f, 0x6c, + 0x10, 0x6b, 0x10, 0x6a, 0x11, 0x6a, 0x12, 0x6a, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, 0x00, + 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, + 0x00, 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x45, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2d, 0x56, 0x2c, 0x57, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x42, 0x0f, 0x39, 0x15, 0x33, 0x1a, 0x2f, 0x1f, + 0x2d, 0x24, 0x2a, 0x27, 0x29, 0x2b, 0x27, 0x2e, 0x26, 0x31, 0x26, 0x34, 0x25, 0x36, 0x25, 0x38, + 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3e, 0x24, 0x40, 0x24, 0x41, 0x24, 0x43, 0x23, 0x44, 0x23, 0x45, + 0x00, 0xae, 0x00, 0xa1, 0x00, 0x96, 0x00, 0x8d, 0x01, 0x87, 0x02, 0x82, 0x03, 0x7e, 0x05, 0x7b, + 0x06, 0x78, 0x07, 0x76, 0x09, 0x74, 0x0a, 0x73, 0x0b, 0x71, 0x0c, 0x70, 0x0c, 0x6f, 0x0d, 0x6e, + 0x0e, 0x6d, 0x0f, 0x6c, 0x10, 0x6c, 0x10, 0x6b, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, 0x00, + 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x00, 0x00, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, + 0x00, 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x45, 0x3a, 0x47, 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x52, 0x2f, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x55, + 0x2c, 0x57, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x43, 0x0e, 0x3b, 0x13, 0x35, 0x18, 0x31, 0x1c, + 0x2e, 0x21, 0x2c, 0x24, 0x2a, 0x28, 0x28, 0x2b, 0x27, 0x2e, 0x27, 0x30, 0x26, 0x33, 0x25, 0x35, + 0x25, 0x37, 0x25, 0x39, 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x24, 0x40, 0x24, 0x42, + 0x00, 0xaf, 0x00, 0xa3, 0x00, 0x99, 0x00, 0x91, 0x00, 0x8b, 0x02, 0x86, 0x02, 0x81, 0x03, 0x7e, + 0x05, 0x7b, 0x06, 0x79, 0x07, 0x77, 0x08, 0x75, 0x09, 0x74, 0x0a, 0x73, 0x0b, 0x71, 0x0c, 0x71, + 0x0c, 0x6f, 0x0d, 0x6e, 0x0e, 0x6e, 0x0e, 0x6d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x10, 0x00, 0x1f, 0x00, + 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x00, 0x00, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, + 0x00, 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x55, 0x2c, 0x56, 0x2b, 0x57, 0x44, 0x0e, 0x3c, 0x12, 0x36, 0x16, 0x33, 0x1a, + 0x30, 0x1e, 0x2d, 0x22, 0x2b, 0x25, 0x2a, 0x28, 0x28, 0x2a, 0x27, 0x2d, 0x27, 0x30, 0x26, 0x32, + 0x26, 0x34, 0x25, 0x35, 0x25, 0x37, 0x25, 0x39, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, + 0x00, 0xb0, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x95, 0x00, 0x8e, 0x01, 0x89, 0x02, 0x85, 0x03, 0x81, + 0x04, 0x7e, 0x05, 0x7c, 0x06, 0x7a, 0x07, 0x78, 0x07, 0x76, 0x09, 0x75, 0x09, 0x73, 0x0b, 0x73, + 0x0b, 0x71, 0x0c, 0x71, 0x0c, 0x70, 0x0d, 0x6e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x08, 0x00, + 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x00, 0x00, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, + 0x00, 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x34, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, 0x44, 0x0d, 0x3d, 0x11, 0x38, 0x15, 0x34, 0x19, + 0x31, 0x1c, 0x2e, 0x20, 0x2d, 0x23, 0x2b, 0x25, 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2d, 0x27, 0x2f, + 0x26, 0x31, 0x26, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x38, 0x25, 0x39, 0x24, 0x3a, 0x24, 0x3c, + 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x98, 0x00, 0x91, 0x00, 0x8c, 0x01, 0x88, 0x02, 0x84, + 0x03, 0x81, 0x04, 0x7e, 0x05, 0x7c, 0x06, 0x7a, 0x06, 0x79, 0x07, 0x77, 0x08, 0x76, 0x09, 0x74, + 0x0a, 0x73, 0x0b, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, + 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x33, + 0x00, 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x44, 0x0d, 0x3e, 0x10, 0x39, 0x14, 0x35, 0x17, + 0x32, 0x1b, 0x30, 0x1e, 0x2e, 0x21, 0x2c, 0x23, 0x2b, 0x26, 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2d, + 0x27, 0x2e, 0x26, 0x30, 0x26, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x36, 0x25, 0x38, 0x25, 0x39, + 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x87, + 0x02, 0x84, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x06, 0x7b, 0x06, 0x79, 0x07, 0x78, 0x07, 0x76, + 0x09, 0x76, 0x09, 0x74, 0x0a, 0x73, 0x0b, 0x72, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, + 0x00, 0x0f, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, 0x00, 0x00, 0x0c, 0x00, 0x1f, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, 0x35, 0x4b, 0x35, 0x4e, + 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x52, 0x2f, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x44, 0x0c, 0x3f, 0x10, 0x3a, 0x13, 0x36, 0x16, + 0x33, 0x19, 0x30, 0x1c, 0x2e, 0x1f, 0x2d, 0x21, 0x2b, 0x24, 0x2a, 0x26, 0x2a, 0x28, 0x28, 0x2a, + 0x28, 0x2c, 0x27, 0x2e, 0x26, 0x30, 0x26, 0x31, 0x26, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x37, + 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa2, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x91, 0x00, 0x8d, 0x01, 0x89, + 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x06, 0x7b, 0x06, 0x79, 0x07, 0x79, + 0x07, 0x77, 0x08, 0x76, 0x09, 0x75, 0x09, 0x74, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, + 0x00, 0x1f, 0x00, 0x11, 0x00, 0x04, 0x07, 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, 0x03, 0x00, 0x00, 0x0f, + 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x46, 0x39, 0x46, 0x39, 0x47, 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, + 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x51, 0x2f, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x45, 0x0c, 0x3f, 0x0f, 0x3a, 0x12, 0x37, 0x15, + 0x34, 0x18, 0x32, 0x1b, 0x30, 0x1d, 0x2e, 0x20, 0x2d, 0x22, 0x2b, 0x24, 0x2a, 0x26, 0x2a, 0x28, + 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2e, 0x27, 0x2f, 0x26, 0x31, 0x26, 0x32, 0x25, 0x33, 0x25, 0x35, + 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x98, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8b, + 0x01, 0x88, 0x02, 0x85, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7a, + 0x07, 0x79, 0x07, 0x78, 0x07, 0x76, 0x09, 0x76, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, + 0x00, 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x45, 0x3a, 0x46, 0x39, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x51, + 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x45, 0x0c, 0x40, 0x0f, 0x3b, 0x11, 0x38, 0x14, + 0x35, 0x17, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, 0x2d, 0x21, 0x2c, 0x22, 0x2b, 0x25, 0x2a, 0x26, + 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2d, 0x27, 0x2f, 0x26, 0x30, 0x26, 0x32, 0x25, 0x33, + 0x00, 0xb2, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x91, 0x00, 0x8e, + 0x01, 0x8a, 0x02, 0x88, 0x02, 0x85, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, + 0x06, 0x7a, 0x06, 0x79, 0x07, 0x78, 0x07, 0x77, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, + 0x00, 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x00, + 0x00, 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x44, 0x3a, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x38, 0x13, + 0x36, 0x16, 0x33, 0x18, 0x31, 0x1b, 0x30, 0x1d, 0x2e, 0x1f, 0x2d, 0x21, 0x2b, 0x23, 0x2b, 0x25, + 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2d, 0x27, 0x2f, 0x26, 0x2f, 0x26, 0x31, + 0x00, 0xb3, 0x00, 0xac, 0x00, 0xa7, 0x00, 0xa1, 0x00, 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, + 0x01, 0x8c, 0x01, 0x89, 0x02, 0x87, 0x02, 0x85, 0x02, 0x82, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, + 0x05, 0x7c, 0x06, 0x7b, 0x06, 0x79, 0x07, 0x79, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, + 0x00, 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, 0x00, + 0x07, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x46, 0x39, 0x47, 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, 0x2f, 0x53, 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x39, 0x13, + 0x37, 0x15, 0x34, 0x18, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, 0x2d, 0x20, 0x2d, 0x22, 0x2b, 0x23, + 0x2b, 0x25, 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2d, 0x27, 0x2f, 0x26, 0x2f, + 0x00, 0xb3, 0x00, 0xad, 0x00, 0xa7, 0x00, 0xa3, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x95, 0x00, 0x91, + 0x00, 0x8e, 0x01, 0x8b, 0x02, 0x88, 0x02, 0x86, 0x02, 0x84, 0x03, 0x82, 0x03, 0x81, 0x04, 0x7f, + 0x05, 0x7e, 0x05, 0x7c, 0x06, 0x7b, 0x06, 0x7a, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, + 0x00, 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, 0x00, + 0x10, 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x52, 0x45, 0x0b, 0x41, 0x0e, 0x3d, 0x10, 0x3a, 0x12, + 0x37, 0x14, 0x34, 0x17, 0x32, 0x19, 0x31, 0x1b, 0x30, 0x1d, 0x2e, 0x1f, 0x2d, 0x20, 0x2c, 0x22, + 0x2b, 0x24, 0x2a, 0x25, 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2c, 0x27, 0x2e, + 0x00, 0xb3, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x93, + 0x00, 0x8f, 0x01, 0x8d, 0x01, 0x8a, 0x02, 0x88, 0x02, 0x86, 0x02, 0x84, 0x03, 0x82, 0x03, 0x81, + 0x04, 0x7f, 0x05, 0x7e, 0x05, 0x7c, 0x06, 0x7c, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, + 0x00, 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, + 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x47, 0x38, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x45, 0x0b, 0x41, 0x0e, 0x3e, 0x10, 0x3a, 0x11, + 0x38, 0x14, 0x35, 0x16, 0x33, 0x18, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2e, 0x20, 0x2d, 0x21, + 0x2b, 0x22, 0x2b, 0x24, 0x2a, 0x25, 0x2a, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2c, + 0x00, 0xb3, 0x00, 0xae, 0x00, 0xa9, 0x00, 0xa4, 0x00, 0xa0, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x94, + 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, 0x01, 0x89, 0x02, 0x87, 0x02, 0x86, 0x02, 0x84, 0x03, 0x82, + 0x03, 0x80, 0x04, 0x7f, 0x05, 0x7e, 0x05, 0x7c, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, + 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, 0x00, + 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x4a, 0x36, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x46, 0x0b, 0x42, 0x0d, 0x3e, 0x10, 0x3b, 0x11, + 0x38, 0x13, 0x36, 0x16, 0x34, 0x17, 0x32, 0x19, 0x30, 0x1b, 0x30, 0x1d, 0x2e, 0x1e, 0x2d, 0x20, + 0x2d, 0x22, 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x25, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2c, + 0x00, 0xb3, 0x00, 0xae, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x96, + 0x00, 0x93, 0x00, 0x90, 0x00, 0x8d, 0x01, 0x8b, 0x02, 0x89, 0x02, 0x87, 0x02, 0x85, 0x02, 0x83, + 0x03, 0x82, 0x03, 0x80, 0x04, 0x7f, 0x05, 0x7e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, + 0x00, 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, 0x00, + 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x45, 0x3a, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x34, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x46, 0x0b, 0x42, 0x0d, 0x3e, 0x0f, 0x3c, 0x11, + 0x39, 0x13, 0x37, 0x15, 0x34, 0x16, 0x33, 0x18, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2e, 0x20, + 0x2d, 0x20, 0x2c, 0x22, 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x26, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, + 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x9a, 0x00, 0x97, + 0x00, 0x94, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, 0x01, 0x8a, 0x02, 0x88, 0x02, 0x86, 0x02, 0x85, + 0x02, 0x83, 0x03, 0x82, 0x03, 0x80, 0x04, 0x7f, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6f, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x19, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb2, 0x00, 0x6f, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc8, 0x00, 0xa2, 0x00, 0x4f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd2, 0x00, 0xb9, 0x00, 0x7f, 0x00, 0x3c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd6, 0x00, 0xc6, 0x00, 0x9c, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd9, 0x00, 0xcd, 0x00, 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, 0x3d, 0x00, 0x1d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0xd7, 0x00, 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, + 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, + 0x30, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, + 0x00, 0x09, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, 0x78, 0x00, 0x5d, 0x00, + 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x13, 0x00, 0x0f, + 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, + 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, + 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, + 0x64, 0x00, 0x4e, 0x00, 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, + 0x00, 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x99, 0x00, 0x85, 0x00, + 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x14, + 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, + 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1c, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x24, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x04, 0x11, 0x09, 0x01, 0x11, 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x61, 0x01, 0x23, 0x03, 0x03, 0x09, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, + 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x2b, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x28, 0x00, 0x21, 0x00, 0x1d, 0x00, 0x1e, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x39, 0x01, 0x0f, 0x07, 0x02, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1b, + 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, + 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x04, 0x01, 0x00, 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, + 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, + 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, + 0x00, 0x34, 0x00, 0x27, 0x00, 0x0f, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1a, + 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0x87, 0x00, 0x32, 0x00, 0x19, 0x05, 0x0b, 0x09, 0x03, 0x0b, 0x00, 0x0e, 0x00, 0x12, + 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, + 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0a, + 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x12, 0x00, 0x15, + 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, + 0x00, 0x32, 0x00, 0x2b, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x12, 0x00, 0x15, + 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcd, 0x00, 0xaf, 0x00, 0x6e, 0x00, 0x31, 0x00, 0x1e, 0x04, 0x12, 0x07, 0x0a, 0x09, 0x04, 0x0a, + 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, + 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, + 0x00, 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x1b, + 0x00, 0x2c, 0x00, 0x28, 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, + 0x00, 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd4, 0x00, 0xc1, 0x00, 0x93, 0x00, 0x5f, 0x00, 0x30, 0x00, 0x22, 0x03, 0x17, 0x06, 0x0f, 0x07, + 0x0a, 0x09, 0x05, 0x0a, 0x02, 0x0b, 0x00, 0x0c, 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x15, + 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, 0x00, + 0x14, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, + 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, + 0x00, 0x24, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, + 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd8, 0x00, 0xcb, 0x00, 0xaa, 0x00, 0x7f, 0x00, 0x55, 0x00, 0x30, 0x00, 0x24, 0x02, 0x1b, 0x05, + 0x14, 0x06, 0x0e, 0x08, 0x09, 0x09, 0x06, 0x0a, 0x03, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0f, + 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, 0x00, + 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xda, 0x00, 0xd1, 0x00, 0xb8, 0x00, 0x96, 0x00, 0x71, 0x00, 0x4e, 0x00, 0x30, 0x00, 0x26, 0x02, + 0x1e, 0x04, 0x17, 0x06, 0x12, 0x07, 0x0d, 0x08, 0x09, 0x09, 0x06, 0x0a, 0x04, 0x0a, 0x01, 0x0b, + 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, 0x00, + 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0xa6, 0x00, 0x87, 0x00, 0x67, 0x00, 0x4a, 0x00, 0x30, 0x00, + 0x27, 0x02, 0x20, 0x03, 0x1a, 0x05, 0x14, 0x06, 0x10, 0x07, 0x0c, 0x08, 0x09, 0x09, 0x07, 0x0a, + 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, 0x00, + 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0xd7, 0x00, 0xc8, 0x00, 0xb2, 0x00, 0x97, 0x00, 0x7b, 0x00, 0x60, 0x00, 0x46, 0x00, + 0x30, 0x00, 0x28, 0x01, 0x21, 0x03, 0x1c, 0x04, 0x17, 0x06, 0x13, 0x07, 0x0f, 0x08, 0x0c, 0x08, + 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, 0x00, + 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xba, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x72, 0x00, 0x5a, 0x00, + 0x44, 0x00, 0x2f, 0x00, 0x29, 0x01, 0x23, 0x03, 0x1e, 0x04, 0x19, 0x05, 0x15, 0x06, 0x11, 0x07, + 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, 0x00, + 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, + 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xd9, 0x00, 0xd0, 0x00, 0xc0, 0x00, 0xad, 0x00, 0x98, 0x00, 0x81, 0x00, 0x6b, 0x00, + 0x56, 0x00, 0x42, 0x00, 0x2f, 0x00, 0x29, 0x01, 0x24, 0x02, 0x1f, 0x04, 0x1b, 0x05, 0x17, 0x06, + 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, 0x00, + 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, + 0x00, 0x09, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xc5, 0x00, 0xb5, 0x00, 0xa2, 0x00, 0x8e, 0x00, 0x79, 0x00, + 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x2a, 0x01, 0x25, 0x02, 0x20, 0x03, 0x1c, 0x04, + 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, 0x00, + 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x13, 0x00, 0x0f, + 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xaa, 0x00, 0x98, 0x00, 0x85, 0x00, + 0x72, 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x2f, 0x00, 0x2a, 0x01, 0x26, 0x02, 0x21, 0x03, + 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, 0x00, + 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, + 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8f, 0x00, + 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x3d, 0x00, 0x2f, 0x00, 0x2b, 0x01, 0x26, 0x02, + 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, 0x00, + 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, + 0x00, 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xce, 0x00, 0xc3, 0x00, 0xb6, 0x00, 0xa8, 0x00, 0x98, 0x00, + 0x88, 0x00, 0x78, 0x00, 0x68, 0x00, 0x59, 0x00, 0x4a, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x2b, 0x01, + 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, 0x00, + 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x14, + 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbb, 0x00, 0xae, 0x00, 0xa0, 0x00, + 0x91, 0x00, 0x82, 0x00, 0x73, 0x00, 0x64, 0x00, 0x56, 0x00, 0x48, 0x00, 0x3b, 0x00, 0x2f, 0x00, + 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, 0x00, + 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x6f, 0x00, 0xb2, 0x00, 0xc8, 0x00, 0xd2, 0x00, 0xd6, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, + 0x4c, 0x04, 0x87, 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd4, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdb, 0x00, + 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa6, 0x00, 0x76, 0x00, 0x9b, 0x00, 0xac, 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, + 0x61, 0x01, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x12, 0x00, 0x6f, 0x00, 0xa2, 0x00, 0xb9, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xd2, 0x00, + 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, + 0x11, 0x09, 0x39, 0x01, 0x87, 0x00, 0xaf, 0x00, 0xc1, 0x00, 0xcb, 0x00, 0xd1, 0x00, 0xd4, 0x00, + 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x76, 0x00, 0x5b, 0x00, 0x75, 0x00, 0x96, 0x00, 0xab, 0x00, 0xc0, 0x00, 0xcd, 0x00, 0xd2, 0x00, + 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, + 0x23, 0x03, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, 0x00, + 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xae, 0x00, 0xb9, 0x00, + 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, + 0x01, 0x11, 0x0f, 0x07, 0x32, 0x00, 0x6e, 0x00, 0x93, 0x00, 0xaa, 0x00, 0xb8, 0x00, 0xc1, 0x00, + 0xc8, 0x00, 0xcc, 0x00, 0xd0, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x75, 0x00, 0x2b, 0x00, 0x5b, 0x00, 0x7f, 0x00, 0x9b, 0x00, 0xae, 0x00, 0xb9, 0x00, + 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, + 0x03, 0x09, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, 0x00, + 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, + 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0xca, 0x00, 0xcd, 0x00, + 0x00, 0x17, 0x02, 0x0c, 0x19, 0x05, 0x31, 0x00, 0x5f, 0x00, 0x7f, 0x00, 0x96, 0x00, 0xa6, 0x00, + 0xb2, 0x00, 0xba, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x96, 0x00, 0x5b, 0x00, 0x13, 0x00, 0x41, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, + 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0xca, 0x00, 0xcd, 0x00, + 0x00, 0x13, 0x04, 0x01, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, 0x00, + 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, + 0x84, 0x00, 0x93, 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, + 0x00, 0x1b, 0x00, 0x12, 0x0b, 0x09, 0x1e, 0x04, 0x30, 0x00, 0x55, 0x00, 0x71, 0x00, 0x87, 0x00, + 0x97, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb5, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc7, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0xab, 0x00, 0x7f, 0x00, 0x41, 0x00, 0x03, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, + 0x84, 0x00, 0x93, 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, + 0x00, 0x18, 0x00, 0x0a, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, 0x00, + 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x27, 0x00, 0x47, 0x00, + 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, + 0x00, 0x1c, 0x00, 0x17, 0x03, 0x0b, 0x12, 0x07, 0x22, 0x03, 0x30, 0x00, 0x4e, 0x00, 0x67, 0x00, + 0x7b, 0x00, 0x8b, 0x00, 0x98, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, 0x00, 0xbb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcd, 0x00, 0xc0, 0x00, 0x9b, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x27, 0x00, 0x47, 0x00, + 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, + 0x00, 0x1a, 0x00, 0x11, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, 0x00, + 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x99, 0x00, 0xa0, 0x00, + 0x00, 0x1d, 0x00, 0x19, 0x00, 0x0e, 0x0a, 0x09, 0x17, 0x06, 0x24, 0x02, 0x30, 0x00, 0x4a, 0x00, + 0x60, 0x00, 0x72, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd9, 0x00, 0xcd, 0x00, 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x01, 0x00, 0x21, 0x00, + 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x99, 0x00, 0xa0, 0x00, + 0x00, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, 0x00, + 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x36, 0x00, 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, + 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x04, 0x0a, 0x0f, 0x07, 0x1b, 0x05, 0x26, 0x02, 0x30, 0x00, + 0x46, 0x00, 0x5a, 0x00, 0x6b, 0x00, 0x79, 0x00, 0x85, 0x00, 0x8f, 0x00, 0x98, 0x00, 0xa0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x01, 0x00, + 0x1d, 0x00, 0x36, 0x00, 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, + 0x00, 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, 0x00, + 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x15, 0x00, 0x0b, 0x0a, 0x09, 0x14, 0x06, 0x1e, 0x04, 0x27, 0x02, + 0x30, 0x00, 0x44, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x7e, 0x00, 0x88, 0x00, 0x91, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, 0x3d, 0x00, 0x1d, 0x00, + 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, + 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, 0x00, + 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, 0x5c, 0x00, 0x68, 0x00, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x17, 0x00, 0x0f, 0x05, 0x0a, 0x0e, 0x08, 0x17, 0x06, 0x20, 0x03, + 0x28, 0x01, 0x2f, 0x00, 0x42, 0x00, 0x52, 0x00, 0x60, 0x00, 0x6d, 0x00, 0x78, 0x00, 0x82, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0xd7, 0x00, 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, 0x5c, 0x00, 0x68, 0x00, + 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, 0x00, + 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x02, 0x0b, 0x09, 0x09, 0x12, 0x07, 0x1a, 0x05, + 0x21, 0x03, 0x29, 0x01, 0x2f, 0x00, 0x40, 0x00, 0x4f, 0x00, 0x5c, 0x00, 0x68, 0x00, 0x73, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, + 0x30, 0x00, 0x17, 0x00, 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, + 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x06, 0x0a, 0x0d, 0x08, 0x14, 0x06, + 0x1c, 0x04, 0x23, 0x03, 0x29, 0x01, 0x2f, 0x00, 0x3e, 0x00, 0x4c, 0x00, 0x59, 0x00, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, 0x78, 0x00, 0x5d, 0x00, + 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x0c, 0x00, 0x1b, 0x00, 0x29, 0x00, + 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x22, 0x00, 0x30, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x16, 0x00, 0x0f, 0x03, 0x0b, 0x09, 0x09, 0x10, 0x07, + 0x17, 0x06, 0x1e, 0x04, 0x24, 0x02, 0x2a, 0x01, 0x2f, 0x00, 0x3d, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, + 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x11, 0x00, 0x22, 0x00, 0x30, 0x00, + 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, 0x05, 0x06, 0x00, 0x13, 0x00, 0x21, 0x00, + 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x11, 0x00, 0x0b, 0x06, 0x0a, 0x0c, 0x08, + 0x13, 0x07, 0x19, 0x05, 0x1f, 0x04, 0x25, 0x02, 0x2a, 0x01, 0x2f, 0x00, 0x3c, 0x00, 0x48, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, + 0x64, 0x00, 0x4e, 0x00, 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x01, 0x00, 0x0d, 0x00, 0x19, 0x00, + 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x04, 0x0a, 0x09, 0x09, + 0x0f, 0x08, 0x15, 0x06, 0x1b, 0x05, 0x20, 0x03, 0x26, 0x02, 0x2b, 0x01, 0x2f, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x99, 0x00, 0x85, 0x00, + 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x08, 0x00, 0x13, 0x00, + 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x0f, 0x01, 0x0b, 0x07, 0x0a, + 0x0c, 0x08, 0x11, 0x07, 0x17, 0x06, 0x1c, 0x04, 0x21, 0x03, 0x26, 0x02, 0x2b, 0x01, 0x2f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, + 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, 0x03, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x01, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x01, 0x23, 0x03, 0x03, 0x09, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, + 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x03, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, 0x00, + 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x04, 0x01, 0x00, 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, + 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x09, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, 0x00, + 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0a, + 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x13, 0x04, 0x01, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, 0x00, + 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x0a, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, 0x00, + 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, 0x00, + 0x14, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1a, 0x00, 0x11, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, 0x00, + 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, 0x00, + 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, 0x00, + 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, 0x00, + 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, 0x00, + 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, 0x00, + 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, 0x00, + 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, 0x00, + 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, 0x00, + 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, 0x00, + 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, + 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, 0x00, + 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x0c, 0x00, 0x1b, 0x00, 0x29, 0x00, + 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, 0x00, + 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, 0x05, 0x06, 0x00, 0x13, 0x00, 0x21, 0x00, + 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, 0x00, + 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x01, 0x00, 0x0d, 0x00, 0x19, 0x00, + 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, 0x00, + 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x08, 0x00, 0x13, 0x00, + 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, 0x00, + 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, 0x03, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, 0x00, + 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6f, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb2, 0x00, 0x6f, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc8, 0x00, 0xa2, 0x00, 0x4f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x7f, 0x00, 0x3c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1a, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd6, 0x00, 0xc6, 0x00, 0x9c, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd9, 0x00, 0xcd, 0x00, 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, 0x3d, 0x00, 0x1d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, + 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, + 0x00, 0x30, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, 0x00, + 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, 0x78, 0x00, 0x5d, + 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, + 0x09, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, + 0x00, 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x13, 0x00, 0x0f, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, + 0x00, 0x64, 0x00, 0x4e, 0x00, 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, 0x00, + 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x99, 0x00, 0x85, + 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, + 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, + 0x00, 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x14, 0x00, + 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x37, 0x00, 0x6f, 0x00, 0xb2, 0x00, 0xc8, 0x00, 0xd2, 0x00, 0xd6, 0x00, 0xd9, 0x00, 0xda, + 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, + 0x00, 0xa6, 0x00, 0x76, 0x00, 0x9b, 0x00, 0xac, 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xda, + 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x4c, 0x00, 0x87, 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd4, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdb, + 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, + 0x01, 0x61, 0x00, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x00, 0x6f, 0x00, 0xa2, 0x00, 0xb9, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xd2, + 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0x76, 0x00, 0x5b, 0x00, 0x75, 0x00, 0x96, 0x00, 0xab, 0x00, 0xc0, 0x00, 0xcd, 0x00, 0xd2, + 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x11, 0x01, 0x39, 0x00, 0x87, 0x00, 0xaf, 0x00, 0xc1, 0x00, 0xcb, 0x00, 0xd1, 0x00, 0xd4, + 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, + 0x03, 0x23, 0x00, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, + 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xae, 0x00, 0xb9, + 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, + 0x00, 0x9b, 0x00, 0x75, 0x00, 0x2b, 0x00, 0x5b, 0x00, 0x7f, 0x00, 0x9b, 0x00, 0xae, 0x00, 0xb9, + 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x01, 0x07, 0x0f, 0x00, 0x32, 0x00, 0x6e, 0x00, 0x93, 0x00, 0xaa, 0x00, 0xb8, 0x00, 0xc1, + 0x00, 0xc8, 0x00, 0xcc, 0x00, 0xd0, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, + 0x09, 0x03, 0x00, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, + 0x00, 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, + 0x00, 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0xca, 0x00, 0xcd, + 0x00, 0xac, 0x00, 0x96, 0x00, 0x5b, 0x00, 0x13, 0x00, 0x41, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, + 0x00, 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0xca, 0x00, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x0c, 0x02, 0x05, 0x19, 0x00, 0x31, 0x00, 0x5f, 0x00, 0x7f, 0x00, 0x96, 0x00, 0xa6, + 0x00, 0xb2, 0x00, 0xba, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, + 0x13, 0x00, 0x01, 0x04, 0x00, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, + 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, + 0x00, 0x84, 0x00, 0x93, 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, + 0x00, 0xbc, 0x00, 0xab, 0x00, 0x7f, 0x00, 0x41, 0x00, 0x03, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, + 0x00, 0x84, 0x00, 0x93, 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x12, 0x00, 0x09, 0x0b, 0x04, 0x1e, 0x00, 0x30, 0x00, 0x55, 0x00, 0x71, 0x00, 0x87, + 0x00, 0x97, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb5, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc7, + 0x18, 0x00, 0x0a, 0x00, 0x00, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, + 0x00, 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x27, 0x00, 0x47, + 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, + 0x00, 0xcd, 0x00, 0xc0, 0x00, 0x9b, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x27, 0x00, 0x47, + 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x03, 0x07, 0x12, 0x03, 0x22, 0x00, 0x30, 0x00, 0x4e, 0x00, 0x67, + 0x00, 0x7b, 0x00, 0x8b, 0x00, 0x98, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, 0x00, 0xbb, + 0x1a, 0x00, 0x11, 0x00, 0x00, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, + 0x00, 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, + 0x00, 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x99, 0x00, 0xa0, + 0x00, 0xd9, 0x00, 0xcd, 0x00, 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x01, 0x00, 0x21, + 0x00, 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x99, 0x00, 0xa0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x19, 0x00, 0x0e, 0x00, 0x09, 0x0a, 0x06, 0x17, 0x02, 0x24, 0x00, 0x30, 0x00, 0x4a, + 0x00, 0x60, 0x00, 0x72, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa8, 0x00, 0xae, + 0x1c, 0x00, 0x15, 0x00, 0x03, 0x00, 0x00, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, + 0x00, 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1d, 0x00, 0x36, 0x00, 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, + 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x01, + 0x00, 0x1d, 0x00, 0x36, 0x00, 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x0a, 0x04, 0x07, 0x0f, 0x05, 0x1b, 0x02, 0x26, 0x00, 0x30, + 0x00, 0x46, 0x00, 0x5a, 0x00, 0x6b, 0x00, 0x79, 0x00, 0x85, 0x00, 0x8f, 0x00, 0x98, 0x00, 0xa0, + 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, + 0x00, 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, + 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, 0x3d, 0x00, 0x1d, + 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x15, 0x00, 0x0b, 0x00, 0x09, 0x0a, 0x06, 0x14, 0x04, 0x1e, 0x02, 0x27, + 0x00, 0x30, 0x00, 0x44, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x7e, 0x00, 0x88, 0x00, 0x91, + 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, + 0x00, 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, 0x5c, 0x00, 0x68, + 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, + 0x00, 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, 0x5c, 0x00, 0x68, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x17, 0x00, 0x0f, 0x00, 0x0a, 0x05, 0x08, 0x0e, 0x06, 0x17, 0x03, 0x20, + 0x01, 0x28, 0x00, 0x2f, 0x00, 0x42, 0x00, 0x52, 0x00, 0x60, 0x00, 0x6d, 0x00, 0x78, 0x00, 0x82, + 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, + 0x00, 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, + 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, + 0x00, 0x30, 0x00, 0x17, 0x00, 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x0b, 0x02, 0x09, 0x09, 0x07, 0x12, 0x05, 0x1a, + 0x03, 0x21, 0x01, 0x29, 0x00, 0x2f, 0x00, 0x40, 0x00, 0x4f, 0x00, 0x5c, 0x00, 0x68, 0x00, 0x73, + 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, + 0x00, 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, + 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, 0x78, 0x00, 0x5d, + 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x0a, 0x06, 0x08, 0x0d, 0x06, 0x14, + 0x04, 0x1c, 0x03, 0x23, 0x01, 0x29, 0x00, 0x2f, 0x00, 0x3e, 0x00, 0x4c, 0x00, 0x59, 0x00, 0x64, + 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x29, + 0x00, 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x22, 0x00, 0x30, + 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, + 0x00, 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x11, 0x00, 0x22, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x16, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x09, 0x09, 0x07, 0x10, + 0x06, 0x17, 0x04, 0x1e, 0x02, 0x24, 0x01, 0x2a, 0x00, 0x2f, 0x00, 0x3d, 0x00, 0x4a, 0x00, 0x56, + 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x21, + 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, + 0x00, 0x64, 0x00, 0x4e, 0x00, 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x0a, 0x06, 0x08, 0x0c, + 0x07, 0x13, 0x05, 0x19, 0x04, 0x1f, 0x02, 0x25, 0x01, 0x2a, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x48, + 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x19, + 0x00, 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x99, 0x00, 0x85, + 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x0a, 0x04, 0x09, 0x09, + 0x08, 0x0f, 0x06, 0x15, 0x05, 0x1b, 0x03, 0x20, 0x02, 0x26, 0x01, 0x2b, 0x00, 0x2f, 0x00, 0x3b, + 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x13, + 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, + 0x00, 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0b, 0x01, 0x0a, 0x07, + 0x08, 0x0c, 0x07, 0x11, 0x06, 0x17, 0x04, 0x1c, 0x03, 0x21, 0x02, 0x26, 0x01, 0x2b, 0x00, 0x2f, + 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x0e, + 0x00, 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1c, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x04, 0x4c, 0x09, 0x11, 0x11, 0x01, 0x17, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x24, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x01, 0x61, 0x03, 0x23, 0x09, 0x03, 0x13, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, + 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x00, 0x87, 0x01, 0x39, 0x07, 0x0f, 0x0c, 0x02, 0x12, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1b, 0x00, + 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x28, 0x00, 0x21, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x01, 0x04, 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, 0x00, + 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, + 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, + 0x00, 0xbc, 0x00, 0x87, 0x00, 0x32, 0x05, 0x19, 0x09, 0x0b, 0x0b, 0x03, 0x0e, 0x00, 0x12, 0x00, + 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x27, 0x00, 0x0f, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1a, 0x00, + 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, + 0x00, 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x03, 0x00, 0x0a, 0x00, + 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x12, 0x00, 0x15, 0x00, + 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, + 0x00, 0xcd, 0x00, 0xaf, 0x00, 0x6e, 0x00, 0x31, 0x04, 0x1e, 0x07, 0x12, 0x09, 0x0a, 0x0a, 0x04, + 0x0b, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x2b, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x12, 0x00, 0x15, 0x00, + 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, + 0x00, 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, + 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, 0x00, + 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x1b, 0x00, + 0x00, 0xd4, 0x00, 0xc1, 0x00, 0x93, 0x00, 0x5f, 0x00, 0x30, 0x03, 0x22, 0x06, 0x17, 0x07, 0x0f, + 0x09, 0x0a, 0x0a, 0x05, 0x0b, 0x02, 0x0c, 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x28, 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, 0x00, + 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x1b, 0x00, + 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, + 0x00, 0x14, 0x00, 0x0b, 0x00, 0x04, 0x01, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, + 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, + 0x00, 0xd8, 0x00, 0xcb, 0x00, 0xaa, 0x00, 0x7f, 0x00, 0x55, 0x00, 0x30, 0x02, 0x24, 0x05, 0x1b, + 0x06, 0x14, 0x08, 0x0e, 0x09, 0x09, 0x0a, 0x06, 0x0b, 0x03, 0x0b, 0x00, 0x0d, 0x00, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, + 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, + 0x00, 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, + 0x00, 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x02, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, + 0x00, 0xda, 0x00, 0xd1, 0x00, 0xb8, 0x00, 0x96, 0x00, 0x71, 0x00, 0x4e, 0x00, 0x30, 0x02, 0x26, + 0x04, 0x1e, 0x06, 0x17, 0x07, 0x12, 0x08, 0x0d, 0x09, 0x09, 0x0a, 0x06, 0x0a, 0x04, 0x0b, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, + 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, + 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, + 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0xa6, 0x00, 0x87, 0x00, 0x67, 0x00, 0x4a, 0x00, 0x30, + 0x02, 0x27, 0x03, 0x20, 0x05, 0x1a, 0x06, 0x14, 0x07, 0x10, 0x08, 0x0c, 0x09, 0x09, 0x0a, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, + 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, + 0x00, 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, 0x00, + 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc8, 0x00, 0xb2, 0x00, 0x97, 0x00, 0x7b, 0x00, 0x60, 0x00, 0x46, + 0x00, 0x30, 0x01, 0x28, 0x03, 0x21, 0x04, 0x1c, 0x06, 0x17, 0x07, 0x13, 0x08, 0x0f, 0x08, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, 0x00, + 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, + 0x00, 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, + 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xba, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x72, 0x00, 0x5a, + 0x00, 0x44, 0x00, 0x2f, 0x01, 0x29, 0x03, 0x23, 0x04, 0x1e, 0x05, 0x19, 0x06, 0x15, 0x07, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, + 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, + 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, 0x00, + 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd0, 0x00, 0xc0, 0x00, 0xad, 0x00, 0x98, 0x00, 0x81, 0x00, 0x6b, + 0x00, 0x56, 0x00, 0x42, 0x00, 0x2f, 0x01, 0x29, 0x02, 0x24, 0x04, 0x1f, 0x05, 0x1b, 0x06, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, 0x00, + 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, 0x00, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, + 0x00, 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, + 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xc5, 0x00, 0xb5, 0x00, 0xa2, 0x00, 0x8e, 0x00, 0x79, + 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, 0x01, 0x2a, 0x02, 0x25, 0x03, 0x20, 0x04, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, + 0x09, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, + 0x00, 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xaa, 0x00, 0x98, 0x00, 0x85, + 0x00, 0x72, 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x2f, 0x01, 0x2a, 0x02, 0x26, 0x03, 0x21, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x13, 0x00, 0x0f, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, + 0x00, 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8f, + 0x00, 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x3d, 0x00, 0x2f, 0x01, 0x2b, 0x02, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, 0x00, + 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, + 0x00, 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xce, 0x00, 0xc3, 0x00, 0xb6, 0x00, 0xa8, 0x00, 0x98, + 0x00, 0x88, 0x00, 0x78, 0x00, 0x68, 0x00, 0x59, 0x00, 0x4a, 0x00, 0x3c, 0x00, 0x2f, 0x01, 0x2b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, + 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, + 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbb, 0x00, 0xae, 0x00, 0xa0, + 0x00, 0x91, 0x00, 0x82, 0x00, 0x73, 0x00, 0x64, 0x00, 0x56, 0x00, 0x48, 0x00, 0x3b, 0x00, 0x2f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x14, 0x00, + 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, + 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x61, 0x03, 0x23, 0x09, 0x03, 0x13, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, + 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x61, 0x00, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x01, 0x04, 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, 0x00, + 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x23, 0x00, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, + 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x03, 0x00, 0x0a, 0x00, + 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x03, 0x00, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, + 0x00, 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, + 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x01, 0x04, 0x00, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, + 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, + 0x00, 0x14, 0x00, 0x0b, 0x00, 0x04, 0x01, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x0a, 0x00, 0x00, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, + 0x00, 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, + 0x00, 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x02, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x11, 0x00, 0x00, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, + 0x00, 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, + 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x15, 0x00, 0x03, 0x00, 0x00, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, + 0x00, 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, + 0x00, 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, + 0x00, 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, + 0x00, 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, + 0x00, 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, + 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, + 0x00, 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, + 0x00, 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, + 0x00, 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, + 0x00, 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x29, + 0x00, 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, + 0x00, 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x21, + 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, + 0x00, 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x19, + 0x00, 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, + 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x13, + 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, + 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x0e, + 0x00, 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +#endif diff --git a/src/video_core/smaa_search_tex.h b/src/video_core/smaa_search_tex.h new file mode 100644 index 0000000..61939e2 --- /dev/null +++ b/src/video_core/smaa_search_tex.h @@ -0,0 +1,88 @@ +// SPDX-FileCopyrightText: 2013 Jorge Jimenez (jorge@iryoku.com) +// SPDX-FileCopyrightText: 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) +// SPDX-FileCopyrightText: 2013 Belen Masia (bmasia@unizar.es) +// SPDX-FileCopyrightText: 2013 Fernando Navarro (fernandn@microsoft.com) +// SPDX-FileCopyrightText: 2013 Diego Gutierrez (diegog@unizar.es) +// SPDX-License-Identifier: MIT + +#ifndef SEARCHTEX_H +#define SEARCHTEX_H + +#define SEARCHTEX_WIDTH 64 +#define SEARCHTEX_HEIGHT 16 +#define SEARCHTEX_PITCH SEARCHTEX_WIDTH +#define SEARCHTEX_SIZE (SEARCHTEX_HEIGHT * SEARCHTEX_PITCH) + +/** + * Stored in R8 format. Load it in the following format: + * - DX9: D3DFMT_L8 + * - DX10: DXGI_FORMAT_R8_UNORM + */ +static const unsigned char searchTexBytes[] = { + 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#endif diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 079d5f0..1a76d41 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -73,17 +73,17 @@ bool SurfaceTargetIsArray(SurfaceTarget target) { PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) { switch (format) { - case Tegra::DepthFormat::S8_UINT_Z24_UNORM: + case Tegra::DepthFormat::Z24_UNORM_S8_UINT: return PixelFormat::S8_UINT_D24_UNORM; - case Tegra::DepthFormat::D24S8_UNORM: + case Tegra::DepthFormat::S8Z24_UNORM: return PixelFormat::D24_UNORM_S8_UINT; - case Tegra::DepthFormat::D32_FLOAT: + case Tegra::DepthFormat::Z32_FLOAT: return PixelFormat::D32_FLOAT; - case Tegra::DepthFormat::D16_UNORM: + case Tegra::DepthFormat::Z16_UNORM: return PixelFormat::D16_UNORM; case Tegra::DepthFormat::S8_UINT: return PixelFormat::S8_UINT; - case Tegra::DepthFormat::D32_FLOAT_S8X24_UINT: + case Tegra::DepthFormat::Z32_FLOAT_X24S8_UINT: return PixelFormat::D32_FLOAT_S8_UINT; default: UNIMPLEMENTED_MSG("Unimplemented format={}", format); @@ -93,11 +93,14 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) { PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { switch (format) { - case Tegra::RenderTargetFormat::R32B32G32A32_FLOAT: + case Tegra::RenderTargetFormat::R32G32B32A32_FLOAT: + case Tegra::RenderTargetFormat::R32G32B32X32_FLOAT: return PixelFormat::R32G32B32A32_FLOAT; case Tegra::RenderTargetFormat::R32G32B32A32_SINT: + case Tegra::RenderTargetFormat::R32G32B32X32_SINT: return PixelFormat::R32G32B32A32_SINT; case Tegra::RenderTargetFormat::R32G32B32A32_UINT: + case Tegra::RenderTargetFormat::R32G32B32X32_UINT: return PixelFormat::R32G32B32A32_UINT; case Tegra::RenderTargetFormat::R16G16B16A16_UNORM: return PixelFormat::R16G16B16A16_UNORM; @@ -117,17 +120,23 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) return PixelFormat::R32G32_UINT; case Tegra::RenderTargetFormat::R16G16B16X16_FLOAT: return PixelFormat::R16G16B16X16_FLOAT; - case Tegra::RenderTargetFormat::B8G8R8A8_UNORM: + case Tegra::RenderTargetFormat::A8R8G8B8_UNORM: + case Tegra::RenderTargetFormat::X8R8G8B8_UNORM: return PixelFormat::B8G8R8A8_UNORM; - case Tegra::RenderTargetFormat::B8G8R8A8_SRGB: + case Tegra::RenderTargetFormat::A8R8G8B8_SRGB: + case Tegra::RenderTargetFormat::X8R8G8B8_SRGB: return PixelFormat::B8G8R8A8_SRGB; case Tegra::RenderTargetFormat::A2B10G10R10_UNORM: return PixelFormat::A2B10G10R10_UNORM; case Tegra::RenderTargetFormat::A2B10G10R10_UINT: return PixelFormat::A2B10G10R10_UINT; + case Tegra::RenderTargetFormat::A2R10G10B10_UNORM: + return PixelFormat::A2R10G10B10_UNORM; case Tegra::RenderTargetFormat::A8B8G8R8_UNORM: + case Tegra::RenderTargetFormat::X8B8G8R8_UNORM: return PixelFormat::A8B8G8R8_UNORM; case Tegra::RenderTargetFormat::A8B8G8R8_SRGB: + case Tegra::RenderTargetFormat::X8B8G8R8_SRGB: return PixelFormat::A8B8G8R8_SRGB; case Tegra::RenderTargetFormat::A8B8G8R8_SNORM: return PixelFormat::A8B8G8R8_SNORM; @@ -156,6 +165,7 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) case Tegra::RenderTargetFormat::R5G6B5_UNORM: return PixelFormat::R5G6B5_UNORM; case Tegra::RenderTargetFormat::A1R5G5B5_UNORM: + case Tegra::RenderTargetFormat::X1R5G5B5_UNORM: return PixelFormat::A1R5G5B5_UNORM; case Tegra::RenderTargetFormat::R8G8_UNORM: return PixelFormat::R8G8_UNORM; @@ -204,23 +214,16 @@ PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format) } SurfaceType GetFormatType(PixelFormat pixel_format) { - if (static_cast(pixel_format) < - static_cast(PixelFormat::MaxColorFormat)) { + if (pixel_format < PixelFormat::MaxColorFormat) { return SurfaceType::ColorTexture; } - - if (static_cast(pixel_format) < - static_cast(PixelFormat::MaxDepthFormat)) { + if (pixel_format < PixelFormat::MaxDepthFormat) { return SurfaceType::Depth; } - - if (static_cast(pixel_format) < - static_cast(PixelFormat::MaxStencilFormat)) { + if (pixel_format < PixelFormat::MaxStencilFormat) { return SurfaceType::Stencil; } - - if (static_cast(pixel_format) < - static_cast(PixelFormat::MaxDepthStencilFormat)) { + if (pixel_format < PixelFormat::MaxDepthStencilFormat) { return SurfaceType::DepthStencil; } @@ -247,6 +250,8 @@ bool IsPixelFormatASTC(PixelFormat format) { case PixelFormat::ASTC_2D_6X6_UNORM: case PixelFormat::ASTC_2D_6X6_SRGB: case PixelFormat::ASTC_2D_10X6_UNORM: + case PixelFormat::ASTC_2D_10X5_UNORM: + case PixelFormat::ASTC_2D_10X5_SRGB: case PixelFormat::ASTC_2D_10X10_UNORM: case PixelFormat::ASTC_2D_10X10_SRGB: case PixelFormat::ASTC_2D_12X12_UNORM: @@ -276,6 +281,7 @@ bool IsPixelFormatSRGB(PixelFormat format) { case PixelFormat::ASTC_2D_5X5_SRGB: case PixelFormat::ASTC_2D_10X8_SRGB: case PixelFormat::ASTC_2D_6X6_SRGB: + case PixelFormat::ASTC_2D_10X5_SRGB: case PixelFormat::ASTC_2D_10X10_SRGB: case PixelFormat::ASTC_2D_12X12_SRGB: case PixelFormat::ASTC_2D_8X6_SRGB: diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 16273f1..44b79af 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -23,6 +23,7 @@ enum class PixelFormat { A1R5G5B5_UNORM, A2B10G10R10_UNORM, A2B10G10R10_UINT, + A2R10G10B10_UNORM, A1B5G5R5_UNORM, A5B5G5R1_UNORM, R8_UNORM, @@ -82,7 +83,7 @@ enum class PixelFormat { BC3_SRGB, BC7_SRGB, A4B4G4R4_UNORM, - R4G4_UNORM, + G4R4_UNORM, ASTC_2D_4X4_SRGB, ASTC_2D_8X8_SRGB, ASTC_2D_8X5_SRGB, @@ -94,6 +95,8 @@ enum class PixelFormat { ASTC_2D_6X6_UNORM, ASTC_2D_6X6_SRGB, ASTC_2D_10X6_UNORM, + ASTC_2D_10X5_UNORM, + ASTC_2D_10X5_SRGB, ASTC_2D_10X10_UNORM, ASTC_2D_10X10_SRGB, ASTC_2D_12X12_UNORM, @@ -157,6 +160,7 @@ constexpr std::array BLOCK_WIDTH_TABLE = {{ 1, // A1R5G5B5_UNORM 1, // A2B10G10R10_UNORM 1, // A2B10G10R10_UINT + 1, // A2R10G10B10_UNORM 1, // A1B5G5R5_UNORM 1, // A5B5G5R1_UNORM 1, // R8_UNORM @@ -216,7 +220,7 @@ constexpr std::array BLOCK_WIDTH_TABLE = {{ 4, // BC3_SRGB 4, // BC7_SRGB 1, // A4B4G4R4_UNORM - 1, // R4G4_UNORM + 1, // G4R4_UNORM 4, // ASTC_2D_4X4_SRGB 8, // ASTC_2D_8X8_SRGB 8, // ASTC_2D_8X5_SRGB @@ -228,6 +232,8 @@ constexpr std::array BLOCK_WIDTH_TABLE = {{ 6, // ASTC_2D_6X6_UNORM 6, // ASTC_2D_6X6_SRGB 10, // ASTC_2D_10X6_UNORM + 10, // ASTC_2D_10X5_UNORM + 10, // ASTC_2D_10X5_SRGB 10, // ASTC_2D_10X10_UNORM 10, // ASTC_2D_10X10_SRGB 12, // ASTC_2D_12X12_UNORM @@ -260,6 +266,7 @@ constexpr std::array BLOCK_HEIGHT_TABLE = {{ 1, // A1R5G5B5_UNORM 1, // A2B10G10R10_UNORM 1, // A2B10G10R10_UINT + 1, // A2R10G10B10_UNORM 1, // A1B5G5R5_UNORM 1, // A5B5G5R1_UNORM 1, // R8_UNORM @@ -319,7 +326,7 @@ constexpr std::array BLOCK_HEIGHT_TABLE = {{ 4, // BC3_SRGB 4, // BC7_SRGB 1, // A4B4G4R4_UNORM - 1, // R4G4_UNORM + 1, // G4R4_UNORM 4, // ASTC_2D_4X4_SRGB 8, // ASTC_2D_8X8_SRGB 5, // ASTC_2D_8X5_SRGB @@ -331,6 +338,8 @@ constexpr std::array BLOCK_HEIGHT_TABLE = {{ 6, // ASTC_2D_6X6_UNORM 6, // ASTC_2D_6X6_SRGB 6, // ASTC_2D_10X6_UNORM + 5, // ASTC_2D_10X5_UNORM + 5, // ASTC_2D_10X5_SRGB 10, // ASTC_2D_10X10_UNORM 10, // ASTC_2D_10X10_SRGB 12, // ASTC_2D_12X12_UNORM @@ -363,6 +372,7 @@ constexpr std::array BITS_PER_BLOCK_TABLE = {{ 16, // A1R5G5B5_UNORM 32, // A2B10G10R10_UNORM 32, // A2B10G10R10_UINT + 32, // A2R10G10B10_UNORM 16, // A1B5G5R5_UNORM 16, // A5B5G5R1_UNORM 8, // R8_UNORM @@ -422,7 +432,7 @@ constexpr std::array BITS_PER_BLOCK_TABLE = {{ 128, // BC3_SRGB 128, // BC7_UNORM 16, // A4B4G4R4_UNORM - 8, // R4G4_UNORM + 8, // G4R4_UNORM 128, // ASTC_2D_4X4_SRGB 128, // ASTC_2D_8X8_SRGB 128, // ASTC_2D_8X5_SRGB @@ -434,6 +444,8 @@ constexpr std::array BITS_PER_BLOCK_TABLE = {{ 128, // ASTC_2D_6X6_UNORM 128, // ASTC_2D_6X6_SRGB 128, // ASTC_2D_10X6_UNORM + 128, // ASTC_2D_10X5_UNORM + 128, // ASTC_2D_10X5_SRGB 128, // ASTC_2D_10X10_UNORM 128, // ASTC_2D_10X10_SRGB 128, // ASTC_2D_12X12_UNORM diff --git a/src/video_core/texture_cache/descriptor_table.h b/src/video_core/texture_cache/descriptor_table.h index b18e383..ee42402 100644 --- a/src/video_core/texture_cache/descriptor_table.h +++ b/src/video_core/texture_cache/descriptor_table.h @@ -18,7 +18,7 @@ class DescriptorTable { public: explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {} - [[nodiscard]] bool Synchornize(GPUVAddr gpu_addr, u32 limit) { + [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) { [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { return false; } diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index 1412aa0..08aa8ca 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp @@ -63,7 +63,7 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, case Hash(TextureFormat::A4B4G4R4, UNORM): return PixelFormat::A4B4G4R4_UNORM; case Hash(TextureFormat::G4R4, UNORM): - return PixelFormat::R4G4_UNORM; + return PixelFormat::G4R4_UNORM; case Hash(TextureFormat::A5B5G5R1, UNORM): return PixelFormat::A5B5G5R1_UNORM; case Hash(TextureFormat::R8, UNORM): @@ -150,6 +150,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, return PixelFormat::D24_UNORM_S8_UINT; case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): return PixelFormat::D32_FLOAT_S8_UINT; + case Hash(TextureFormat::R32_B24G8, FLOAT, UINT, UNORM, UNORM, LINEAR): + return PixelFormat::D32_FLOAT_S8_UINT; case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR): return PixelFormat::BC1_RGBA_UNORM; case Hash(TextureFormat::BC1_RGBA, UNORM, SRGB): @@ -208,6 +210,10 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, return PixelFormat::ASTC_2D_6X6_SRGB; case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR): return PixelFormat::ASTC_2D_10X6_UNORM; + case Hash(TextureFormat::ASTC_2D_10X5, UNORM, LINEAR): + return PixelFormat::ASTC_2D_10X5_UNORM; + case Hash(TextureFormat::ASTC_2D_10X5, UNORM, SRGB): + return PixelFormat::ASTC_2D_10X5_SRGB; case Hash(TextureFormat::ASTC_2D_10X10, UNORM, LINEAR): return PixelFormat::ASTC_2D_10X10_UNORM; case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB): diff --git a/src/video_core/texture_cache/formatter.cpp b/src/video_core/texture_cache/formatter.cpp index ee4f2d4..4188901 100644 --- a/src/video_core/texture_cache/formatter.cpp +++ b/src/video_core/texture_cache/formatter.cpp @@ -4,6 +4,7 @@ #include #include +#include "common/polyfill_ranges.h" #include "video_core/texture_cache/formatter.h" #include "video_core/texture_cache/image_base.h" #include "video_core/texture_cache/image_info.h" diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h index 95a5726..f1f0a05 100644 --- a/src/video_core/texture_cache/formatter.h +++ b/src/video_core/texture_cache/formatter.h @@ -35,6 +35,8 @@ struct fmt::formatter : fmt::formatter : fmt::formatter : fmt::formatter #include "common/common_types.h" +#include "common/div_ceil.h" #include "video_core/surface.h" #include "video_core/texture_cache/formatter.h" #include "video_core/texture_cache/image_base.h" @@ -182,10 +183,6 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i }; const bool is_lhs_compressed = lhs_block.width > 1 || lhs_block.height > 1; const bool is_rhs_compressed = rhs_block.width > 1 || rhs_block.height > 1; - if (is_lhs_compressed && is_rhs_compressed) { - LOG_ERROR(HW_GPU, "Compressed to compressed image aliasing is not implemented"); - return; - } const s32 lhs_mips = lhs.info.resources.levels; const s32 rhs_mips = rhs.info.resources.levels; const s32 num_mips = std::min(lhs_mips - base->level, rhs_mips); @@ -199,12 +196,12 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i Extent3D lhs_size = MipSize(lhs.info.size, base->level + mip_level); Extent3D rhs_size = MipSize(rhs.info.size, mip_level); if (is_lhs_compressed) { - lhs_size.width /= lhs_block.width; - lhs_size.height /= lhs_block.height; + lhs_size.width = Common::DivCeil(lhs_size.width, lhs_block.width); + lhs_size.height = Common::DivCeil(lhs_size.height, lhs_block.height); } if (is_rhs_compressed) { - rhs_size.width /= rhs_block.width; - rhs_size.height /= rhs_block.height; + rhs_size.width = Common::DivCeil(rhs_size.width, rhs_block.width); + rhs_size.height = Common::DivCeil(rhs_size.height, rhs_block.height); } const Extent3D copy_size{ .width = std::min(lhs_size.width, rhs_size.width), diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index 1f85ec9..6205656 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -88,6 +88,9 @@ struct ImageBase { u32 scale_rating = 0; u64 scale_tick = 0; bool has_scaled = false; + + size_t channel = 0; + ImageFlagBits flags = ImageFlagBits::CpuModified; GPUVAddr gpu_addr = 0; diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 6c073ee..852ec25 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/assert.h" #include "video_core/surface.h" #include "video_core/texture_cache/format_lookup_table.h" @@ -12,6 +14,7 @@ namespace VideoCommon { +using Tegra::Engines::Maxwell3D; using Tegra::Texture::TextureType; using Tegra::Texture::TICEntry; using VideoCore::Surface::PixelFormat; @@ -107,12 +110,13 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept { } } -ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept { +ImageInfo::ImageInfo(const Maxwell3D::Regs& regs, size_t index) noexcept { const auto& rt = regs.rt[index]; format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(rt.format); rescaleable = false; if (rt.tile_mode.is_pitch_linear) { - ASSERT(rt.tile_mode.is_3d == 0); + ASSERT(rt.tile_mode.dim_control == + Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesArray); type = ImageType::Linear; pitch = rt.width; size = Extent3D{ @@ -124,15 +128,16 @@ ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) } size.width = rt.width; size.height = rt.height; - layer_stride = rt.layer_stride * 4; + layer_stride = rt.array_pitch * 4; maybe_unaligned_layer_stride = layer_stride; - num_samples = NumSamples(regs.multisample_mode); + num_samples = NumSamples(regs.anti_alias_samples_mode); block = Extent3D{ .width = rt.tile_mode.block_width, .height = rt.tile_mode.block_height, .depth = rt.tile_mode.block_depth, }; - if (rt.tile_mode.is_3d) { + if (rt.tile_mode.dim_control == + Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesDepth) { type = ImageType::e3D; size.depth = rt.depth; } else { @@ -146,31 +151,37 @@ ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept { format = VideoCore::Surface::PixelFormatFromDepthFormat(regs.zeta.format); - size.width = regs.zeta_width; - size.height = regs.zeta_height; + size.width = regs.zeta_size.width; + size.height = regs.zeta_size.height; rescaleable = false; resources.levels = 1; - layer_stride = regs.zeta.layer_stride * 4; + layer_stride = regs.zeta.array_pitch * 4; maybe_unaligned_layer_stride = layer_stride; - num_samples = NumSamples(regs.multisample_mode); + num_samples = NumSamples(regs.anti_alias_samples_mode); block = Extent3D{ .width = regs.zeta.tile_mode.block_width, .height = regs.zeta.tile_mode.block_height, .depth = regs.zeta.tile_mode.block_depth, }; if (regs.zeta.tile_mode.is_pitch_linear) { - ASSERT(regs.zeta.tile_mode.is_3d == 0); + ASSERT(regs.zeta.tile_mode.dim_control == + Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesArray); type = ImageType::Linear; pitch = size.width * BytesPerBlock(format); - } else if (regs.zeta.tile_mode.is_3d) { + } else if (regs.zeta.tile_mode.dim_control == + Maxwell3D::Regs::TileMode::DimensionControl::DepthDefinesDepth) { ASSERT(regs.zeta.tile_mode.is_pitch_linear == 0); + ASSERT(regs.zeta_size.dim_control == + Maxwell3D::Regs::ZetaSize::DimensionControl::ArraySizeOne); type = ImageType::e3D; - size.depth = regs.zeta_depth; + size.depth = regs.zeta_size.depth; } else { + ASSERT(regs.zeta_size.dim_control == + Maxwell3D::Regs::ZetaSize::DimensionControl::DepthDefinesArray); rescaleable = block.depth == 0; downscaleable = size.height > 512; type = ImageType::e2D; - resources.layers = regs.zeta_depth; + resources.layers = regs.zeta_size.depth; } } diff --git a/src/video_core/texture_cache/render_targets.h b/src/video_core/texture_cache/render_targets.h index da8ffe9..0829d77 100644 --- a/src/video_core/texture_cache/render_targets.h +++ b/src/video_core/texture_cache/render_targets.h @@ -13,7 +13,7 @@ namespace VideoCommon { /// Framebuffer properties used to lookup a framebuffer struct RenderTargets { - constexpr auto operator<=>(const RenderTargets&) const noexcept = default; + constexpr bool operator==(const RenderTargets&) const noexcept = default; constexpr bool Contains(std::span elements) const noexcept { const auto contains = [elements](ImageViewId item) { @@ -26,6 +26,7 @@ struct RenderTargets { ImageViewId depth_buffer_id{}; std::array draw_buffers{}; Extent2D size{}; + bool is_rescaled{}; }; } // namespace VideoCommon diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h index 46e8a86..1e2aad7 100644 --- a/src/video_core/texture_cache/slot_vector.h +++ b/src/video_core/texture_cache/slot_vector.h @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/polyfill_ranges.h" namespace VideoCommon { diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp new file mode 100644 index 0000000..8a9a32f --- /dev/null +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "video_core/control/channel_state_cache.inc" +#include "video_core/texture_cache/texture_cache_base.h" + +namespace VideoCommon { + +TextureCacheChannelInfo::TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept + : ChannelInfo(state), graphics_image_table{gpu_memory}, graphics_sampler_table{gpu_memory}, + compute_image_table{gpu_memory}, compute_sampler_table{gpu_memory} {} + +template class VideoCommon::ChannelSetupCaches; + +} // namespace VideoCommon diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 1dbe01b..8e68a2e 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -7,6 +7,7 @@ #include "common/alignment.h" #include "common/settings.h" +#include "video_core/control/channel_state.h" #include "video_core/dirty_flags.h" #include "video_core/engines/kepler_compute.h" #include "video_core/texture_cache/image_view_base.h" @@ -29,12 +30,8 @@ using VideoCore::Surface::SurfaceType; using namespace Common::Literals; template -TextureCache

    ::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_, - Tegra::Engines::Maxwell3D& maxwell3d_, - Tegra::Engines::KeplerCompute& kepler_compute_, - Tegra::MemoryManager& gpu_memory_) - : runtime{runtime_}, rasterizer{rasterizer_}, maxwell3d{maxwell3d_}, - kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_} { +TextureCache

    ::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_) + : runtime{runtime_}, rasterizer{rasterizer_} { // Configure null sampler TSCEntry sampler_descriptor{}; sampler_descriptor.min_filter.Assign(Tegra::Texture::TextureFilter::Linear); @@ -93,7 +90,7 @@ void TextureCache

    ::RunGarbageCollector() { const auto copies = FullDownloadCopies(image.info); image.DownloadMemory(map, copies); runtime.Finish(); - SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); + SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); } if (True(image.flags & ImageFlagBits::Tracked)) { UntrackImage(image, image_id); @@ -152,22 +149,24 @@ void TextureCache

    ::MarkModification(ImageId id) noexcept { template template void TextureCache

    ::FillGraphicsImageViews(std::span views) { - FillImageViews(graphics_image_table, graphics_image_view_ids, views); + FillImageViews(channel_state->graphics_image_table, + channel_state->graphics_image_view_ids, views); } template void TextureCache

    ::FillComputeImageViews(std::span views) { - FillImageViews(compute_image_table, compute_image_view_ids, views); + FillImageViews(channel_state->compute_image_table, channel_state->compute_image_view_ids, + views); } template typename P::Sampler* TextureCache

    ::GetGraphicsSampler(u32 index) { - if (index > graphics_sampler_table.Limit()) { + if (index > channel_state->graphics_sampler_table.Limit()) { LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); return &slot_samplers[NULL_SAMPLER_ID]; } - const auto [descriptor, is_new] = graphics_sampler_table.Read(index); - SamplerId& id = graphics_sampler_ids[index]; + const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index); + SamplerId& id = channel_state->graphics_sampler_ids[index]; if (is_new) { id = FindSampler(descriptor); } @@ -176,12 +175,12 @@ typename P::Sampler* TextureCache

    ::GetGraphicsSampler(u32 index) { template typename P::Sampler* TextureCache

    ::GetComputeSampler(u32 index) { - if (index > compute_sampler_table.Limit()) { + if (index > channel_state->compute_sampler_table.Limit()) { LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); return &slot_samplers[NULL_SAMPLER_ID]; } - const auto [descriptor, is_new] = compute_sampler_table.Read(index); - SamplerId& id = compute_sampler_ids[index]; + const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index); + SamplerId& id = channel_state->compute_sampler_ids[index]; if (is_new) { id = FindSampler(descriptor); } @@ -190,35 +189,38 @@ typename P::Sampler* TextureCache

    ::GetComputeSampler(u32 index) { template void TextureCache

    ::SynchronizeGraphicsDescriptors() { - using SamplerIndex = Tegra::Engines::Maxwell3D::Regs::SamplerIndex; - const bool linked_tsc = maxwell3d.regs.sampler_index == SamplerIndex::ViaHeaderIndex; - const u32 tic_limit = maxwell3d.regs.tic.limit; - const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d.regs.tsc.limit; - if (graphics_sampler_table.Synchornize(maxwell3d.regs.tsc.Address(), tsc_limit)) { - graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); + using SamplerBinding = Tegra::Engines::Maxwell3D::Regs::SamplerBinding; + const bool linked_tsc = maxwell3d->regs.sampler_binding == SamplerBinding::ViaHeaderBinding; + const u32 tic_limit = maxwell3d->regs.tex_header.limit; + const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tex_sampler.limit; + if (channel_state->graphics_sampler_table.Synchronize(maxwell3d->regs.tex_sampler.Address(), + tsc_limit)) { + channel_state->graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); } - if (graphics_image_table.Synchornize(maxwell3d.regs.tic.Address(), tic_limit)) { - graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); + if (channel_state->graphics_image_table.Synchronize(maxwell3d->regs.tex_header.Address(), + tic_limit)) { + channel_state->graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); } } template void TextureCache

    ::SynchronizeComputeDescriptors() { - const bool linked_tsc = kepler_compute.launch_description.linked_tsc; - const u32 tic_limit = kepler_compute.regs.tic.limit; - const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute.regs.tsc.limit; - const GPUVAddr tsc_gpu_addr = kepler_compute.regs.tsc.Address(); - if (compute_sampler_table.Synchornize(tsc_gpu_addr, tsc_limit)) { - compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); + const bool linked_tsc = kepler_compute->launch_description.linked_tsc; + const u32 tic_limit = kepler_compute->regs.tic.limit; + const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute->regs.tsc.limit; + const GPUVAddr tsc_gpu_addr = kepler_compute->regs.tsc.Address(); + if (channel_state->compute_sampler_table.Synchronize(tsc_gpu_addr, tsc_limit)) { + channel_state->compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID); } - if (compute_image_table.Synchornize(kepler_compute.regs.tic.Address(), tic_limit)) { - compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); + if (channel_state->compute_image_table.Synchronize(kepler_compute->regs.tic.Address(), + tic_limit)) { + channel_state->compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID); } } template bool TextureCache

    ::RescaleRenderTargets(bool is_clear) { - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; u32 scale_rating = 0; bool rescaled = false; std::array tmp_color_images{}; @@ -315,7 +317,7 @@ bool TextureCache

    ::RescaleRenderTargets(bool is_clear) { template void TextureCache

    ::UpdateRenderTargets(bool is_clear) { using namespace VideoCommon::Dirty; - auto& flags = maxwell3d.dirty.flags; + auto& flags = maxwell3d->dirty.flags; if (!flags[Dirty::RenderTargets]) { for (size_t index = 0; index < NUM_RT; ++index) { ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index]; @@ -342,7 +344,7 @@ void TextureCache

    ::UpdateRenderTargets(bool is_clear) { PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id)); for (size_t index = 0; index < NUM_RT; ++index) { - render_targets.draw_buffers[index] = static_cast(maxwell3d.regs.rt_control.Map(index)); + render_targets.draw_buffers[index] = static_cast(maxwell3d->regs.rt_control.Map(index)); } u32 up_scale = 1; u32 down_shift = 0; @@ -351,9 +353,10 @@ void TextureCache

    ::UpdateRenderTargets(bool is_clear) { down_shift = Settings::values.resolution_info.down_shift; } render_targets.size = Extent2D{ - (maxwell3d.regs.render_area.width * up_scale) >> down_shift, - (maxwell3d.regs.render_area.height * up_scale) >> down_shift, + (maxwell3d->regs.surface_clip.width * up_scale) >> down_shift, + (maxwell3d->regs.surface_clip.height * up_scale) >> down_shift, }; + render_targets.is_rescaled = is_rescaling; flags[Dirty::DepthBiasGlobal] = true; } @@ -439,7 +442,7 @@ void TextureCache

    ::WriteMemory(VAddr cpu_addr, size_t size) { template void TextureCache

    ::DownloadMemory(VAddr cpu_addr, size_t size) { std::vector images; - ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) { + ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) { if (!image.IsSafeDownload()) { return; } @@ -458,7 +461,7 @@ void TextureCache

    ::DownloadMemory(VAddr cpu_addr, size_t size) { const auto copies = FullDownloadCopies(image.info); image.DownloadMemory(map, copies); runtime.Finish(); - SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); + SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); } } @@ -477,12 +480,20 @@ void TextureCache

    ::UnmapMemory(VAddr cpu_addr, size_t size) { } template -void TextureCache

    ::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) { +void TextureCache

    ::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) { std::vector deleted_images; - ForEachImageInRegionGPU(gpu_addr, size, + ForEachImageInRegionGPU(as_id, gpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); }); for (const ImageId id : deleted_images) { Image& image = slot_images[id]; + if (True(image.flags & ImageFlagBits::CpuModified)) { + return; + } + image.flags |= ImageFlagBits::CpuModified; + if (True(image.flags & ImageFlagBits::Tracked)) { + UntrackImage(image, id); + } + /* if (True(image.flags & ImageFlagBits::Remapped)) { continue; } @@ -490,14 +501,19 @@ void TextureCache

    ::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) { if (True(image.flags & ImageFlagBits::Tracked)) { UntrackImage(image, id); } + */ } } template -void TextureCache

    ::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, +bool TextureCache

    ::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Config& copy) { - const BlitImages images = GetBlitImages(dst, src, copy); + const auto result = GetBlitImages(dst, src, copy); + if (!result) { + return false; + } + const BlitImages images = *result; const ImageId dst_id = images.dst_id; const ImageId src_id = images.src_id; @@ -584,6 +600,7 @@ void TextureCache

    ::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, runtime.BlitImage(dst_framebuffer, dst_view, src_view, dst_region, src_region, copy.filter, copy.operation); } + return true; } template @@ -655,7 +672,7 @@ void TextureCache

    ::PopAsyncFlushes() { for (const ImageId image_id : download_ids) { const ImageBase& image = slot_images[image_id]; const auto copies = FullDownloadCopies(image.info); - SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, download_span); + SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span); download_map.offset += image.unswizzled_size_bytes; download_span = download_span.subspan(image.unswizzled_size_bytes); } @@ -714,26 +731,26 @@ void TextureCache

    ::UploadImageContents(Image& image, StagingBuffer& staging) const GPUVAddr gpu_addr = image.gpu_addr; if (True(image.flags & ImageFlagBits::AcceleratedUpload)) { - gpu_memory.ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); + gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); const auto uploads = FullUploadSwizzles(image.info); runtime.AccelerateImageUpload(image, staging, uploads); } else if (True(image.flags & ImageFlagBits::Converted)) { std::vector unswizzled_data(image.unswizzled_size_bytes); - auto copies = UnswizzleImage(gpu_memory, gpu_addr, image.info, unswizzled_data); + auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, unswizzled_data); ConvertImage(unswizzled_data, image.info, mapped_span, copies); image.UploadMemory(staging, copies); } else { - const auto copies = UnswizzleImage(gpu_memory, gpu_addr, image.info, mapped_span); + const auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, mapped_span); image.UploadMemory(staging, copies); } } template ImageViewId TextureCache

    ::FindImageView(const TICEntry& config) { - if (!IsValidEntry(gpu_memory, config)) { + if (!IsValidEntry(*gpu_memory, config)) { return NULL_IMAGE_VIEW_ID; } - const auto [pair, is_new] = image_views.try_emplace(config); + const auto [pair, is_new] = channel_state->image_views.try_emplace(config); ImageViewId& image_view_id = pair->second; if (is_new) { image_view_id = CreateImageView(config); @@ -777,9 +794,9 @@ ImageId TextureCache

    ::FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_a template ImageId TextureCache

    ::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, RelaxedOptions options) { - std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); if (!cpu_addr) { - cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, CalculateGuestSizeInBytes(info)); + cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr, CalculateGuestSizeInBytes(info)); if (!cpu_addr) { return ImageId{}; } @@ -860,7 +877,7 @@ void TextureCache

    ::InvalidateScale(Image& image) { image.scale_tick = frame_tick + 1; } const std::span image_view_ids = image.image_view_ids; - auto& dirty = maxwell3d.dirty.flags; + auto& dirty = maxwell3d->dirty.flags; dirty[Dirty::RenderTargets] = true; dirty[Dirty::ZetaBuffer] = true; for (size_t rt = 0; rt < NUM_RT; ++rt) { @@ -880,12 +897,15 @@ void TextureCache

    ::InvalidateScale(Image& image) { } image.image_view_ids.clear(); image.image_view_infos.clear(); - if constexpr (ENABLE_VALIDATION) { - std::ranges::fill(graphics_image_view_ids, CORRUPT_ID); - std::ranges::fill(compute_image_view_ids, CORRUPT_ID); + for (size_t c : active_channel_ids) { + auto& channel_info = channel_storage[c]; + if constexpr (ENABLE_VALIDATION) { + std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID); + std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID); + } + channel_info.graphics_image_table.Invalidate(); + channel_info.compute_image_table.Invalidate(); } - graphics_image_table.Invalidate(); - compute_image_table.Invalidate(); has_deleted_images = true; } @@ -929,10 +949,10 @@ bool TextureCache

    ::ScaleDown(Image& image) { template ImageId TextureCache

    ::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr, RelaxedOptions options) { - std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); if (!cpu_addr) { const auto size = CalculateGuestSizeInBytes(info); - cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, size); + cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr, size); if (!cpu_addr) { const VAddr fake_addr = ~(1ULL << 40ULL) + virtual_invalid_space; virtual_invalid_space += Common::AlignUp(size, 32); @@ -1050,7 +1070,7 @@ ImageId TextureCache

    ::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr); Image& new_image = slot_images[new_image_id]; - if (!gpu_memory.IsContinousRange(new_image.gpu_addr, new_image.guest_size_bytes)) { + if (!gpu_memory->IsContinousRange(new_image.gpu_addr, new_image.guest_size_bytes)) { new_image.flags |= ImageFlagBits::Sparse; } @@ -1118,7 +1138,7 @@ ImageId TextureCache

    ::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA } template -typename TextureCache

    ::BlitImages TextureCache

    ::GetBlitImages( +std::optional::BlitImages> TextureCache

    ::GetBlitImages( const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Config& copy) { @@ -1139,6 +1159,20 @@ typename TextureCache

    ::BlitImages TextureCache

    ::GetBlitImages( has_deleted_images = false; src_id = FindImage(src_info, src_addr, try_options); dst_id = FindImage(dst_info, dst_addr, try_options); + if (!copy.must_accelerate) { + do { + if (!src_id && !dst_id) { + return std::nullopt; + } + if (src_id && True(slot_images[src_id].flags & ImageFlagBits::GpuModified)) { + break; + } + if (dst_id && True(slot_images[dst_id].flags & ImageFlagBits::GpuModified)) { + break; + } + return std::nullopt; + } while (false); + } const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr; if (src_image && src_image->info.num_samples > 1) { RelaxedOptions find_options{FIND_OPTIONS | RelaxedOptions::ForceBrokenViews}; @@ -1179,12 +1213,12 @@ typename TextureCache

    ::BlitImages TextureCache

    ::GetBlitImages( dst_id = FindOrInsertImage(dst_info, dst_addr, RelaxedOptions{}); } while (has_deleted_images); } - return BlitImages{ + return {BlitImages{ .dst_id = dst_id, .src_id = src_id, .dst_format = dst_info.format, .src_format = src_info.format, - }; + }}; } template @@ -1192,7 +1226,7 @@ SamplerId TextureCache

    ::FindSampler(const TSCEntry& config) { if (std::ranges::all_of(config.raw, [](u64 value) { return value == 0; })) { return NULL_SAMPLER_ID; } - const auto [pair, is_new] = samplers.try_emplace(config); + const auto [pair, is_new] = channel_state->samplers.try_emplace(config); if (is_new) { pair->second = slot_samplers.insert(runtime, config); } @@ -1201,7 +1235,7 @@ SamplerId TextureCache

    ::FindSampler(const TSCEntry& config) { template ImageViewId TextureCache

    ::FindColorBuffer(size_t index, bool is_clear) { - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (index >= regs.rt_control.count) { return ImageViewId{}; } @@ -1219,7 +1253,7 @@ ImageViewId TextureCache

    ::FindColorBuffer(size_t index, bool is_clear) { template ImageViewId TextureCache

    ::FindDepthBuffer(bool is_clear) { - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; if (!regs.zeta_enable) { return ImageViewId{}; } @@ -1316,11 +1350,17 @@ void TextureCache

    ::ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& f template template -void TextureCache

    ::ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func) { +void TextureCache

    ::ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, size_t size, + Func&& func) { using FuncReturn = typename std::invoke_result::type; static constexpr bool BOOL_BREAK = std::is_same_v; boost::container::small_vector images; - ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) { + auto storage_id = getStorageID(as_id); + if (!storage_id) { + return; + } + auto& gpu_page_table = gpu_page_table_storage[*storage_id]; + ForEachGPUPage(gpu_addr, size, [this, gpu_page_table, &images, gpu_addr, size, func](u64 page) { const auto it = gpu_page_table.find(page); if (it == gpu_page_table.end()) { if constexpr (BOOL_BREAK) { @@ -1403,9 +1443,9 @@ template void TextureCache

    ::ForEachSparseSegment(ImageBase& image, Func&& func) { using FuncReturn = typename std::invoke_result::type; static constexpr bool RETURNS_BOOL = std::is_same_v; - const auto segments = gpu_memory.GetSubmappedRange(image.gpu_addr, image.guest_size_bytes); + const auto segments = gpu_memory->GetSubmappedRange(image.gpu_addr, image.guest_size_bytes); for (const auto& [gpu_addr, size] : segments) { - std::optional cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + std::optional cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); ASSERT(cpu_addr); if constexpr (RETURNS_BOOL) { if (func(gpu_addr, *cpu_addr, size)) { @@ -1448,8 +1488,9 @@ void TextureCache

    ::RegisterImage(ImageId image_id) { } image.lru_index = lru_cache.Insert(image_id, frame_tick); - ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, - [this, image_id](u64 page) { gpu_page_table[page].push_back(image_id); }); + ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, image_id](u64 page) { + (*channel_state->gpu_page_table)[page].push_back(image_id); + }); if (False(image.flags & ImageFlagBits::Sparse)) { auto map_id = slot_map_views.insert(image.gpu_addr, image.cpu_addr, image.guest_size_bytes, image_id); @@ -1480,9 +1521,9 @@ void TextureCache

    ::UnregisterImage(ImageId image_id) { image.flags &= ~ImageFlagBits::BadOverlap; lru_cache.Free(image.lru_index); const auto& clear_page_table = - [this, image_id]( - u64 page, - std::unordered_map, IdentityHash>& selected_page_table) { + [image_id](u64 page, + std::unordered_map, Common::IdentityHash>& + selected_page_table) { const auto page_it = selected_page_table.find(page); if (page_it == selected_page_table.end()) { ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS); @@ -1497,8 +1538,9 @@ void TextureCache

    ::UnregisterImage(ImageId image_id) { } image_ids.erase(vector_it); }; - ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, - [this, &clear_page_table](u64 page) { clear_page_table(page, gpu_page_table); }); + ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, &clear_page_table](u64 page) { + clear_page_table(page, (*channel_state->gpu_page_table)); + }); if (False(image.flags & ImageFlagBits::Sparse)) { const auto map_id = image.map_view_id; ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) { @@ -1631,7 +1673,7 @@ void TextureCache

    ::DeleteImage(ImageId image_id, bool immediate_delete) { ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Image was not unregistered"); // Mark render targets as dirty - auto& dirty = maxwell3d.dirty.flags; + auto& dirty = maxwell3d->dirty.flags; dirty[Dirty::RenderTargets] = true; dirty[Dirty::ZetaBuffer] = true; for (size_t rt = 0; rt < NUM_RT; ++rt) { @@ -1681,24 +1723,30 @@ void TextureCache

    ::DeleteImage(ImageId image_id, bool immediate_delete) { if (alloc_images.empty()) { image_allocs_table.erase(alloc_it); } - if constexpr (ENABLE_VALIDATION) { - std::ranges::fill(graphics_image_view_ids, CORRUPT_ID); - std::ranges::fill(compute_image_view_ids, CORRUPT_ID); + for (size_t c : active_channel_ids) { + auto& channel_info = channel_storage[c]; + if constexpr (ENABLE_VALIDATION) { + std::ranges::fill(channel_info.graphics_image_view_ids, CORRUPT_ID); + std::ranges::fill(channel_info.compute_image_view_ids, CORRUPT_ID); + } + channel_info.graphics_image_table.Invalidate(); + channel_info.compute_image_table.Invalidate(); } - graphics_image_table.Invalidate(); - compute_image_table.Invalidate(); has_deleted_images = true; } template void TextureCache

    ::RemoveImageViewReferences(std::span removed_views) { - auto it = image_views.begin(); - while (it != image_views.end()) { - const auto found = std::ranges::find(removed_views, it->second); - if (found != removed_views.end()) { - it = image_views.erase(it); - } else { - ++it; + for (size_t c : active_channel_ids) { + auto& channel_info = channel_storage[c]; + auto it = channel_info.image_views.begin(); + while (it != channel_info.image_views.end()) { + const auto found = std::ranges::find(removed_views, it->second); + if (found != removed_views.end()) { + it = channel_info.image_views.erase(it); + } else { + ++it; + } } } } @@ -1729,6 +1777,7 @@ void TextureCache

    ::SynchronizeAliases(ImageId image_id) { boost::container::small_vector aliased_images; Image& image = slot_images[image_id]; bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled); + bool any_modified = True(image.flags & ImageFlagBits::GpuModified); u64 most_recent_tick = image.modification_tick; for (const AliasedImage& aliased : image.aliased_images) { ImageBase& aliased_image = slot_images[aliased.id]; @@ -1736,9 +1785,7 @@ void TextureCache

    ::SynchronizeAliases(ImageId image_id) { most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick); aliased_images.push_back(&aliased); any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled); - if (True(aliased_image.flags & ImageFlagBits::GpuModified)) { - image.flags |= ImageFlagBits::GpuModified; - } + any_modified |= True(aliased_image.flags & ImageFlagBits::GpuModified); } } if (aliased_images.empty()) { @@ -1753,6 +1800,9 @@ void TextureCache

    ::SynchronizeAliases(ImageId image_id) { } } image.modification_tick = most_recent_tick; + if (any_modified) { + image.flags |= ImageFlagBits::GpuModified; + } std::ranges::sort(aliased_images, [this](const AliasedImage* lhs, const AliasedImage* rhs) { const ImageBase& lhs_image = slot_images[lhs->id]; const ImageBase& rhs_image = slot_images[rhs->id]; @@ -1931,6 +1981,7 @@ std::pair TextureCache

    ::RenderTargetFromImage( .color_buffer_ids = {color_view_id}, .depth_buffer_id = depth_view_id, .size = {extent.width >> samples_x, extent.height >> samples_y}, + .is_rescaled = is_rescaled, }); return {framebuffer_id, view_id}; } @@ -1943,13 +1994,13 @@ bool TextureCache

    ::IsFullClear(ImageViewId id) { const ImageViewBase& image_view = slot_image_views[id]; const ImageBase& image = slot_images[image_view.image_id]; const Extent3D size = image_view.size; - const auto& regs = maxwell3d.regs; + const auto& regs = maxwell3d->regs; const auto& scissor = regs.scissor_test[0]; if (image.info.resources.levels > 1 || image.info.resources.layers > 1) { // Images with multiple resources can't be cleared in a single call return false; } - if (regs.clear_flags.scissor == 0) { + if (regs.clear_control.use_scissor == 0) { // If scissor testing is disabled, the clear is always full return true; } @@ -1958,4 +2009,19 @@ bool TextureCache

    ::IsFullClear(ImageViewId id) { scissor.max_y >= size.height; } +template +void TextureCache

    ::CreateChannel(struct Tegra::Control::ChannelState& channel) { + VideoCommon::ChannelSetupCaches::CreateChannel(channel); + const auto it = channel_map.find(channel.bind_id); + auto* this_state = &channel_storage[it->second]; + const auto& this_as_ref = address_spaces[channel.memory_manager->GetID()]; + this_state->gpu_page_table = &gpu_page_table_storage[this_as_ref.storage_id]; +} + +/// Bind a channel for execution. +template +void TextureCache

    ::OnGPUASRegister([[maybe_unused]] size_t map_id) { + gpu_page_table_storage.emplace_back(); +} + } // namespace VideoCommon diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 7e6c6ce..587339a 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -1,8 +1,10 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once +#include +#include #include #include #include @@ -11,9 +13,12 @@ #include #include "common/common_types.h" +#include "common/hash.h" #include "common/literals.h" #include "common/lru_cache.h" +#include "common/polyfill_ranges.h" #include "video_core/compatible_formats.h" +#include "video_core/control/channel_state_cache.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/engines/fermi_2d.h" #include "video_core/surface.h" @@ -26,6 +31,10 @@ #include "video_core/texture_cache/types.h" #include "video_core/textures/texture.h" +namespace Tegra::Control { +struct ChannelState; +} + namespace VideoCommon { using Tegra::Texture::SwizzleSource; @@ -44,8 +53,33 @@ struct ImageViewInOut { ImageViewId id{}; }; +using TextureCacheGPUMap = std::unordered_map, Common::IdentityHash>; + +class TextureCacheChannelInfo : public ChannelInfo { +public: + TextureCacheChannelInfo() = delete; + TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept; + TextureCacheChannelInfo(const TextureCacheChannelInfo& state) = delete; + TextureCacheChannelInfo& operator=(const TextureCacheChannelInfo&) = delete; + + DescriptorTable graphics_image_table{gpu_memory}; + DescriptorTable graphics_sampler_table{gpu_memory}; + std::vector graphics_sampler_ids; + std::vector graphics_image_view_ids; + + DescriptorTable compute_image_table{gpu_memory}; + DescriptorTable compute_sampler_table{gpu_memory}; + std::vector compute_sampler_ids; + std::vector compute_image_view_ids; + + std::unordered_map image_views; + std::unordered_map samplers; + + TextureCacheGPUMap* gpu_page_table; +}; + template -class TextureCache { +class TextureCache : public VideoCommon::ChannelSetupCaches { /// Address shift for caching images into a hash table static constexpr u64 YUZU_PAGEBITS = 20; @@ -58,6 +92,8 @@ class TextureCache { /// True when the API can provide info about the memory of the device. static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; + static constexpr size_t UNSET_CHANNEL{std::numeric_limits::max()}; + static constexpr s64 TARGET_THRESHOLD = 4_GiB; static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB; static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB; @@ -77,16 +113,8 @@ class TextureCache { PixelFormat src_format; }; - template - struct IdentityHash { - [[nodiscard]] size_t operator()(T value) const noexcept { - return static_cast(value); - } - }; - public: - explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&, Tegra::Engines::Maxwell3D&, - Tegra::Engines::KeplerCompute&, Tegra::MemoryManager&); + explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&); /// Notify the cache that a new frame has been queued void TickFrame(); @@ -142,10 +170,10 @@ public: void UnmapMemory(VAddr cpu_addr, size_t size); /// Remove images in a region - void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size); + void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size); /// Blit an image with the given parameters - void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, + bool BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Config& copy); @@ -171,6 +199,9 @@ public: [[nodiscard]] bool IsRescaling(const ImageViewBase& image_view) const noexcept; + /// Create channel state. + void CreateChannel(Tegra::Control::ChannelState& channel) final override; + std::mutex mutex; private: @@ -205,6 +236,8 @@ private: } } + void OnGPUASRegister(size_t map_id) final override; + /// Runs the Garbage Collector. void RunGarbageCollector(); @@ -251,9 +284,9 @@ private: [[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr); /// Return a blit image pair from the given guest blit parameters - [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst, - const Tegra::Engines::Fermi2D::Surface& src, - const Tegra::Engines::Fermi2D::Config& copy); + [[nodiscard]] std::optional GetBlitImages( + const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src, + const Tegra::Engines::Fermi2D::Config& copy); /// Find or create a sampler from a guest descriptor sampler [[nodiscard]] SamplerId FindSampler(const TSCEntry& config); @@ -273,7 +306,7 @@ private: void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func); template - void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func); + void ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, size_t size, Func&& func); template void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func); @@ -338,31 +371,16 @@ private: u64 GetScaledImageSizeBytes(ImageBase& image); Runtime& runtime; + VideoCore::RasterizerInterface& rasterizer; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; - Tegra::MemoryManager& gpu_memory; - - DescriptorTable graphics_image_table{gpu_memory}; - DescriptorTable graphics_sampler_table{gpu_memory}; - std::vector graphics_sampler_ids; - std::vector graphics_image_view_ids; - - DescriptorTable compute_image_table{gpu_memory}; - DescriptorTable compute_sampler_table{gpu_memory}; - std::vector compute_sampler_ids; - std::vector compute_image_view_ids; + std::deque gpu_page_table_storage; RenderTargets render_targets; - std::unordered_map image_views; - std::unordered_map samplers; std::unordered_map framebuffers; - std::unordered_map, IdentityHash> page_table; - std::unordered_map, IdentityHash> gpu_page_table; - std::unordered_map, IdentityHash> sparse_page_table; - + std::unordered_map, Common::IdentityHash> page_table; + std::unordered_map, Common::IdentityHash> sparse_page_table; std::unordered_map> sparse_views; VAddr virtual_invalid_space{}; diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 1820823..e8c908b 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -516,8 +516,6 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size); const u32 host_bytes_per_layer = num_blocks_per_layer * bytes_per_block; - UNIMPLEMENTED_IF(info.tile_width_spacing > 0); - UNIMPLEMENTED_IF(copy.image_offset.x != 0); UNIMPLEMENTED_IF(copy.image_offset.y != 0); UNIMPLEMENTED_IF(copy.image_offset.z != 0); @@ -755,7 +753,7 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config if (address == 0) { return false; } - if (address > (1ULL << 48)) { + if (address >= (1ULL << 40)) { return false; } if (gpu_memory.GpuToCpuAddress(address).has_value()) { diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index b159494..e8d7c78 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp @@ -15,6 +15,7 @@ #include "common/alignment.h" #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "common/thread_worker.h" #include "video_core/textures/astc.h" @@ -1413,7 +1414,7 @@ static void FillVoidExtentLDR(InputBitStream& strm, std::span outBuf, u32 b static void FillError(std::span outBuf, u32 blockWidth, u32 blockHeight) { for (u32 j = 0; j < blockHeight; j++) { for (u32 i = 0; i < blockWidth; i++) { - outBuf[j * blockWidth + i] = 0xFFFF00FF; + outBuf[j * blockWidth + i] = 0x00000000; } } } @@ -1656,13 +1657,13 @@ void Decompress(std::span data, uint32_t width, uint32_t height, const u32 cols = Common::DivideUp(width, block_width); Common::ThreadWorker workers{std::max(std::thread::hardware_concurrency(), 2U) / 2, - "yuzu:ASTCDecompress"}; + "ASTCDecompress"}; for (u32 z = 0; z < depth; ++z) { const u32 depth_offset = z * height * width * 4; for (u32 y_index = 0; y_index < rows; ++y_index) { - auto decompress_stride = [data, width, height, depth, block_width, block_height, output, - rows, cols, z, depth_offset, y_index] { + auto decompress_stride = [data, width, height, block_width, block_height, output, rows, + cols, z, depth_offset, y_index] { const u32 y = y_index * block_height; for (u32 x_index = 0; x_index < cols; ++x_index) { const u32 block_index = (z * rows * cols) + (y_index * cols) + x_index; diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 913f8eb..59120cd 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -21,7 +21,7 @@ constexpr u32 pdep(u32 value) { u32 m = mask; for (u32 bit = 1; m; bit += bit) { if (value & bit) - result |= m & -m; + result |= m & (~m + 1); m &= m - 1; } return result; @@ -35,7 +35,7 @@ void incrpdep(u32& value) { template void SwizzleImpl(std::span output, std::span input, u32 width, u32 height, u32 depth, - u32 block_height, u32 block_depth, u32 stride_alignment) { + u32 block_height, u32 block_depth, u32 stride) { // The origin of the transformation can be configured here, leave it as zero as the current API // doesn't expose it. static constexpr u32 origin_x = 0; @@ -45,7 +45,6 @@ void SwizzleImpl(std::span output, std::span input, u32 width, u32 // We can configure here a custom pitch // As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch. const u32 pitch = width * BYTES_PER_PIXEL; - const u32 stride = Common::AlignUpLog2(width, stride_alignment) * BYTES_PER_PIXEL; const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); @@ -89,6 +88,69 @@ void SwizzleImpl(std::span output, std::span input, u32 width, u32 } } +template +void SwizzleSubrectImpl(std::span output, std::span input, u32 width, u32 height, + u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, u32 num_lines, + u32 block_height, u32 block_depth, u32 pitch_linear) { + // The origin of the transformation can be configured here, leave it as zero as the current API + // doesn't expose it. + static constexpr u32 origin_z = 0; + + // We can configure here a custom pitch + // As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch. + const u32 pitch = pitch_linear; + const u32 stride = Common::AlignUpLog2(width * BYTES_PER_PIXEL, GOB_SIZE_X_SHIFT); + + const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); + const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); + const u32 slice_size = + Common::DivCeilLog2(height, block_height + GOB_SIZE_Y_SHIFT) * block_size; + + const u32 block_height_mask = (1U << block_height) - 1; + const u32 block_depth_mask = (1U << block_depth) - 1; + const u32 x_shift = GOB_SIZE_SHIFT + block_height + block_depth; + + u32 unprocessed_lines = num_lines; + u32 extent_y = std::min(num_lines, height - origin_y); + + for (u32 slice = 0; slice < depth; ++slice) { + const u32 z = slice + origin_z; + const u32 offset_z = (z >> block_depth) * slice_size + + ((z & block_depth_mask) << (GOB_SIZE_SHIFT + block_height)); + const u32 lines_in_y = std::min(unprocessed_lines, extent_y); + for (u32 line = 0; line < lines_in_y; ++line) { + const u32 y = line + origin_y; + const u32 swizzled_y = pdep(y); + + const u32 block_y = y >> GOB_SIZE_Y_SHIFT; + const u32 offset_y = (block_y >> block_height) * block_size + + ((block_y & block_height_mask) << GOB_SIZE_SHIFT); + + u32 swizzled_x = pdep(origin_x * BYTES_PER_PIXEL); + for (u32 column = 0; column < extent_x; + ++column, incrpdep(swizzled_x)) { + const u32 x = (column + origin_x) * BYTES_PER_PIXEL; + const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; + + const u32 base_swizzled_offset = offset_z + offset_y + offset_x; + const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); + + const u32 unswizzled_offset = + slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; + + u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset]; + const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset]; + + std::memcpy(dst, src, BYTES_PER_PIXEL); + } + } + unprocessed_lines -= lines_in_y; + if (unprocessed_lines == 0) { + return; + } + } +} + template void Swizzle(std::span output, std::span input, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { @@ -108,125 +170,43 @@ void Swizzle(std::span output, std::span input, u32 bytes_per_pixe #undef BPP_CASE default: ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); + break; } } -template -void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, - u8* swizzled_data, const u8* unswizzled_data, u32 block_height_bit, - u32 offset_x, u32 offset_y) { - const u32 block_height = 1U << block_height_bit; - const u32 image_width_in_gobs = - (swizzled_width * BYTES_PER_PIXEL + (GOB_SIZE_X - 1)) / GOB_SIZE_X; - for (u32 line = 0; line < subrect_height; ++line) { - const u32 dst_y = line + offset_y; - const u32 gob_address_y = - (dst_y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs + - ((dst_y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; - - const u32 swizzled_y = pdep(dst_y); - u32 swizzled_x = pdep(offset_x * BYTES_PER_PIXEL); - for (u32 x = 0; x < subrect_width; - ++x, incrpdep(swizzled_x)) { - const u32 dst_x = x + offset_x; - const u32 gob_address = - gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height; - const u32 swizzled_offset = gob_address + (swizzled_x | swizzled_y); - const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL; - - const u8* const source_line = unswizzled_data + unswizzled_offset; - u8* const dest_addr = swizzled_data + swizzled_offset; - std::memcpy(dest_addr, source_line, BYTES_PER_PIXEL); - } - } -} - -template -void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 block_height, - u32 origin_x, u32 origin_y, u8* output, const u8* input) { - const u32 stride = width * BYTES_PER_PIXEL; - const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; - const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height); - - const u32 block_height_mask = (1U << block_height) - 1; - const u32 x_shift = GOB_SIZE_SHIFT + block_height; - - for (u32 line = 0; line < line_count; ++line) { - const u32 src_y = line + origin_y; - const u32 swizzled_y = pdep(src_y); - - const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT; - const u32 src_offset_y = (block_y >> block_height) * block_size + - ((block_y & block_height_mask) << GOB_SIZE_SHIFT); - - u32 swizzled_x = pdep(origin_x * BYTES_PER_PIXEL); - for (u32 column = 0; column < line_length_in; - ++column, incrpdep(swizzled_x)) { - const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL; - const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift; - - const u32 swizzled_offset = src_offset_y + src_offset_x + (swizzled_x | swizzled_y); - const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL; - - std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL); - } - } -} - -template -void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, - u32 block_height, u32 block_depth, u32 origin_x, u32 origin_y, u8* output, - const u8* input) { - UNIMPLEMENTED_IF(origin_x > 0); - UNIMPLEMENTED_IF(origin_y > 0); - - const u32 stride = width * BYTES_PER_PIXEL; - const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; - const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); - - const u32 block_height_mask = (1U << block_height) - 1; - const u32 x_shift = static_cast(GOB_SIZE_SHIFT) + block_height + block_depth; - - for (u32 line = 0; line < line_count; ++line) { - const u32 swizzled_y = pdep(line); - const u32 block_y = line / GOB_SIZE_Y; - const u32 dst_offset_y = - (block_y >> block_height) * block_size + (block_y & block_height_mask) * GOB_SIZE; - - u32 swizzled_x = 0; - for (u32 x = 0; x < line_length_in; ++x, incrpdep(swizzled_x)) { - const u32 dst_offset = - ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + (swizzled_x | swizzled_y); - const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch; - std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL); - } - } -} } // Anonymous namespace void UnswizzleTexture(std::span output, std::span input, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { + const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel; + const u32 new_bpp = std::min(4U, static_cast(std::countr_zero(width * bytes_per_pixel))); + width = (width * bytes_per_pixel) >> new_bpp; + bytes_per_pixel = 1U << new_bpp; Swizzle(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, - stride_alignment); + stride); } void SwizzleTexture(std::span output, std::span input, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { + const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel; + const u32 new_bpp = std::min(4U, static_cast(std::countr_zero(width * bytes_per_pixel))); + width = (width * bytes_per_pixel) >> new_bpp; + bytes_per_pixel = 1U << new_bpp; Swizzle(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, - stride_alignment); + stride); } -void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, - u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, - u32 block_height_bit, u32 offset_x, u32 offset_y) { +void SwizzleSubrect(std::span output, std::span input, u32 bytes_per_pixel, u32 width, + u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, u32 extent_y, + u32 block_height, u32 block_depth, u32 pitch_linear) { switch (bytes_per_pixel) { #define BPP_CASE(x) \ case x: \ - return SwizzleSubrect(subrect_width, subrect_height, source_pitch, swizzled_width, \ - swizzled_data, unswizzled_data, block_height_bit, offset_x, \ - offset_y); + return SwizzleSubrectImpl(output, input, width, height, depth, origin_x, \ + origin_y, extent_x, extent_y, block_height, \ + block_depth, pitch_linear); BPP_CASE(1) BPP_CASE(2) BPP_CASE(3) @@ -238,16 +218,19 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 #undef BPP_CASE default: ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); + break; } } -void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, - u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) { +void UnswizzleSubrect(std::span output, std::span input, u32 bytes_per_pixel, + u32 width, u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, + u32 extent_y, u32 block_height, u32 block_depth, u32 pitch_linear) { switch (bytes_per_pixel) { #define BPP_CASE(x) \ case x: \ - return UnswizzleSubrect(line_length_in, line_count, pitch, width, block_height, \ - origin_x, origin_y, output, input); + return SwizzleSubrectImpl(output, input, width, height, depth, origin_x, \ + origin_y, extent_x, extent_y, block_height, \ + block_depth, pitch_linear); BPP_CASE(1) BPP_CASE(2) BPP_CASE(3) @@ -259,55 +242,7 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, #undef BPP_CASE default: ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); - } -} - -void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, - u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x, - u32 origin_y, u8* output, const u8* input) { - switch (bytes_per_pixel) { -#define BPP_CASE(x) \ - case x: \ - return SwizzleSliceToVoxel(line_length_in, line_count, pitch, width, height, \ - block_height, block_depth, origin_x, origin_y, output, \ - input); - BPP_CASE(1) - BPP_CASE(2) - BPP_CASE(3) - BPP_CASE(4) - BPP_CASE(6) - BPP_CASE(8) - BPP_CASE(12) - BPP_CASE(16) -#undef BPP_CASE - default: - ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); - } -} - -void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, - const u32 block_height_bit, const std::size_t copy_size, const u8* source_data, - u8* swizzle_data) { - const u32 block_height = 1U << block_height_bit; - const u32 image_width_in_gobs{(width + GOB_SIZE_X - 1) / GOB_SIZE_X}; - std::size_t count = 0; - for (std::size_t y = dst_y; y < height && count < copy_size; ++y) { - const std::size_t gob_address_y = - (y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs + - ((y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; - const u32 swizzled_y = pdep(static_cast(y)); - u32 swizzled_x = pdep(dst_x); - for (std::size_t x = dst_x; x < width && count < copy_size; - ++x, incrpdep(swizzled_x)) { - const std::size_t gob_address = - gob_address_y + (x / GOB_SIZE_X) * GOB_SIZE * block_height; - const std::size_t swizzled_offset = gob_address + (swizzled_x | swizzled_y); - const u8* source_line = source_data + count; - u8* dest_addr = swizzle_data + swizzled_offset; - count++; - - *dest_addr = *source_line; - } + break; } } diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index 31a1170..e704076 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -40,7 +40,6 @@ constexpr SwizzleTable MakeSwizzleTable() { } return table; } -constexpr SwizzleTable SWIZZLE_TABLE = MakeSwizzleTable(); /// Unswizzles a block linear texture into linear memory. void UnswizzleTexture(std::span output, std::span input, u32 bytes_per_pixel, @@ -57,34 +56,14 @@ std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height u32 block_height, u32 block_depth); /// Copies an untiled subrectangle into a tiled surface. -void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, - u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, - u32 block_height_bit, u32 offset_x, u32 offset_y); +void SwizzleSubrect(std::span output, std::span input, u32 bytes_per_pixel, u32 width, + u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, u32 extent_y, + u32 block_height, u32 block_depth, u32 pitch_linear); /// Copies a tiled subrectangle into a linear surface. -void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, - u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input); - -/// @brief Swizzles a 2D array of pixels into a 3D texture -/// @param line_length_in Number of pixels per line -/// @param line_count Number of lines -/// @param pitch Number of bytes per line -/// @param width Width of the swizzled texture -/// @param height Height of the swizzled texture -/// @param bytes_per_pixel Number of bytes used per pixel -/// @param block_height Block height shift -/// @param block_depth Block depth shift -/// @param origin_x Column offset in pixels of the swizzled texture -/// @param origin_y Row offset in pixels of the swizzled texture -/// @param output Pointer to the pixels of the swizzled texture -/// @param input Pointer to the 2D array of pixels used as input -/// @pre input and output points to an array large enough to hold the number of bytes used -void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, - u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x, - u32 origin_y, u8* output, const u8* input); - -void SwizzleKepler(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height, - std::size_t copy_size, const u8* source_data, u8* swizzle_data); +void UnswizzleSubrect(std::span output, std::span input, u32 bytes_per_pixel, + u32 width, u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, + u32 extent_y, u32 block_height, u32 block_depth, u32 pitch_linear); /// Obtains the offset of the gob for positions 'dst_x' & 'dst_y' u64 GetGOBOffset(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height, diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp index b8327c8..26649ae 100644 --- a/src/video_core/textures/texture.cpp +++ b/src/video_core/textures/texture.cpp @@ -64,10 +64,11 @@ float TSCEntry::MaxAnisotropy() const noexcept { return 1.0f; } const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue(); - u32 added_anisotropic{}; + s32 added_anisotropic{}; if (anisotropic_settings == 0) { added_anisotropic = Settings::values.resolution_info.up_scale >> Settings::values.resolution_info.down_shift; + added_anisotropic = std::max(added_anisotropic - 1, 0); } else { added_anisotropic = Settings::values.max_anisotropy.GetValue() - 1U; } diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp index 7e60598..1555993 100644 --- a/src/video_core/transform_feedback.cpp +++ b/src/video_core/transform_feedback.cpp @@ -7,6 +7,7 @@ #include "common/alignment.h" #include "common/assert.h" +#include "common/polyfill_ranges.h" #include "shader_recompiler/shader_info.h" #include "video_core/transform_feedback.h" @@ -15,51 +16,51 @@ namespace VideoCommon { std::vector MakeTransformFeedbackVaryings( const TransformFeedbackState& state) { static constexpr std::array VECTORS{ - 28, // gl_Position - 32, // Generic 0 - 36, // Generic 1 - 40, // Generic 2 - 44, // Generic 3 - 48, // Generic 4 - 52, // Generic 5 - 56, // Generic 6 - 60, // Generic 7 - 64, // Generic 8 - 68, // Generic 9 - 72, // Generic 10 - 76, // Generic 11 - 80, // Generic 12 - 84, // Generic 13 - 88, // Generic 14 - 92, // Generic 15 - 96, // Generic 16 - 100, // Generic 17 - 104, // Generic 18 - 108, // Generic 19 - 112, // Generic 20 - 116, // Generic 21 - 120, // Generic 22 - 124, // Generic 23 - 128, // Generic 24 - 132, // Generic 25 - 136, // Generic 26 - 140, // Generic 27 - 144, // Generic 28 - 148, // Generic 29 - 152, // Generic 30 - 156, // Generic 31 - 160, // gl_FrontColor - 164, // gl_FrontSecondaryColor - 160, // gl_BackColor - 164, // gl_BackSecondaryColor - 192, // gl_TexCoord[0] - 196, // gl_TexCoord[1] - 200, // gl_TexCoord[2] - 204, // gl_TexCoord[3] - 208, // gl_TexCoord[4] - 212, // gl_TexCoord[5] - 216, // gl_TexCoord[6] - 220, // gl_TexCoord[7] + 28U, // gl_Position + 32U, // Generic 0 + 36U, // Generic 1 + 40U, // Generic 2 + 44U, // Generic 3 + 48U, // Generic 4 + 52U, // Generic 5 + 56U, // Generic 6 + 60U, // Generic 7 + 64U, // Generic 8 + 68U, // Generic 9 + 72U, // Generic 10 + 76U, // Generic 11 + 80U, // Generic 12 + 84U, // Generic 13 + 88U, // Generic 14 + 92U, // Generic 15 + 96U, // Generic 16 + 100U, // Generic 17 + 104U, // Generic 18 + 108U, // Generic 19 + 112U, // Generic 20 + 116U, // Generic 21 + 120U, // Generic 22 + 124U, // Generic 23 + 128U, // Generic 24 + 132U, // Generic 25 + 136U, // Generic 26 + 140U, // Generic 27 + 144U, // Generic 28 + 148U, // Generic 29 + 152U, // Generic 30 + 156U, // Generic 31 + 160U, // gl_FrontColor + 164U, // gl_FrontSecondaryColor + 160U, // gl_BackColor + 164U, // gl_BackSecondaryColor + 192U, // gl_TexCoord[0] + 196U, // gl_TexCoord[1] + 200U, // gl_TexCoord[2] + 204U, // gl_TexCoord[3] + 208U, // gl_TexCoord[4] + 212U, // gl_TexCoord[5] + 216U, // gl_TexCoord[6] + 220U, // gl_TexCoord[7] }; std::vector xfb(256); for (size_t buffer = 0; buffer < state.layouts.size(); ++buffer) { @@ -68,8 +69,20 @@ std::vector MakeTransformFeedbackVaryings( const u32 varying_count = layout.varying_count; u32 highest = 0; for (u32 offset = 0; offset < varying_count; ++offset) { - const u32 base_offset = offset; - const u8 location = locations[offset]; + const auto get_attribute = [&locations](u32 index) -> u32 { + switch (index % 4) { + case 0: + return locations[index / 4].attribute0.Value(); + case 1: + return locations[index / 4].attribute1.Value(); + case 2: + return locations[index / 4].attribute2.Value(); + case 3: + return locations[index / 4].attribute3.Value(); + } + UNREACHABLE(); + return 0; + }; UNIMPLEMENTED_IF_MSG(layout.stream != 0, "Stream is not zero: {}", layout.stream); Shader::TransformFeedbackVarying varying{ @@ -78,16 +91,18 @@ std::vector MakeTransformFeedbackVaryings( .offset = offset * 4, .components = 1, }; - if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) { - UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB"); + const u32 base_offset = offset; + const auto attribute{get_attribute(offset)}; + if (std::ranges::find(VECTORS, Common::AlignDown(attribute, 4)) != VECTORS.end()) { + UNIMPLEMENTED_IF_MSG(attribute % 4 != 0, "Unaligned TFB {}", attribute); - const u8 base_index = location / 4; - while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) { + const auto base_index = attribute / 4; + while (offset + 1 < varying_count && base_index == get_attribute(offset + 1) / 4) { ++offset; ++varying.components; } } - xfb[location] = varying; + xfb[attribute] = varying; highest = std::max(highest, (base_offset + varying.components) * 4); } UNIMPLEMENTED_IF(highest != layout.stride); diff --git a/src/video_core/transform_feedback.h b/src/video_core/transform_feedback.h index a519adb..d13eb16 100644 --- a/src/video_core/transform_feedback.h +++ b/src/video_core/transform_feedback.h @@ -19,7 +19,8 @@ struct TransformFeedbackState { u32 stride; }; std::array layouts; - std::array, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers> + std::array, + Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers> varyings; }; diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 04ac4af..fedb4a7 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -7,6 +7,7 @@ #include "common/settings.h" #include "core/core.h" #include "video_core/renderer_base.h" +#include "video_core/renderer_null/renderer_null.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/video_core.h" @@ -26,6 +27,9 @@ std::unique_ptr CreateRenderer( case Settings::RendererBackend::Vulkan: return std::make_unique(telemetry_session, emu_window, cpu_memory, gpu, std::move(context)); + case Settings::RendererBackend::Null: + return std::make_unique(emu_window, cpu_memory, gpu, + std::move(context)); default: return nullptr; } diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp index 7364740..10a001b 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp +++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp @@ -16,6 +16,8 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0]) case 0xe8616bf2u: // Bound VkDescriptorSet 0x0[] was destroyed. Likely push_descriptor related + case 0x1608dec0u: // Image layout in vkUpdateDescriptorSet doesn't match descriptor use + case 0x55362756u: // Descriptor binding and framebuffer attachment overlap return VK_FALSE; default: break; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index ddecfca..c4d3168 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/literals.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "video_core/vulkan_common/nsight_aftermath_tracker.h" #include "video_core/vulkan_common/vulkan_device.h" @@ -74,23 +75,8 @@ enum class NvidiaArchitecture { }; constexpr std::array REQUIRED_EXTENSIONS{ - VK_KHR_MAINTENANCE1_EXTENSION_NAME, - VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, - VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME, - VK_KHR_16BIT_STORAGE_EXTENSION_NAME, - VK_KHR_8BIT_STORAGE_EXTENSION_NAME, - VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, - VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, - VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, - VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, - VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME, - VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, - VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME, - VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, - VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, - VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME, #ifdef _WIN32 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, #endif @@ -99,6 +85,19 @@ constexpr std::array REQUIRED_EXTENSIONS{ #endif }; +constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_2{ + VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, + VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, + VK_KHR_8BIT_STORAGE_EXTENSION_NAME, + VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME, + VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, + VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, +}; + +constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_3{ + VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME, +}; + template void SetNext(void**& next, T& data) { *next = &data; @@ -308,10 +307,10 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{}; shading_rate_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; - VkPhysicalDeviceProperties2KHR physical_properties{}; - physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; + VkPhysicalDeviceProperties2 physical_properties{}; + physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; physical_properties.pNext = &shading_rate_props; - physical.GetProperties2KHR(physical_properties); + physical.GetProperties2(physical_properties); if (shading_rate_props.primitiveFragmentShadingRateWithMultipleViewports) { // Only Ampere and newer support this feature return NvidiaArchitecture::AmpereOrNewer; @@ -327,7 +326,8 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, const vk::InstanceDispatch& dld_) : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, - supported_extensions{GetSupportedExtensions(physical)}, + instance_version{properties.apiVersion}, supported_extensions{GetSupportedExtensions( + physical)}, format_properties(GetFormatProperties(physical)) { CheckSuitability(surface != nullptr); SetupFamilies(surface); @@ -401,15 +401,15 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const void* first_next = &features2; void** next = &features2.pNext; - VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR, + VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, .pNext = nullptr, .timelineSemaphore = true, }; SetNext(next, timeline_semaphore); - VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, + VkPhysicalDevice16BitStorageFeatures bit16_storage{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, .pNext = nullptr, .storageBuffer16BitAccess = true, .uniformAndStorageBuffer16BitAccess = true, @@ -418,10 +418,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR }; SetNext(next, bit16_storage); - VkPhysicalDevice8BitStorageFeaturesKHR bit8_storage{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR, + VkPhysicalDevice8BitStorageFeatures bit8_storage{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, .pNext = nullptr, - .storageBuffer8BitAccess = false, + .storageBuffer8BitAccess = true, .uniformAndStorageBuffer8BitAccess = true, .storagePushConstant8 = false, }; @@ -436,32 +436,39 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR }; SetNext(next, robustness2); - VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT, + VkPhysicalDeviceHostQueryResetFeatures host_query_reset{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES, .pNext = nullptr, .hostQueryReset = true, }; SetNext(next, host_query_reset); - VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR, + VkPhysicalDeviceVariablePointerFeatures variable_pointers{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, .pNext = nullptr, .variablePointersStorageBuffer = VK_TRUE, .variablePointers = VK_TRUE, }; SetNext(next, variable_pointers); - VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT, + VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES, .pNext = nullptr, .shaderDemoteToHelperInvocation = true, }; SetNext(next, demote); - VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8; + VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES, + .pNext = nullptr, + .shaderDrawParameters = true, + }; + SetNext(next, draw_parameters); + + VkPhysicalDeviceShaderFloat16Int8Features float16_int8; if (is_int8_supported || is_float16_supported) { float16_int8 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, .pNext = nullptr, .shaderFloat16 = is_float16_supported, .shaderInt8 = is_int8_supported, @@ -487,10 +494,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_INFO(Render_Vulkan, "Device doesn't support passthrough geometry shaders"); } - VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR std430_layout; + VkPhysicalDeviceUniformBufferStandardLayoutFeatures std430_layout; if (khr_uniform_buffer_standard_layout) { std430_layout = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES, .pNext = nullptr, .uniformBufferStandardLayout = true, }; @@ -608,10 +615,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_INFO(Render_Vulkan, "Device doesn't support vertex input dynamic state"); } - VkPhysicalDeviceShaderAtomicInt64FeaturesKHR atomic_int64; + VkPhysicalDeviceShaderAtomicInt64Features atomic_int64; if (ext_shader_atomic_int64) { atomic_int64 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES, .pNext = nullptr, .shaderBufferInt64Atomics = VK_TRUE, .shaderSharedInt64Atomics = VK_TRUE, @@ -653,6 +660,16 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); } + VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features; + if (ext_depth_clip_control) { + depth_clip_control_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT, + .pNext = nullptr, + .depthClipControl = VK_TRUE, + }; + SetNext(next, depth_clip_control_features); + } + VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv; if (Settings::values.enable_nsight_aftermath && nv_device_diagnostics_config) { nsight_aftermath_tracker = std::make_unique(); @@ -896,28 +913,51 @@ std::string Device::GetDriverName() const { } } +static std::vector ExtensionsRequiredForInstanceVersion(u32 available_version) { + std::vector extensions{REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()}; + + if (available_version < VK_API_VERSION_1_2) { + extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_2.begin(), + REQUIRED_EXTENSIONS_BEFORE_1_2.end()); + } + + if (available_version < VK_API_VERSION_1_3) { + extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_3.begin(), + REQUIRED_EXTENSIONS_BEFORE_1_3.end()); + } + + return extensions; +} + void Device::CheckSuitability(bool requires_swapchain) const { - std::bitset available_extensions; - bool has_swapchain = false; - for (const VkExtensionProperties& property : physical.EnumerateDeviceExtensionProperties()) { - const std::string_view name{property.extensionName}; - for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { - if (available_extensions[i]) { - continue; - } - available_extensions[i] = name == REQUIRED_EXTENSIONS[i]; - } - has_swapchain = has_swapchain || name == VK_KHR_SWAPCHAIN_EXTENSION_NAME; + std::vector required_extensions = + ExtensionsRequiredForInstanceVersion(instance_version); + std::vector available_extensions; + + if (requires_swapchain) { + required_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); } - for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { - if (available_extensions[i]) { - continue; - } - LOG_ERROR(Render_Vulkan, "Missing required extension: {}", REQUIRED_EXTENSIONS[i]); - throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); + + auto extension_properties = physical.EnumerateDeviceExtensionProperties(); + + for (const VkExtensionProperties& property : extension_properties) { + available_extensions.push_back(property.extensionName); } - if (requires_swapchain && !has_swapchain) { - LOG_ERROR(Render_Vulkan, "Missing required extension: VK_KHR_swapchain"); + + bool has_all_required_extensions = true; + for (const char* requirement_name : required_extensions) { + const bool found = + std::ranges::any_of(available_extensions, [&](const char* extension_name) { + return std::strcmp(requirement_name, extension_name) == 0; + }); + + if (!found) { + LOG_ERROR(Render_Vulkan, "Missing required extension: {}", requirement_name); + has_all_required_extensions = false; + } + } + + if (!has_all_required_extensions) { throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); } @@ -940,27 +980,46 @@ void Device::CheckSuitability(bool requires_swapchain) const { throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); } } - VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{}; - demote.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT; + VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{}; + demote.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES; demote.pNext = nullptr; - VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers{}; - variable_pointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR; + VkPhysicalDeviceVariablePointerFeatures variable_pointers{}; + variable_pointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES; variable_pointers.pNext = &demote; VkPhysicalDeviceRobustness2FeaturesEXT robustness2{}; robustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT; robustness2.pNext = &variable_pointers; - VkPhysicalDeviceFeatures2KHR features2{}; - features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - features2.pNext = &robustness2; + VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{}; + timeline_semaphore.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES; + timeline_semaphore.pNext = &robustness2; - physical.GetFeatures2KHR(features2); + VkPhysicalDevice16BitStorageFeatures bit16_storage{}; + bit16_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES; + bit16_storage.pNext = &timeline_semaphore; + + VkPhysicalDevice8BitStorageFeatures bit8_storage{}; + bit8_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES; + bit8_storage.pNext = &bit16_storage; + + VkPhysicalDeviceHostQueryResetFeatures host_query_reset{}; + host_query_reset.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES; + host_query_reset.pNext = &bit8_storage; + + VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{}; + draw_parameters.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES; + draw_parameters.pNext = &host_query_reset; + + VkPhysicalDeviceFeatures2 features2{}; + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + features2.pNext = &draw_parameters; + + physical.GetFeatures2(features2); const VkPhysicalDeviceFeatures& features{features2.features}; - const std::array feature_report{ + std::array feature_report{ std::make_pair(features.robustBufferAccess, "robustBufferAccess"), std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), std::make_pair(features.imageCubeArray, "imageCubeArray"), @@ -976,6 +1035,7 @@ void Device::CheckSuitability(bool requires_swapchain) const { std::make_pair(features.tessellationShader, "tessellationShader"), std::make_pair(features.sampleRateShading, "sampleRateShading"), std::make_pair(features.dualSrcBlend, "dualSrcBlend"), + std::make_pair(features.logicOp, "logicOp"), std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"), std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"), std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"), @@ -983,27 +1043,39 @@ void Device::CheckSuitability(bool requires_swapchain) const { "shaderStorageImageWriteWithoutFormat"), std::make_pair(features.shaderClipDistance, "shaderClipDistance"), std::make_pair(features.shaderCullDistance, "shaderCullDistance"), - std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"), std::make_pair(variable_pointers.variablePointers, "variablePointers"), std::make_pair(variable_pointers.variablePointersStorageBuffer, "variablePointersStorageBuffer"), std::make_pair(robustness2.robustBufferAccess2, "robustBufferAccess2"), std::make_pair(robustness2.robustImageAccess2, "robustImageAccess2"), std::make_pair(robustness2.nullDescriptor, "nullDescriptor"), + std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"), + std::make_pair(timeline_semaphore.timelineSemaphore, "timelineSemaphore"), + std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"), + std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess, + "uniformAndStorageBuffer16BitAccess"), + std::make_pair(bit8_storage.storageBuffer8BitAccess, "storageBuffer8BitAccess"), + std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess, + "uniformAndStorageBuffer8BitAccess"), + std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"), + std::make_pair(draw_parameters.shaderDrawParameters, "shaderDrawParameters"), }; + + bool has_all_required_features = true; for (const auto& [is_supported, name] : feature_report) { - if (is_supported) { - continue; + if (!is_supported) { + LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); + has_all_required_features = false; } - LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); + } + + if (!has_all_required_features) { throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); } } std::vector Device::LoadExtensions(bool requires_surface) { - std::vector extensions; - extensions.reserve(8 + REQUIRED_EXTENSIONS.size()); - extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()); + std::vector extensions = ExtensionsRequiredForInstanceVersion(instance_version); if (requires_surface) { extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); } @@ -1022,6 +1094,7 @@ std::vector Device::LoadExtensions(bool requires_surface) { bool has_ext_vertex_input_dynamic_state{}; bool has_ext_line_rasterization{}; bool has_ext_primitive_topology_list_restart{}; + bool has_ext_depth_clip_control{}; for (const std::string& extension : supported_extensions) { const auto test = [&](std::optional> status, const char* name, bool push) { @@ -1055,10 +1128,11 @@ std::vector Device::LoadExtensions(bool requires_surface) { test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true); test(ext_conservative_rasterization, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME, true); + test(has_ext_depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, false); test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false); test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false); test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); - test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false); + test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, true); test(has_ext_provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, false); test(has_ext_vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME, false); @@ -1079,37 +1153,37 @@ std::vector Device::LoadExtensions(bool requires_surface) { VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false); } } - VkPhysicalDeviceFeatures2KHR features{}; - features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; + VkPhysicalDeviceFeatures2 features{}; + features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - VkPhysicalDeviceProperties2KHR physical_properties{}; - physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; + VkPhysicalDeviceProperties2 physical_properties{}; + physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; if (has_khr_shader_float16_int8) { - VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8_features; - float16_int8_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR; + VkPhysicalDeviceShaderFloat16Int8Features float16_int8_features; + float16_int8_features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES; float16_int8_features.pNext = nullptr; features.pNext = &float16_int8_features; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); is_float16_supported = float16_int8_features.shaderFloat16; is_int8_supported = float16_int8_features.shaderInt8; extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); } if (has_ext_subgroup_size_control) { - VkPhysicalDeviceSubgroupSizeControlFeaturesEXT subgroup_features; - subgroup_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT; + VkPhysicalDeviceSubgroupSizeControlFeatures subgroup_features; + subgroup_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES; subgroup_features.pNext = nullptr; features.pNext = &subgroup_features; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); - VkPhysicalDeviceSubgroupSizeControlPropertiesEXT subgroup_properties; + VkPhysicalDeviceSubgroupSizeControlProperties subgroup_properties; subgroup_properties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT; + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES; subgroup_properties.pNext = nullptr; physical_properties.pNext = &subgroup_properties; - physical.GetProperties2KHR(physical_properties); + physical.GetProperties2(physical_properties); is_warp_potentially_bigger = subgroup_properties.maxSubgroupSize > GuestWarpSize; @@ -1128,7 +1202,7 @@ std::vector Device::LoadExtensions(bool requires_surface) { provoking_vertex.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT; provoking_vertex.pNext = nullptr; features.pNext = &provoking_vertex; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (provoking_vertex.provokingVertexLast && provoking_vertex.transformFeedbackPreservesProvokingVertex) { @@ -1142,7 +1216,7 @@ std::vector Device::LoadExtensions(bool requires_surface) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT; vertex_input.pNext = nullptr; features.pNext = &vertex_input; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (vertex_input.vertexInputDynamicState) { extensions.push_back(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); @@ -1154,7 +1228,7 @@ std::vector Device::LoadExtensions(bool requires_surface) { atomic_int64.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES; atomic_int64.pNext = nullptr; features.pNext = &atomic_int64; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (atomic_int64.shaderBufferInt64Atomics && atomic_int64.shaderSharedInt64Atomics) { extensions.push_back(VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); @@ -1166,13 +1240,13 @@ std::vector Device::LoadExtensions(bool requires_surface) { tfb_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT; tfb_features.pNext = nullptr; features.pNext = &tfb_features; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); VkPhysicalDeviceTransformFeedbackPropertiesEXT tfb_properties; tfb_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT; tfb_properties.pNext = nullptr; physical_properties.pNext = &tfb_properties; - physical.GetProperties2KHR(physical_properties); + physical.GetProperties2(physical_properties); if (tfb_features.transformFeedback && tfb_features.geometryStreams && tfb_properties.maxTransformFeedbackStreams >= 4 && @@ -1187,7 +1261,7 @@ std::vector Device::LoadExtensions(bool requires_surface) { border_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT; border_features.pNext = nullptr; features.pNext = &border_features; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (border_features.customBorderColors && border_features.customBorderColorWithoutFormat) { extensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); @@ -1200,7 +1274,7 @@ std::vector Device::LoadExtensions(bool requires_surface) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; extended_dynamic_state.pNext = nullptr; features.pNext = &extended_dynamic_state; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (extended_dynamic_state.extendedDynamicState) { extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); @@ -1212,19 +1286,32 @@ std::vector Device::LoadExtensions(bool requires_surface) { line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT; line_raster.pNext = nullptr; features.pNext = &line_raster; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (line_raster.rectangularLines && line_raster.smoothLines) { extensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME); ext_line_rasterization = true; } } + if (has_ext_depth_clip_control) { + VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features; + depth_clip_control_features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT; + depth_clip_control_features.pNext = nullptr; + features.pNext = &depth_clip_control_features; + physical.GetFeatures2(features); + + if (depth_clip_control_features.depthClipControl) { + extensions.push_back(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); + ext_depth_clip_control = true; + } + } if (has_khr_workgroup_memory_explicit_layout) { VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR layout; layout.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR; layout.pNext = nullptr; features.pNext = &layout; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (layout.workgroupMemoryExplicitLayout && layout.workgroupMemoryExplicitLayout8BitAccess && @@ -1240,7 +1327,7 @@ std::vector Device::LoadExtensions(bool requires_surface) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR; executable_properties.pNext = nullptr; features.pNext = &executable_properties; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (executable_properties.pipelineExecutableInfo) { extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); @@ -1253,7 +1340,7 @@ std::vector Device::LoadExtensions(bool requires_surface) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT; primitive_topology_list_restart.pNext = nullptr; features.pNext = &primitive_topology_list_restart; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); is_topology_list_restart_supported = primitive_topology_list_restart.primitiveTopologyListRestart; @@ -1271,7 +1358,7 @@ std::vector Device::LoadExtensions(bool requires_surface) { push_descriptor.pNext = nullptr; physical_properties.pNext = &push_descriptor; - physical.GetProperties2KHR(physical_properties); + physical.GetProperties2(physical_properties); max_push_descriptors = push_descriptor.maxPushDescriptors; } @@ -1319,21 +1406,25 @@ void Device::SetupFeatures() { is_shader_storage_image_multisample = features.shaderStorageImageMultisample; is_blit_depth_stencil_supported = TestDepthStencilBlits(); is_optimal_astc_supported = IsOptimalAstcSupported(features); + + const VkPhysicalDeviceLimits& limits{properties.limits}; + max_vertex_input_attributes = limits.maxVertexInputAttributes; + max_vertex_input_bindings = limits.maxVertexInputBindings; } void Device::SetupProperties() { - float_controls.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR; + float_controls.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES; VkPhysicalDeviceProperties2KHR properties2{}; - properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; + properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; properties2.pNext = &float_controls; - physical.GetProperties2KHR(properties2); + physical.GetProperties2(properties2); } void Device::CollectTelemetryParameters() { - VkPhysicalDeviceDriverPropertiesKHR driver{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR, + VkPhysicalDeviceDriverProperties driver{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, .pNext = nullptr, .driverID = {}, .driverName = {}, @@ -1341,12 +1432,12 @@ void Device::CollectTelemetryParameters() { .conformanceVersion = {}, }; - VkPhysicalDeviceProperties2KHR device_properties{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, + VkPhysicalDeviceProperties2 device_properties{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &driver, .properties = {}, }; - physical.GetProperties2KHR(device_properties); + physical.GetProperties2(device_properties); driver_id = driver.driverID; vendor_name = driver.driverName; @@ -1402,23 +1493,10 @@ void Device::CollectToolingInfo() { if (!ext_tooling_info) { return; } - const auto vkGetPhysicalDeviceToolPropertiesEXT = - reinterpret_cast( - dld.vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceToolPropertiesEXT")); - if (!vkGetPhysicalDeviceToolPropertiesEXT) { - return; - } - u32 tool_count = 0; - if (vkGetPhysicalDeviceToolPropertiesEXT(physical, &tool_count, nullptr) != VK_SUCCESS) { - return; - } - std::vector tools(tool_count); - if (vkGetPhysicalDeviceToolPropertiesEXT(physical, &tool_count, tools.data()) != VK_SUCCESS) { - return; - } - for (const VkPhysicalDeviceToolPropertiesEXT& tool : tools) { + auto tools{physical.GetPhysicalDeviceToolProperties()}; + for (const VkPhysicalDeviceToolProperties& tool : tools) { const std::string_view name = tool.name; - LOG_INFO(Render_Vulkan, "{}", name); + LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", name); has_renderdoc = has_renderdoc || name == "RenderDoc"; has_nsight_graphics = has_nsight_graphics || name == "NVIDIA Nsight Graphics"; } diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index d7cc6c5..6a26c4e 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -211,18 +211,13 @@ public: return khr_uniform_buffer_standard_layout; } - /// Returns true if the device supports VK_KHR_spirv_1_4. - bool IsKhrSpirv1_4Supported() const { - return khr_spirv_1_4; - } - /// Returns true if the device supports VK_KHR_push_descriptor. bool IsKhrPushDescriptorSupported() const { return khr_push_descriptor; } /// Returns true if VK_KHR_pipeline_executable_properties is enabled. - bool IsKhrPipelineEexecutablePropertiesEnabled() const { + bool IsKhrPipelineExecutablePropertiesEnabled() const { return khr_pipeline_executable_properties; } @@ -261,6 +256,11 @@ public: return ext_depth_range_unrestricted; } + /// Returns true if the device supports VK_EXT_depth_clip_control. + bool IsExtDepthClipControlSupported() const { + return ext_depth_clip_control; + } + /// Returns true if the device supports VK_EXT_shader_viewport_index_layer. bool IsExtShaderViewportIndexLayerSupported() const { return ext_shader_viewport_index_layer; @@ -316,6 +316,17 @@ public: return ext_shader_atomic_int64; } + /// Returns the minimum supported version of SPIR-V. + u32 SupportedSpirvVersion() const { + if (instance_version >= VK_API_VERSION_1_3) { + return 0x00010600U; + } + if (khr_spirv_1_4) { + return 0x00010400U; + } + return 0x00010000U; + } + /// Returns true when a known debugging tool is attached. bool HasDebuggingToolAttached() const { return has_renderdoc || has_nsight_graphics; @@ -362,6 +373,14 @@ public: return must_emulate_bgr565; } + u32 GetMaxVertexInputAttributes() const { + return max_vertex_input_attributes; + } + + u32 GetMaxVertexInputBindings() const { + return max_vertex_input_bindings; + } + private: /// Checks if the physical device is suitable. void CheckSuitability(bool requires_swapchain) const; @@ -440,6 +459,7 @@ private: bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format. bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. + bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. @@ -461,6 +481,8 @@ private: bool supports_d24_depth{}; ///< Supports D24 depth buffers. bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. + u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline + u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline // Telemetry parameters std::string vendor_name; ///< Device's driver name. diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index a082e30..562039b 100644 --- a/src/video_core/vulkan_common/vulkan_instance.cpp +++ b/src/video_core/vulkan_common/vulkan_instance.cpp @@ -9,18 +9,21 @@ #include "common/common_types.h" #include "common/dynamic_library.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "core/frontend/emu_window.h" #include "video_core/vulkan_common/vulkan_instance.h" #include "video_core/vulkan_common/vulkan_wrapper.h" // Include these late to avoid polluting previous headers -#ifdef _WIN32 +#if defined(_WIN32) #include // ensure include order #include -#endif - -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) +#include +#elif defined(__ANDROID__) +#include +#else #include #include #include @@ -39,8 +42,15 @@ namespace { case Core::Frontend::WindowSystemType::Windows: extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); break; -#endif -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) + case Core::Frontend::WindowSystemType::Cocoa: + extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); + break; +#elif defined(__ANDROID__) + case Core::Frontend::WindowSystemType::Android: + extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); + break; +#else case Core::Frontend::WindowSystemType::X11: extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); break; @@ -59,6 +69,10 @@ namespace { extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + +#ifdef __APPLE__ + extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); +#endif return extensions; } @@ -140,7 +154,7 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD } vk::Instance instance = std::async([&] { - return vk::Instance::Create(required_version, layers, extensions, dld); + return vk::Instance::Create(available_version, layers, extensions, dld); }).get(); if (!vk::Load(*instance, dld)) { LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers"); diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 6442898..1732866 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp index 69f9c49..fa9bafa 100644 --- a/src/video_core/vulkan_common/vulkan_surface.cpp +++ b/src/video_core/vulkan_common/vulkan_surface.cpp @@ -11,9 +11,11 @@ #include // ensure include order #include -#endif - -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) +#include +#elif defined(__ANDROID__) +#include +#else #include #include #include @@ -40,8 +42,33 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance, throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); } } -#endif -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) + if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) { + const VkMacOSSurfaceCreateInfoMVK mvk_ci{VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, + nullptr, 0, window_info.render_surface}; + const auto vkCreateMacOSSurfaceMVK = reinterpret_cast( + dld.vkGetInstanceProcAddr(*instance, "vkCreateMacOSSurfaceMVK")); + if (!vkCreateMacOSSurfaceMVK || + vkCreateMacOSSurfaceMVK(*instance, &mvk_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Metal surface"); + throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); + } + } +#elif defined(__ANDROID__) + if (window_info.type == Core::Frontend::WindowSystemType::Android) { + const VkAndroidSurfaceCreateInfoKHR android_ci{ + VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, nullptr, 0, + reinterpret_cast(window_info.render_surface)}; + const auto vkCreateAndroidSurfaceKHR = reinterpret_cast( + dld.vkGetInstanceProcAddr(*instance, "vkCreateAndroidSurfaceKHR")); + if (!vkCreateAndroidSurfaceKHR || + vkCreateAndroidSurfaceKHR(*instance, &android_ci, nullptr, &unsafe_surface) != + VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Android surface"); + throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); + } + } +#else if (window_info.type == Core::Frontend::WindowSystemType::X11) { const VkXlibSurfaceCreateInfoKHR xlib_ci{ VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, @@ -70,6 +97,7 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance, } } #endif + if (!unsafe_surface) { LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 2ad98dc..483b534 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -86,6 +86,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdBindVertexBuffers); X(vkCmdBlitImage); X(vkCmdClearAttachments); + X(vkCmdClearColorImage); X(vkCmdCopyBuffer); X(vkCmdCopyBufferToImage); X(vkCmdCopyImage); @@ -130,7 +131,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCreateComputePipelines); X(vkCreateDescriptorPool); X(vkCreateDescriptorSetLayout); - X(vkCreateDescriptorUpdateTemplateKHR); + X(vkCreateDescriptorUpdateTemplate); X(vkCreateEvent); X(vkCreateFence); X(vkCreateFramebuffer); @@ -149,7 +150,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkDestroyCommandPool); X(vkDestroyDescriptorPool); X(vkDestroyDescriptorSetLayout); - X(vkDestroyDescriptorUpdateTemplateKHR); + X(vkDestroyDescriptorUpdateTemplate); X(vkDestroyEvent); X(vkDestroyFence); X(vkDestroyFramebuffer); @@ -180,18 +181,29 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkGetQueryPoolResults); X(vkGetPipelineExecutablePropertiesKHR); X(vkGetPipelineExecutableStatisticsKHR); - X(vkGetSemaphoreCounterValueKHR); + X(vkGetSemaphoreCounterValue); X(vkMapMemory); X(vkQueueSubmit); X(vkResetFences); - X(vkResetQueryPoolEXT); + X(vkResetQueryPool); X(vkSetDebugUtilsObjectNameEXT); X(vkSetDebugUtilsObjectTagEXT); X(vkUnmapMemory); - X(vkUpdateDescriptorSetWithTemplateKHR); + X(vkUpdateDescriptorSetWithTemplate); X(vkUpdateDescriptorSets); X(vkWaitForFences); - X(vkWaitSemaphoresKHR); + X(vkWaitSemaphores); + + // Support for timeline semaphores is mandatory in Vulkan 1.2 + if (!dld.vkGetSemaphoreCounterValue) { + Proc(dld.vkGetSemaphoreCounterValue, dld, "vkGetSemaphoreCounterValueKHR", device); + Proc(dld.vkWaitSemaphores, dld, "vkWaitSemaphoresKHR", device); + } + + // Support for host query reset is mandatory in Vulkan 1.2 + if (!dld.vkResetQueryPool) { + Proc(dld.vkResetQueryPool, dld, "vkResetQueryPoolEXT", device); + } #undef X } @@ -224,12 +236,13 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept { X(vkCreateDebugUtilsMessengerEXT); X(vkDestroyDebugUtilsMessengerEXT); X(vkDestroySurfaceKHR); - X(vkGetPhysicalDeviceFeatures2KHR); - X(vkGetPhysicalDeviceProperties2KHR); + X(vkGetPhysicalDeviceFeatures2); + X(vkGetPhysicalDeviceProperties2); X(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); X(vkGetPhysicalDeviceSurfaceFormatsKHR); X(vkGetPhysicalDeviceSurfacePresentModesKHR); X(vkGetPhysicalDeviceSurfaceSupportKHR); + X(vkGetPhysicalDeviceToolProperties); X(vkGetSwapchainImagesKHR); X(vkQueuePresentKHR); @@ -359,9 +372,9 @@ void Destroy(VkDevice device, VkDescriptorSetLayout handle, const DeviceDispatch dld.vkDestroyDescriptorSetLayout(device, handle, nullptr); } -void Destroy(VkDevice device, VkDescriptorUpdateTemplateKHR handle, +void Destroy(VkDevice device, VkDescriptorUpdateTemplate handle, const DeviceDispatch& dld) noexcept { - dld.vkDestroyDescriptorUpdateTemplateKHR(device, handle, nullptr); + dld.vkDestroyDescriptorUpdateTemplate(device, handle, nullptr); } void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld) noexcept { @@ -442,6 +455,12 @@ VkResult Free(VkDevice device, VkCommandPool handle, Span buffe Instance Instance::Create(u32 version, Span layers, Span extensions, InstanceDispatch& dispatch) { +#ifdef __APPLE__ + constexpr VkFlags ci_flags{VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR}; +#else + constexpr VkFlags ci_flags{}; +#endif + const VkApplicationInfo application_info{ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pNext = nullptr, @@ -454,7 +473,7 @@ Instance Instance::Create(u32 version, Span layers, SpanvkCreateDescriptorUpdateTemplateKHR(handle, &ci, nullptr, &object)); - return DescriptorUpdateTemplateKHR(object, handle, *dld); +DescriptorUpdateTemplate Device::CreateDescriptorUpdateTemplate( + const VkDescriptorUpdateTemplateCreateInfo& ci) const { + VkDescriptorUpdateTemplate object; + Check(dld->vkCreateDescriptorUpdateTemplate(handle, &ci, nullptr, &object)); + return DescriptorUpdateTemplate(object, handle, *dld); } QueryPool Device::CreateQueryPool(const VkQueryPoolCreateInfo& ci) const { @@ -857,20 +876,20 @@ VkPhysicalDeviceProperties PhysicalDevice::GetProperties() const noexcept { return properties; } -void PhysicalDevice::GetProperties2KHR(VkPhysicalDeviceProperties2KHR& properties) const noexcept { - dld->vkGetPhysicalDeviceProperties2KHR(physical_device, &properties); +void PhysicalDevice::GetProperties2(VkPhysicalDeviceProperties2& properties) const noexcept { + dld->vkGetPhysicalDeviceProperties2(physical_device, &properties); } VkPhysicalDeviceFeatures PhysicalDevice::GetFeatures() const noexcept { - VkPhysicalDeviceFeatures2KHR features2; - features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; + VkPhysicalDeviceFeatures2 features2; + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; features2.pNext = nullptr; - dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features2); + dld->vkGetPhysicalDeviceFeatures2(physical_device, &features2); return features2.features; } -void PhysicalDevice::GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR& features) const noexcept { - dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features); +void PhysicalDevice::GetFeatures2(VkPhysicalDeviceFeatures2& features) const noexcept { + dld->vkGetPhysicalDeviceFeatures2(physical_device, &features); } VkFormatProperties PhysicalDevice::GetFormatProperties(VkFormat format) const noexcept { @@ -895,6 +914,18 @@ std::vector PhysicalDevice::GetQueueFamilyProperties() return properties; } +std::vector PhysicalDevice::GetPhysicalDeviceToolProperties() + const { + u32 num = 0; + if (!dld->vkGetPhysicalDeviceToolProperties) { + return {}; + } + dld->vkGetPhysicalDeviceToolProperties(physical_device, &num, nullptr); + std::vector properties(num); + dld->vkGetPhysicalDeviceToolProperties(physical_device, &num, properties.data()); + return properties; +} + bool PhysicalDevice::GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR surface) const { VkBool32 supported; Check(dld->vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue_family_index, surface, diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 795f16b..8bd4fd4 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -168,12 +168,13 @@ struct InstanceDispatch { PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{}; PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices{}; PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr{}; - PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{}; + PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2{}; PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{}; PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{}; PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2{}; PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{}; - PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{}; + PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2{}; + PFN_vkGetPhysicalDeviceToolProperties vkGetPhysicalDeviceToolProperties{}; PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{}; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR{}; PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR{}; @@ -204,6 +205,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT{}; PFN_vkCmdBlitImage vkCmdBlitImage{}; PFN_vkCmdClearAttachments vkCmdClearAttachments{}; + PFN_vkCmdClearColorImage vkCmdClearColorImage{}; PFN_vkCmdCopyBuffer vkCmdCopyBuffer{}; PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage{}; PFN_vkCmdCopyImage vkCmdCopyImage{}; @@ -247,7 +249,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCreateComputePipelines vkCreateComputePipelines{}; PFN_vkCreateDescriptorPool vkCreateDescriptorPool{}; PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout{}; - PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR{}; + PFN_vkCreateDescriptorUpdateTemplate vkCreateDescriptorUpdateTemplate{}; PFN_vkCreateEvent vkCreateEvent{}; PFN_vkCreateFence vkCreateFence{}; PFN_vkCreateFramebuffer vkCreateFramebuffer{}; @@ -266,7 +268,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkDestroyCommandPool vkDestroyCommandPool{}; PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool{}; PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout{}; - PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR{}; + PFN_vkDestroyDescriptorUpdateTemplate vkDestroyDescriptorUpdateTemplate{}; PFN_vkDestroyEvent vkDestroyEvent{}; PFN_vkDestroyFence vkDestroyFence{}; PFN_vkDestroyFramebuffer vkDestroyFramebuffer{}; @@ -297,18 +299,18 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{}; PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{}; PFN_vkGetQueryPoolResults vkGetQueryPoolResults{}; - PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{}; + PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue{}; PFN_vkMapMemory vkMapMemory{}; PFN_vkQueueSubmit vkQueueSubmit{}; PFN_vkResetFences vkResetFences{}; - PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT{}; + PFN_vkResetQueryPool vkResetQueryPool{}; PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{}; PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT{}; PFN_vkUnmapMemory vkUnmapMemory{}; - PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR{}; + PFN_vkUpdateDescriptorSetWithTemplate vkUpdateDescriptorSetWithTemplate{}; PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets{}; PFN_vkWaitForFences vkWaitForFences{}; - PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR{}; + PFN_vkWaitSemaphores vkWaitSemaphores{}; }; /// Loads instance agnostic function pointers. @@ -327,7 +329,7 @@ void Destroy(VkDevice, VkBufferView, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkCommandPool, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkDescriptorPool, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept; -void Destroy(VkDevice, VkDescriptorUpdateTemplateKHR, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkDescriptorUpdateTemplate, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkEvent, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept; @@ -519,9 +521,7 @@ public: dld{rhs.dld} {} /// Assign an allocation transfering ownership from another allocation. - /// Releases any previously held allocation. PoolAllocations& operator=(PoolAllocations&& rhs) noexcept { - Release(); allocations = std::move(rhs.allocations); num = rhs.num; device = rhs.device; @@ -530,11 +530,6 @@ public: return *this; } - /// Destroys any held allocation. - ~PoolAllocations() { - Release(); - } - /// Returns the number of allocations. std::size_t size() const noexcept { return num; @@ -557,19 +552,6 @@ public: } private: - /// Destroys the held allocations if they exist. - void Release() noexcept { - if (!allocations) { - return; - } - const Span span(allocations.get(), num); - const VkResult result = Free(device, pool, span, *dld); - // There's no way to report errors from a destructor. - if (result != VK_SUCCESS) { - std::terminate(); - } - } - std::unique_ptr allocations; std::size_t num = 0; VkDevice device = nullptr; @@ -579,7 +561,7 @@ private: using DebugUtilsMessenger = Handle; using DescriptorSetLayout = Handle; -using DescriptorUpdateTemplateKHR = Handle; +using DescriptorUpdateTemplate = Handle; using Pipeline = Handle; using PipelineLayout = Handle; using QueryPool = Handle; @@ -786,7 +768,7 @@ public: [[nodiscard]] u64 GetCounter() const { u64 value; - Check(dld->vkGetSemaphoreCounterValueKHR(owner, handle, &value)); + Check(dld->vkGetSemaphoreCounterValue(owner, handle, &value)); return value; } @@ -798,15 +780,15 @@ public: * @return True on successful wait, false on timeout */ bool Wait(u64 value, u64 timeout = std::numeric_limits::max()) const { - const VkSemaphoreWaitInfoKHR wait_info{ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR, + const VkSemaphoreWaitInfo wait_info{ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, .pNext = nullptr, .flags = 0, .semaphoreCount = 1, .pSemaphores = &handle, .pValues = &value, }; - const VkResult result = dld->vkWaitSemaphoresKHR(owner, &wait_info, timeout); + const VkResult result = dld->vkWaitSemaphores(owner, &wait_info, timeout); switch (result) { case VK_SUCCESS: return true; @@ -860,8 +842,8 @@ public: CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const; - DescriptorUpdateTemplateKHR CreateDescriptorUpdateTemplateKHR( - const VkDescriptorUpdateTemplateCreateInfoKHR& ci) const; + DescriptorUpdateTemplate CreateDescriptorUpdateTemplate( + const VkDescriptorUpdateTemplateCreateInfo& ci) const; QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const; @@ -889,9 +871,9 @@ public: void UpdateDescriptorSets(Span writes, Span copies) const noexcept; - void UpdateDescriptorSet(VkDescriptorSet set, VkDescriptorUpdateTemplateKHR update_template, + void UpdateDescriptorSet(VkDescriptorSet set, VkDescriptorUpdateTemplate update_template, const void* data) const noexcept { - dld->vkUpdateDescriptorSetWithTemplateKHR(handle, set, update_template, data); + dld->vkUpdateDescriptorSetWithTemplate(handle, set, update_template, data); } VkResult AcquireNextImageKHR(VkSwapchainKHR swapchain, u64 timeout, VkSemaphore semaphore, @@ -904,8 +886,8 @@ public: return dld->vkDeviceWaitIdle(handle); } - void ResetQueryPoolEXT(VkQueryPool query_pool, u32 first, u32 count) const noexcept { - dld->vkResetQueryPoolEXT(handle, query_pool, first, count); + void ResetQueryPool(VkQueryPool query_pool, u32 first, u32 count) const noexcept { + dld->vkResetQueryPool(handle, query_pool, first, count); } VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, @@ -930,11 +912,11 @@ public: VkPhysicalDeviceProperties GetProperties() const noexcept; - void GetProperties2KHR(VkPhysicalDeviceProperties2KHR&) const noexcept; + void GetProperties2(VkPhysicalDeviceProperties2&) const noexcept; VkPhysicalDeviceFeatures GetFeatures() const noexcept; - void GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR&) const noexcept; + void GetFeatures2(VkPhysicalDeviceFeatures2&) const noexcept; VkFormatProperties GetFormatProperties(VkFormat) const noexcept; @@ -942,6 +924,8 @@ public: std::vector GetQueueFamilyProperties() const; + std::vector GetPhysicalDeviceToolProperties() const; + bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const; VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const; @@ -1000,7 +984,7 @@ public: dynamic_offsets.size(), dynamic_offsets.data()); } - void PushDescriptorSetWithTemplateKHR(VkDescriptorUpdateTemplateKHR update_template, + void PushDescriptorSetWithTemplateKHR(VkDescriptorUpdateTemplate update_template, VkPipelineLayout layout, u32 set, const void* data) const noexcept { dld->vkCmdPushDescriptorSetWithTemplateKHR(handle, update_template, layout, set, data); @@ -1041,6 +1025,11 @@ public: rects.data()); } + void ClearColorImage(VkImage image, VkImageLayout layout, VkClearColorValue color, + Span ranges) { + dld->vkCmdClearColorImage(handle, image, layout, &color, ranges.size(), ranges.data()); + } + void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, VkImageLayout dst_layout, Span regions, VkFilter filter) const noexcept { diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt index 3f75d97..02582aa 100644 --- a/src/web_service/CMakeLists.txt +++ b/src/web_service/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(web_service STATIC announce_room_json.cpp announce_room_json.h + precompiled_headers.h telemetry_json.cpp telemetry_json.h verify_login.cpp @@ -16,4 +17,8 @@ add_library(web_service STATIC ) create_target_directory_groups(web_service) -target_link_libraries(web_service PRIVATE common network nlohmann_json::nlohmann_json httplib cpp-jwt) +target_link_libraries(web_service PRIVATE common network nlohmann_json::nlohmann_json httplib::httplib cpp-jwt::cpp-jwt) + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(web_service PRIVATE precompiled_headers.h) +endif() diff --git a/src/web_service/precompiled_headers.h b/src/web_service/precompiled_headers.h new file mode 100644 index 0000000..aabae73 --- /dev/null +++ b/src/web_service/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index 378804c..12a7e49 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -111,7 +111,8 @@ struct Client::Impl { httplib::Error error; if (!cli->send(request, response, error)) { - LOG_ERROR(WebService, "{} to {} returned null", method, host + path); + LOG_ERROR(WebService, "{} to {} returned null (httplib Error: {})", method, host + path, + httplib::to_string(error)); return WebResult{WebResult::Code::LibError, "Null response", ""}; } diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 29d506c..4a7d356 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -18,6 +18,9 @@ add_executable(yuzu about_dialog.cpp about_dialog.h aboutdialog.ui + applets/qt_amiibo_settings.cpp + applets/qt_amiibo_settings.h + applets/qt_amiibo_settings.ui applets/qt_controller.cpp applets/qt_controller.h applets/qt_controller.ui @@ -85,6 +88,9 @@ add_executable(yuzu configuration/configure_input_advanced.cpp configuration/configure_input_advanced.h configuration/configure_input_advanced.ui + configuration/configure_input_per_game.cpp + configuration/configure_input_per_game.h + configuration/configure_input_per_game.ui configuration/configure_input_player.cpp configuration/configure_input_player.h configuration/configure_input_player.ui @@ -183,6 +189,7 @@ add_executable(yuzu multiplayer/state.cpp multiplayer/state.h multiplayer/validation.h + precompiled_headers.h startup_checks.cpp startup_checks.h uisettings.cpp @@ -292,7 +299,7 @@ if (APPLE) set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) elseif(WIN32) # compile as a win32 gui application instead of a console application - if (QT_VERSION VERSION_GREATER 6) + if (QT_VERSION VERSION_GREATER_EQUAL 6) target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate) else() target_link_libraries(yuzu PRIVATE Qt5::WinMain) @@ -308,15 +315,15 @@ endif() create_target_directory_groups(yuzu) target_link_libraries(yuzu PRIVATE common core input_common network video_core) -target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets Qt::Multimedia) +target_link_libraries(yuzu PRIVATE Boost::boost glad Qt${QT_MAJOR_VERSION}::Widgets) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) -target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) +target_link_libraries(yuzu PRIVATE Vulkan::Headers) if (NOT WIN32) - target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) + target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS}) endif() -if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - target_link_libraries(yuzu PRIVATE Qt::DBus) +if (UNIX AND NOT APPLE) + target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus) endif() target_compile_definitions(yuzu PRIVATE @@ -347,7 +354,7 @@ if (USE_DISCORD_PRESENCE) discord_impl.cpp discord_impl.h ) - target_link_libraries(yuzu PRIVATE discord-rpc) + target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc) target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) endif() @@ -355,8 +362,13 @@ if (ENABLE_WEB_SERVICE) target_compile_definitions(yuzu PRIVATE -DENABLE_WEB_SERVICE) endif() +if (YUZU_USE_QT_MULTIMEDIA) + target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::Multimedia) + target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_MULTIMEDIA) +endif () + if (YUZU_USE_QT_WEB_ENGINE) - target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets) + target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::WebEngineCore Qt${QT_MAJOR_VERSION}::WebEngineWidgets) target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) endif () @@ -364,13 +376,22 @@ if(UNIX AND NOT APPLE) install(TARGETS yuzu) endif() -if (YUZU_USE_BUNDLED_QT) +if (WIN32 AND QT_VERSION VERSION_GREATER_EQUAL 6) + if (MSVC AND NOT ${CMAKE_GENERATOR} STREQUAL "Ninja") + set(YUZU_EXE_DIR "${CMAKE_BINARY_DIR}/bin/$") + else() + set(YUZU_EXE_DIR "${CMAKE_BINARY_DIR}/bin") + endif() + add_custom_command(TARGET yuzu POST_BUILD COMMAND ${WINDEPLOYQT_EXECUTABLE} "${YUZU_EXE_DIR}/yuzu.exe" --dir "${YUZU_EXE_DIR}" --libdir "${YUZU_EXE_DIR}" --plugindir "${YUZU_EXE_DIR}/plugins" --no-compiler-runtime --no-opengl-sw --no-system-d3d-compiler --no-translations --verbose 0) +endif() + +if (YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_LESS 6) include(CopyYuzuQt5Deps) copy_yuzu_Qt5_deps(yuzu) endif() if (ENABLE_SDL2) - target_link_libraries(yuzu PRIVATE SDL2) + target_link_libraries(yuzu PRIVATE SDL2::SDL2) target_compile_definitions(yuzu PRIVATE HAVE_SDL2) endif() @@ -381,10 +402,14 @@ if (MSVC) copy_yuzu_FFmpeg_deps(yuzu) endif() -if (NOT APPLE) +if (NOT APPLE AND ENABLE_OPENGL) target_compile_definitions(yuzu PRIVATE HAS_OPENGL) endif() -if (ARCHITECTURE_x86_64) - target_link_libraries(yuzu PRIVATE dynarmic) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) + target_link_libraries(yuzu PRIVATE dynarmic::dynarmic) +endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(yuzu PRIVATE precompiled_headers.h) endif() diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp new file mode 100644 index 0000000..93ad4b4 --- /dev/null +++ b/src/yuzu/applets/qt_amiibo_settings.cpp @@ -0,0 +1,264 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "common/assert.h" +#include "common/string_util.h" +#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfp/nfp_result.h" +#include "input_common/drivers/virtual_amiibo.h" +#include "input_common/main.h" +#include "ui_qt_amiibo_settings.h" +#ifdef ENABLE_WEB_SERVICE +#include "web_service/web_backend.h" +#endif +#include "yuzu/applets/qt_amiibo_settings.h" +#include "yuzu/main.h" + +QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent, + Core::Frontend::CabinetParameters parameters_, + InputCommon::InputSubsystem* input_subsystem_, + std::shared_ptr nfp_device_) + : QDialog(parent), ui(std::make_unique()), + input_subsystem{input_subsystem_}, nfp_device{std::move(nfp_device_)}, + parameters(std::move(parameters_)) { + ui->setupUi(this); + + LoadInfo(); + + resize(0, 0); +} + +QtAmiiboSettingsDialog::~QtAmiiboSettingsDialog() = default; + +int QtAmiiboSettingsDialog::exec() { + if (!is_initalized) { + return QDialog::Rejected; + } + return QDialog::exec(); +} + +std::string QtAmiiboSettingsDialog::GetName() const { + return ui->amiiboCustomNameValue->text().toStdString(); +} + +void QtAmiiboSettingsDialog::LoadInfo() { + if (input_subsystem->GetVirtualAmiibo()->ReloadAmiibo() != + InputCommon::VirtualAmiibo::Info::Success) { + return; + } + + if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound && + nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) { + return; + } + nfp_device->Mount(Service::NFP::MountTarget::All); + + LoadAmiiboInfo(); + LoadAmiiboData(); + LoadAmiiboGameInfo(); + + ui->amiiboDirectoryValue->setText( + QString::fromStdString(input_subsystem->GetVirtualAmiibo()->GetLastFilePath())); + + SetSettingsDescription(); + is_initalized = true; +} + +void QtAmiiboSettingsDialog::LoadAmiiboInfo() { + Service::NFP::ModelInfo model_info{}; + const auto model_result = nfp_device->GetModelInfo(model_info); + + if (model_result.IsFailure()) { + ui->amiiboImageLabel->setVisible(false); + ui->amiiboInfoGroup->setVisible(false); + return; + } + + const auto amiibo_id = + fmt::format("{:04x}{:02x}{:02x}{:04x}{:02x}02", Common::swap16(model_info.character_id), + model_info.character_variant, model_info.amiibo_type, model_info.model_number, + model_info.series); + + LOG_DEBUG(Frontend, "Loading amiibo id {}", amiibo_id); + // Note: This function is not being used until we host the images on our server + // LoadAmiiboApiInfo(amiibo_id); + ui->amiiboImageLabel->setVisible(false); + ui->amiiboInfoGroup->setVisible(false); +} + +void QtAmiiboSettingsDialog::LoadAmiiboApiInfo(std::string_view amiibo_id) { +#ifdef ENABLE_WEB_SERVICE + // TODO: Host this data on our website + WebService::Client client{"https://amiiboapi.com", {}, {}}; + WebService::Client image_client{"https://raw.githubusercontent.com", {}, {}}; + const auto url_path = fmt::format("/api/amiibo/?id={}", amiibo_id); + + const auto amiibo_json = client.GetJson(url_path, true).returned_data; + if (amiibo_json.empty()) { + ui->amiiboImageLabel->setVisible(false); + ui->amiiboInfoGroup->setVisible(false); + return; + } + + std::string amiibo_series{}; + std::string amiibo_name{}; + std::string amiibo_image_url{}; + std::string amiibo_type{}; + + const auto parsed_amiibo_json_json = nlohmann::json::parse(amiibo_json).at("amiibo"); + parsed_amiibo_json_json.at("amiiboSeries").get_to(amiibo_series); + parsed_amiibo_json_json.at("name").get_to(amiibo_name); + parsed_amiibo_json_json.at("image").get_to(amiibo_image_url); + parsed_amiibo_json_json.at("type").get_to(amiibo_type); + + ui->amiiboSeriesValue->setText(QString::fromStdString(amiibo_series)); + ui->amiiboNameValue->setText(QString::fromStdString(amiibo_name)); + ui->amiiboTypeValue->setText(QString::fromStdString(amiibo_type)); + + if (amiibo_image_url.size() < 34) { + ui->amiiboImageLabel->setVisible(false); + } + + const auto image_url_path = amiibo_image_url.substr(34, amiibo_image_url.size() - 34); + const auto image_data = image_client.GetImage(image_url_path, true).returned_data; + + if (image_data.empty()) { + ui->amiiboImageLabel->setVisible(false); + } + + QPixmap pixmap; + pixmap.loadFromData(reinterpret_cast(image_data.data()), + static_cast(image_data.size())); + pixmap = pixmap.scaled(250, 350, Qt::AspectRatioMode::KeepAspectRatio, + Qt::TransformationMode::SmoothTransformation); + ui->amiiboImageLabel->setPixmap(pixmap); +#endif +} + +void QtAmiiboSettingsDialog::LoadAmiiboData() { + Service::NFP::RegisterInfo register_info{}; + Service::NFP::CommonInfo common_info{}; + const auto register_result = nfp_device->GetRegisterInfo(register_info); + const auto common_result = nfp_device->GetCommonInfo(common_info); + + if (register_result.IsFailure()) { + ui->creationDateValue->setDisabled(true); + ui->modificationDateValue->setDisabled(true); + ui->amiiboCustomNameValue->setReadOnly(false); + ui->amiiboOwnerValue->setReadOnly(false); + return; + } + + if (parameters.mode == Service::NFP::CabinetMode::StartNicknameAndOwnerSettings) { + ui->creationDateValue->setDisabled(true); + ui->modificationDateValue->setDisabled(true); + } + + const auto amiibo_name = std::string(register_info.amiibo_name.data()); + const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data()); + const auto creation_date = + QDate(register_info.creation_date.year, register_info.creation_date.month, + register_info.creation_date.day); + + ui->amiiboCustomNameValue->setText(QString::fromStdString(amiibo_name)); + ui->amiiboOwnerValue->setText(QString::fromStdString(owner_name)); + ui->amiiboCustomNameValue->setReadOnly(true); + ui->amiiboOwnerValue->setReadOnly(true); + ui->creationDateValue->setDate(creation_date); + + if (common_result.IsFailure()) { + ui->modificationDateValue->setDisabled(true); + return; + } + + const auto modification_date = + QDate(common_info.last_write_date.year, common_info.last_write_date.month, + common_info.last_write_date.day); + ui->modificationDateValue->setDate(modification_date); +} + +void QtAmiiboSettingsDialog::LoadAmiiboGameInfo() { + u32 application_area_id{}; + const auto application_result = nfp_device->GetApplicationAreaId(application_area_id); + + if (application_result.IsFailure()) { + ui->gameIdValue->setVisible(false); + ui->gameIdLabel->setText(tr("No game data present")); + return; + } + + SetGameDataName(application_area_id); +} + +void QtAmiiboSettingsDialog::SetGameDataName(u32 application_area_id) { + static constexpr std::array, 12> game_name_list = { + // 3ds, wii u + std::pair{0x10110E00, "Super Smash Bros (3DS/WiiU)"}, + {0x00132600, "Mario & Luigi: Paper Jam"}, + {0x0014F000, "Animal Crossing: Happy Home Designer"}, + {0x00152600, "Chibi-Robo!: Zip Lash"}, + {0x10161f00, "Mario Party 10"}, + {0x1019C800, "The Legend of Zelda: Twilight Princess HD"}, + // switch + {0x10162B00, "Splatoon 2"}, + {0x1016e100, "Shovel Knight: Treasure Trove"}, + {0x1019C800, "The Legend of Zelda: Breath of the Wild"}, + {0x34F80200, "Super Smash Bros. Ultimate"}, + {0x38600500, "Splatoon 3"}, + {0x3B440400, "The Legend of Zelda: Link's Awakening"}, + }; + + for (const auto& [game_id, game_name] : game_name_list) { + if (application_area_id == game_id) { + ui->gameIdValue->setText(QString::fromStdString(game_name)); + return; + } + } + + const auto application_area_string = fmt::format("{:016x}", application_area_id); + ui->gameIdValue->setText(QString::fromStdString(application_area_string)); +} + +void QtAmiiboSettingsDialog::SetSettingsDescription() { + switch (parameters.mode) { + case Service::NFP::CabinetMode::StartFormatter: + ui->cabinetActionDescriptionLabel->setText( + tr("The following amiibo data will be formatted:")); + break; + case Service::NFP::CabinetMode::StartGameDataEraser: + ui->cabinetActionDescriptionLabel->setText(tr("The following game data will removed:")); + break; + case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: + ui->cabinetActionDescriptionLabel->setText(tr("Set nickname and owner:")); + break; + case Service::NFP::CabinetMode::StartRestorer: + ui->cabinetActionDescriptionLabel->setText(tr("Do you wish to restore this amiibo?")); + break; + } +} + +QtAmiiboSettings::QtAmiiboSettings(GMainWindow& parent) { + connect(this, &QtAmiiboSettings::MainWindowShowAmiiboSettings, &parent, + &GMainWindow::AmiiboSettingsShowDialog, Qt::QueuedConnection); + connect(&parent, &GMainWindow::AmiiboSettingsFinished, this, + &QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection); +} + +QtAmiiboSettings::~QtAmiiboSettings() = default; + +void QtAmiiboSettings::ShowCabinetApplet( + const Core::Frontend::CabinetCallback& callback_, + const Core::Frontend::CabinetParameters& parameters, + std::shared_ptr nfp_device) const { + callback = std::move(callback_); + emit MainWindowShowAmiiboSettings(parameters, nfp_device); +} + +void QtAmiiboSettings::MainWindowFinished(bool is_success, const std::string& name) { + callback(is_success, name); +} diff --git a/src/yuzu/applets/qt_amiibo_settings.h b/src/yuzu/applets/qt_amiibo_settings.h new file mode 100644 index 0000000..930c967 --- /dev/null +++ b/src/yuzu/applets/qt_amiibo_settings.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "core/frontend/applets/cabinet.h" + +class GMainWindow; +class QCheckBox; +class QComboBox; +class QDialogButtonBox; +class QGroupBox; +class QLabel; + +namespace InputCommon { +class InputSubsystem; +} + +namespace Ui { +class QtAmiiboSettingsDialog; +} + +namespace Service::NFP { +class NfpDevice; +} // namespace Service::NFP + +class QtAmiiboSettingsDialog final : public QDialog { + Q_OBJECT + +public: + explicit QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_, + InputCommon::InputSubsystem* input_subsystem_, + std::shared_ptr nfp_device_); + ~QtAmiiboSettingsDialog() override; + + int exec() override; + + std::string GetName() const; + +private: + void LoadInfo(); + void LoadAmiiboInfo(); + void LoadAmiiboApiInfo(std::string_view amiibo_id); + void LoadAmiiboData(); + void LoadAmiiboGameInfo(); + void SetGameDataName(u32 application_area_id); + void SetSettingsDescription(); + + std::unique_ptr ui; + + InputCommon::InputSubsystem* input_subsystem; + std::shared_ptr nfp_device; + + // Parameters sent in from the backend HLE applet. + Core::Frontend::CabinetParameters parameters; + + // If false amiibo settings failed to load + bool is_initalized{}; +}; + +class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApplet { + Q_OBJECT + +public: + explicit QtAmiiboSettings(GMainWindow& parent); + ~QtAmiiboSettings() override; + + void ShowCabinetApplet(const Core::Frontend::CabinetCallback& callback_, + const Core::Frontend::CabinetParameters& parameters, + std::shared_ptr nfp_device) const override; + +signals: + void MainWindowShowAmiiboSettings(const Core::Frontend::CabinetParameters& parameters, + std::shared_ptr nfp_device) const; + +private: + void MainWindowFinished(bool is_success, const std::string& name); + + mutable Core::Frontend::CabinetCallback callback; +}; diff --git a/src/yuzu/applets/qt_amiibo_settings.ui b/src/yuzu/applets/qt_amiibo_settings.ui new file mode 100644 index 0000000..f377a6e --- /dev/null +++ b/src/yuzu/applets/qt_amiibo_settings.ui @@ -0,0 +1,494 @@ + + + QtAmiiboSettingsDialog + + + + 0 + 0 + 839 + 500 + + + + Amiibo Settings + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 10 + + + 20 + + + 15 + + + 0 + + + 15 + + + + + + 12 + 75 + true + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 20 + + + 15 + + + 15 + + + + + + 250 + 350 + + + + + 236 + 350 + + + + + + + Qt::AlignCenter + + + + + + + 0 + + + 8 + + + 15 + + + + + Amiibo Info + + + + + + + + Series + + + + + + + + 0 + 0 + + + + true + + + + + + + Type + + + + + + + + 0 + 0 + + + + true + + + + + + + Name + + + + + + + + 0 + 0 + + + + true + + + + + + + + + + + + Amiibo Data + + + + + + + + Custom Name + + + + + + + + 0 + 0 + + + + 10 + + + + + + + Owner + + + + + + + + 0 + 0 + + + + 10 + + + + + + + Creation Date + + + + + + + true + + + + 1970 + 1 + 1 + + + + dd/MM/yyyy + + + + + + + Modification Date + + + + + + + true + + + + 1970 + 1 + 1 + + + + dd/MM/yyyy + + + + + + + + + + + + + 500 + 0 + + + + Game Data + + + + + + Game Id + + + + + + + + 0 + 0 + + + + true + + + + + + + + + + + 500 + 0 + + + + Mount Amiibo + + + + + + ... + + + + + + + Qt::Horizontal + + + QSizePolicy::Maximum + + + + 60 + 20 + + + + + + + + File Path + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + 15 + + + 15 + + + 8 + + + 20 + + + 8 + + + + + true + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + + + + + buttonBox + accepted() + QtAmiiboSettingsDialog + accept() + + + buttonBox + rejected() + QtAmiiboSettingsDialog + reject() + + + diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index 1d80722..c30b544 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -291,7 +291,7 @@ bool QtControllerSelectorDialog::CheckIfParametersMet() { // Here, we check and validate the current configuration against all applicable parameters. const auto num_connected_players = static_cast( std::count_if(player_groupboxes.begin(), player_groupboxes.end(), - [this](const QGroupBox* player) { return player->isChecked(); })); + [](const QGroupBox* player) { return player->isChecked(); })); const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players; const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; @@ -685,7 +685,7 @@ QtControllerSelector::QtControllerSelector(GMainWindow& parent) { QtControllerSelector::~QtControllerSelector() = default; void QtControllerSelector::ReconfigureControllers( - std::function callback_, const Core::Frontend::ControllerParameters& parameters) const { + ReconfigureCallback callback_, const Core::Frontend::ControllerParameters& parameters) const { callback = std::move(callback_); emit MainWindowReconfigureControllers(parameters); } diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h index cf948d2..16e99f5 100644 --- a/src/yuzu/applets/qt_controller.h +++ b/src/yuzu/applets/qt_controller.h @@ -157,7 +157,7 @@ public: ~QtControllerSelector() override; void ReconfigureControllers( - std::function callback_, + ReconfigureCallback callback_, const Core::Frontend::ControllerParameters& parameters) const override; signals: @@ -167,5 +167,5 @@ signals: private: void MainWindowReconfigureFinished(); - mutable std::function callback; + mutable ReconfigureCallback callback; }; diff --git a/src/yuzu/applets/qt_controller.ui b/src/yuzu/applets/qt_controller.ui index c8cb6bc..f5eccba 100644 --- a/src/yuzu/applets/qt_controller.ui +++ b/src/yuzu/applets/qt_controller.ui @@ -2300,7 +2300,7 @@ - Undocked + Handheld diff --git a/src/yuzu/applets/qt_error.cpp b/src/yuzu/applets/qt_error.cpp index 367d535..e0190a9 100644 --- a/src/yuzu/applets/qt_error.cpp +++ b/src/yuzu/applets/qt_error.cpp @@ -14,7 +14,7 @@ QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) { QtErrorDisplay::~QtErrorDisplay() = default; -void QtErrorDisplay::ShowError(Result error, std::function finished) const { +void QtErrorDisplay::ShowError(Result error, FinishedCallback finished) const { callback = std::move(finished); emit MainWindowDisplayError( tr("Error Code: %1-%2 (0x%3)") @@ -25,7 +25,7 @@ void QtErrorDisplay::ShowError(Result error, std::function finished) con } void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds time, - std::function finished) const { + FinishedCallback finished) const { callback = std::move(finished); const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); @@ -42,7 +42,7 @@ void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds t void QtErrorDisplay::ShowCustomErrorText(Result error, std::string dialog_text, std::string fullscreen_text, - std::function finished) const { + FinishedCallback finished) const { callback = std::move(finished); emit MainWindowDisplayError( tr("Error Code: %1-%2 (0x%3)") diff --git a/src/yuzu/applets/qt_error.h b/src/yuzu/applets/qt_error.h index eb4107c..e4e1747 100644 --- a/src/yuzu/applets/qt_error.h +++ b/src/yuzu/applets/qt_error.h @@ -16,11 +16,11 @@ public: explicit QtErrorDisplay(GMainWindow& parent); ~QtErrorDisplay() override; - void ShowError(Result error, std::function finished) const override; + void ShowError(Result error, FinishedCallback finished) const override; void ShowErrorWithTimestamp(Result error, std::chrono::seconds time, - std::function finished) const override; + FinishedCallback finished) const override; void ShowCustomErrorText(Result error, std::string dialog_text, std::string fullscreen_text, - std::function finished) const override; + FinishedCallback finished) const override; signals: void MainWindowDisplayError(QString error_code, QString error_text) const; @@ -28,5 +28,5 @@ signals: private: void MainWindowFinishedError(); - mutable std::function callback; + mutable FinishedCallback callback; }; diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp index c8bcfb2..4145c52 100644 --- a/src/yuzu/applets/qt_profile_select.cpp +++ b/src/yuzu/applets/qt_profile_select.cpp @@ -163,8 +163,7 @@ QtProfileSelector::QtProfileSelector(GMainWindow& parent) { QtProfileSelector::~QtProfileSelector() = default; -void QtProfileSelector::SelectProfile( - std::function)> callback_) const { +void QtProfileSelector::SelectProfile(SelectProfileCallback callback_) const { callback = std::move(callback_); emit MainWindowSelectProfile(); } diff --git a/src/yuzu/applets/qt_profile_select.h b/src/yuzu/applets/qt_profile_select.h index 124f2cd..637a3bd 100644 --- a/src/yuzu/applets/qt_profile_select.h +++ b/src/yuzu/applets/qt_profile_select.h @@ -65,7 +65,7 @@ public: explicit QtProfileSelector(GMainWindow& parent); ~QtProfileSelector() override; - void SelectProfile(std::function)> callback_) const override; + void SelectProfile(SelectProfileCallback callback_) const override; signals: void MainWindowSelectProfile() const; @@ -73,5 +73,5 @@ signals: private: void MainWindowFinishedSelection(std::optional uuid); - mutable std::function)> callback; + mutable SelectProfileCallback callback; }; diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp index e605061..734b0ea 100644 --- a/src/yuzu/applets/qt_software_keyboard.cpp +++ b/src/yuzu/applets/qt_software_keyboard.cpp @@ -1566,10 +1566,7 @@ QtSoftwareKeyboard::~QtSoftwareKeyboard() = default; void QtSoftwareKeyboard::InitializeKeyboard( bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters, - std::function - submit_normal_callback_, - std::function - submit_inline_callback_) { + SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) { if (is_inline) { submit_inline_callback = std::move(submit_inline_callback_); } else { diff --git a/src/yuzu/applets/qt_software_keyboard.h b/src/yuzu/applets/qt_software_keyboard.h index 35d4ee2..30ac8ec 100644 --- a/src/yuzu/applets/qt_software_keyboard.h +++ b/src/yuzu/applets/qt_software_keyboard.h @@ -233,12 +233,10 @@ public: explicit QtSoftwareKeyboard(GMainWindow& parent); ~QtSoftwareKeyboard() override; - void InitializeKeyboard( - bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters, - std::function - submit_normal_callback_, - std::function - submit_inline_callback_) override; + void InitializeKeyboard(bool is_inline, + Core::Frontend::KeyboardInitializeParameters initialize_parameters, + SubmitNormalCallback submit_normal_callback_, + SubmitInlineCallback submit_inline_callback_) override; void ShowNormalKeyboard() const override; @@ -279,8 +277,6 @@ private: void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) const; - mutable std::function - submit_normal_callback; - mutable std::function - submit_inline_callback; + mutable SubmitNormalCallback submit_normal_callback; + mutable SubmitInlineCallback submit_inline_callback; }; diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp index 89bd482..0a59123 100644 --- a/src/yuzu/applets/qt_web_browser.cpp +++ b/src/yuzu/applets/qt_web_browser.cpp @@ -401,9 +401,9 @@ QtWebBrowser::QtWebBrowser(GMainWindow& main_window) { QtWebBrowser::~QtWebBrowser() = default; -void QtWebBrowser::OpenLocalWebPage( - const std::string& local_url, std::function extract_romfs_callback_, - std::function callback_) const { +void QtWebBrowser::OpenLocalWebPage(const std::string& local_url, + ExtractROMFSCallback extract_romfs_callback_, + OpenWebPageCallback callback_) const { extract_romfs_callback = std::move(extract_romfs_callback_); callback = std::move(callback_); @@ -416,9 +416,8 @@ void QtWebBrowser::OpenLocalWebPage( } } -void QtWebBrowser::OpenExternalWebPage( - const std::string& external_url, - std::function callback_) const { +void QtWebBrowser::OpenExternalWebPage(const std::string& external_url, + OpenWebPageCallback callback_) const { callback = std::move(callback_); const auto index = external_url.find('?'); diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h index 0438008..e8fe511 100644 --- a/src/yuzu/applets/qt_web_browser.h +++ b/src/yuzu/applets/qt_web_browser.h @@ -197,13 +197,11 @@ public: ~QtWebBrowser() override; void OpenLocalWebPage(const std::string& local_url, - std::function extract_romfs_callback_, - std::function - callback_) const override; + ExtractROMFSCallback extract_romfs_callback_, + OpenWebPageCallback callback_) const override; void OpenExternalWebPage(const std::string& external_url, - std::function - callback_) const override; + OpenWebPageCallback callback_) const override; signals: void MainWindowOpenWebPage(const std::string& main_url, const std::string& additional_args, @@ -215,7 +213,6 @@ private: void MainWindowWebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url); - mutable std::function extract_romfs_callback; - - mutable std::function callback; + mutable ExtractROMFSCallback extract_romfs_callback; + mutable OpenWebPageCallback callback; }; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index d3fbdb0..1368b20 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -4,8 +4,10 @@ #include #include +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA #include #include +#endif #include #include #include @@ -42,12 +44,14 @@ #include "yuzu/bootmanager.h" #include "yuzu/main.h" +static Core::Frontend::WindowSystemType GetWindowSystemType(); + EmuThread::EmuThread(Core::System& system_) : system{system_} {} EmuThread::~EmuThread() = default; void EmuThread::run() { - std::string name = "yuzu:EmuControlThread"; + std::string name = "EmuControlThread"; MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); @@ -59,8 +63,6 @@ void EmuThread::run() { // Main process has been loaded. Make the context current to this thread and begin GPU and CPU // execution. - gpu.Start(); - gpu.ObtainContext(); emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); @@ -75,9 +77,15 @@ void EmuThread::run() { emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); gpu.ReleaseContext(); + gpu.Start(); system.GetCpuManager().OnGpuReady(); + system.RegisterExitCallback([this]() { + stop_source.request_stop(); + SetRunning(false); + }); + // Holds whether the cpu was running during the last iteration, // so that the DebugModeLeft signal can be emitted before the // next execution step @@ -116,12 +124,12 @@ void EmuThread::run() { } } else { std::unique_lock lock{running_mutex}; - running_cv.wait(lock, stop_token, [this] { return IsRunning(); }); + Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); }); } } - // Shutdown the core emulation - system.Shutdown(); + // Shutdown the main emulated process + system.ShutdownMainProcess(); #if MICROPROFILE_ENABLED MicroProfileOnThreadExit(); @@ -223,6 +231,9 @@ public: explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { setAttribute(Qt::WA_NativeWindow); setAttribute(Qt::WA_PaintOnScreen); + if (GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) { + setAttribute(Qt::WA_DontCreateNativeAncestors); + } } virtual ~RenderWidget() = default; @@ -235,8 +246,7 @@ private: GRenderWindow* render_window; }; -class OpenGLRenderWidget : public RenderWidget { -public: +struct OpenGLRenderWidget : public RenderWidget { explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { windowHandle()->setSurfaceType(QWindow::OpenGLSurface); } @@ -249,13 +259,16 @@ private: std::unique_ptr context; }; -class VulkanRenderWidget : public RenderWidget { -public: +struct VulkanRenderWidget : public RenderWidget { explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { windowHandle()->setSurfaceType(QWindow::VulkanSurface); } }; +struct NullRenderWidget : public RenderWidget { + explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {} +}; + static Core::Frontend::WindowSystemType GetWindowSystemType() { // Determine WSI type based on Qt platform. QString platform_name = QGuiApplication::platformName(); @@ -265,8 +278,14 @@ static Core::Frontend::WindowSystemType GetWindowSystemType() { return Core::Frontend::WindowSystemType::X11; else if (platform_name == QStringLiteral("wayland")) return Core::Frontend::WindowSystemType::Wayland; + else if (platform_name == QStringLiteral("wayland-egl")) + return Core::Frontend::WindowSystemType::Wayland; + else if (platform_name == QStringLiteral("cocoa")) + return Core::Frontend::WindowSystemType::Cocoa; + else if (platform_name == QStringLiteral("android")) + return Core::Frontend::WindowSystemType::Android; - LOG_CRITICAL(Frontend, "Unknown Qt platform!"); + LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString()); return Core::Frontend::WindowSystemType::Windows; } @@ -306,6 +325,9 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, input_subsystem->Initialize(); this->setMouseTracking(true); + strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") || + QGuiApplication::platformName() == QStringLiteral("wayland-egl"); + connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, Qt::QueuedConnection); @@ -401,224 +423,127 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { } int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { - switch (qt_key) { - case Qt::Key_A: - return Settings::NativeKeyboard::A; - case Qt::Key_B: - return Settings::NativeKeyboard::B; - case Qt::Key_C: - return Settings::NativeKeyboard::C; - case Qt::Key_D: - return Settings::NativeKeyboard::D; - case Qt::Key_E: - return Settings::NativeKeyboard::E; - case Qt::Key_F: - return Settings::NativeKeyboard::F; - case Qt::Key_G: - return Settings::NativeKeyboard::G; - case Qt::Key_H: - return Settings::NativeKeyboard::H; - case Qt::Key_I: - return Settings::NativeKeyboard::I; - case Qt::Key_J: - return Settings::NativeKeyboard::J; - case Qt::Key_K: - return Settings::NativeKeyboard::K; - case Qt::Key_L: - return Settings::NativeKeyboard::L; - case Qt::Key_M: - return Settings::NativeKeyboard::M; - case Qt::Key_N: - return Settings::NativeKeyboard::N; - case Qt::Key_O: - return Settings::NativeKeyboard::O; - case Qt::Key_P: - return Settings::NativeKeyboard::P; - case Qt::Key_Q: - return Settings::NativeKeyboard::Q; - case Qt::Key_R: - return Settings::NativeKeyboard::R; - case Qt::Key_S: - return Settings::NativeKeyboard::S; - case Qt::Key_T: - return Settings::NativeKeyboard::T; - case Qt::Key_U: - return Settings::NativeKeyboard::U; - case Qt::Key_V: - return Settings::NativeKeyboard::V; - case Qt::Key_W: - return Settings::NativeKeyboard::W; - case Qt::Key_X: - return Settings::NativeKeyboard::X; - case Qt::Key_Y: - return Settings::NativeKeyboard::Y; - case Qt::Key_Z: - return Settings::NativeKeyboard::Z; - case Qt::Key_1: - return Settings::NativeKeyboard::N1; - case Qt::Key_2: - return Settings::NativeKeyboard::N2; - case Qt::Key_3: - return Settings::NativeKeyboard::N3; - case Qt::Key_4: - return Settings::NativeKeyboard::N4; - case Qt::Key_5: - return Settings::NativeKeyboard::N5; - case Qt::Key_6: - return Settings::NativeKeyboard::N6; - case Qt::Key_7: - return Settings::NativeKeyboard::N7; - case Qt::Key_8: - return Settings::NativeKeyboard::N8; - case Qt::Key_9: - return Settings::NativeKeyboard::N9; - case Qt::Key_0: - return Settings::NativeKeyboard::N0; - case Qt::Key_Return: - return Settings::NativeKeyboard::Return; - case Qt::Key_Escape: - return Settings::NativeKeyboard::Escape; - case Qt::Key_Backspace: - return Settings::NativeKeyboard::Backspace; - case Qt::Key_Tab: - return Settings::NativeKeyboard::Tab; - case Qt::Key_Space: - return Settings::NativeKeyboard::Space; - case Qt::Key_Minus: - return Settings::NativeKeyboard::Minus; - case Qt::Key_Plus: - case Qt::Key_questiondown: - return Settings::NativeKeyboard::Plus; - case Qt::Key_BracketLeft: - case Qt::Key_BraceLeft: - return Settings::NativeKeyboard::OpenBracket; - case Qt::Key_BracketRight: - case Qt::Key_BraceRight: - return Settings::NativeKeyboard::CloseBracket; - case Qt::Key_Bar: - return Settings::NativeKeyboard::Pipe; - case Qt::Key_Dead_Tilde: - return Settings::NativeKeyboard::Tilde; - case Qt::Key_Ntilde: - case Qt::Key_Semicolon: - return Settings::NativeKeyboard::Semicolon; - case Qt::Key_Apostrophe: - return Settings::NativeKeyboard::Quote; - case Qt::Key_Dead_Grave: - return Settings::NativeKeyboard::Backquote; - case Qt::Key_Comma: - return Settings::NativeKeyboard::Comma; - case Qt::Key_Period: - return Settings::NativeKeyboard::Period; - case Qt::Key_Slash: - return Settings::NativeKeyboard::Slash; - case Qt::Key_CapsLock: - return Settings::NativeKeyboard::CapsLock; - case Qt::Key_F1: - return Settings::NativeKeyboard::F1; - case Qt::Key_F2: - return Settings::NativeKeyboard::F2; - case Qt::Key_F3: - return Settings::NativeKeyboard::F3; - case Qt::Key_F4: - return Settings::NativeKeyboard::F4; - case Qt::Key_F5: - return Settings::NativeKeyboard::F5; - case Qt::Key_F6: - return Settings::NativeKeyboard::F6; - case Qt::Key_F7: - return Settings::NativeKeyboard::F7; - case Qt::Key_F8: - return Settings::NativeKeyboard::F8; - case Qt::Key_F9: - return Settings::NativeKeyboard::F9; - case Qt::Key_F10: - return Settings::NativeKeyboard::F10; - case Qt::Key_F11: - return Settings::NativeKeyboard::F11; - case Qt::Key_F12: - return Settings::NativeKeyboard::F12; - case Qt::Key_Print: - return Settings::NativeKeyboard::PrintScreen; - case Qt::Key_ScrollLock: - return Settings::NativeKeyboard::ScrollLock; - case Qt::Key_Pause: - return Settings::NativeKeyboard::Pause; - case Qt::Key_Insert: - return Settings::NativeKeyboard::Insert; - case Qt::Key_Home: - return Settings::NativeKeyboard::Home; - case Qt::Key_PageUp: - return Settings::NativeKeyboard::PageUp; - case Qt::Key_Delete: - return Settings::NativeKeyboard::Delete; - case Qt::Key_End: - return Settings::NativeKeyboard::End; - case Qt::Key_PageDown: - return Settings::NativeKeyboard::PageDown; - case Qt::Key_Right: - return Settings::NativeKeyboard::Right; - case Qt::Key_Left: - return Settings::NativeKeyboard::Left; - case Qt::Key_Down: - return Settings::NativeKeyboard::Down; - case Qt::Key_Up: - return Settings::NativeKeyboard::Up; - case Qt::Key_NumLock: - return Settings::NativeKeyboard::NumLock; - // Numpad keys are missing here - case Qt::Key_F13: - return Settings::NativeKeyboard::F13; - case Qt::Key_F14: - return Settings::NativeKeyboard::F14; - case Qt::Key_F15: - return Settings::NativeKeyboard::F15; - case Qt::Key_F16: - return Settings::NativeKeyboard::F16; - case Qt::Key_F17: - return Settings::NativeKeyboard::F17; - case Qt::Key_F18: - return Settings::NativeKeyboard::F18; - case Qt::Key_F19: - return Settings::NativeKeyboard::F19; - case Qt::Key_F20: - return Settings::NativeKeyboard::F20; - case Qt::Key_F21: - return Settings::NativeKeyboard::F21; - case Qt::Key_F22: - return Settings::NativeKeyboard::F22; - case Qt::Key_F23: - return Settings::NativeKeyboard::F23; - case Qt::Key_F24: - return Settings::NativeKeyboard::F24; - // case Qt::: - // return Settings::NativeKeyboard::KPComma; - // case Qt::: - // return Settings::NativeKeyboard::Ro; - case Qt::Key_Hiragana_Katakana: - return Settings::NativeKeyboard::KatakanaHiragana; - case Qt::Key_yen: - return Settings::NativeKeyboard::Yen; - case Qt::Key_Henkan: - return Settings::NativeKeyboard::Henkan; - case Qt::Key_Muhenkan: - return Settings::NativeKeyboard::Muhenkan; - // case Qt::: - // return Settings::NativeKeyboard::NumPadCommaPc98; - case Qt::Key_Hangul: - return Settings::NativeKeyboard::HangulEnglish; - case Qt::Key_Hangul_Hanja: - return Settings::NativeKeyboard::Hanja; - case Qt::Key_Katakana: - return Settings::NativeKeyboard::KatakanaKey; - case Qt::Key_Hiragana: - return Settings::NativeKeyboard::HiraganaKey; - case Qt::Key_Zenkaku_Hankaku: - return Settings::NativeKeyboard::ZenkakuHankaku; - // Modifier keys are handled by the modifier property - default: - return Settings::NativeKeyboard::None; + static constexpr std::array, 106> key_map = { + std::pair{Qt::Key_A, Settings::NativeKeyboard::A}, + {Qt::Key_A, Settings::NativeKeyboard::A}, + {Qt::Key_B, Settings::NativeKeyboard::B}, + {Qt::Key_C, Settings::NativeKeyboard::C}, + {Qt::Key_D, Settings::NativeKeyboard::D}, + {Qt::Key_E, Settings::NativeKeyboard::E}, + {Qt::Key_F, Settings::NativeKeyboard::F}, + {Qt::Key_G, Settings::NativeKeyboard::G}, + {Qt::Key_H, Settings::NativeKeyboard::H}, + {Qt::Key_I, Settings::NativeKeyboard::I}, + {Qt::Key_J, Settings::NativeKeyboard::J}, + {Qt::Key_K, Settings::NativeKeyboard::K}, + {Qt::Key_L, Settings::NativeKeyboard::L}, + {Qt::Key_M, Settings::NativeKeyboard::M}, + {Qt::Key_N, Settings::NativeKeyboard::N}, + {Qt::Key_O, Settings::NativeKeyboard::O}, + {Qt::Key_P, Settings::NativeKeyboard::P}, + {Qt::Key_Q, Settings::NativeKeyboard::Q}, + {Qt::Key_R, Settings::NativeKeyboard::R}, + {Qt::Key_S, Settings::NativeKeyboard::S}, + {Qt::Key_T, Settings::NativeKeyboard::T}, + {Qt::Key_U, Settings::NativeKeyboard::U}, + {Qt::Key_V, Settings::NativeKeyboard::V}, + {Qt::Key_W, Settings::NativeKeyboard::W}, + {Qt::Key_X, Settings::NativeKeyboard::X}, + {Qt::Key_Y, Settings::NativeKeyboard::Y}, + {Qt::Key_Z, Settings::NativeKeyboard::Z}, + {Qt::Key_1, Settings::NativeKeyboard::N1}, + {Qt::Key_2, Settings::NativeKeyboard::N2}, + {Qt::Key_3, Settings::NativeKeyboard::N3}, + {Qt::Key_4, Settings::NativeKeyboard::N4}, + {Qt::Key_5, Settings::NativeKeyboard::N5}, + {Qt::Key_6, Settings::NativeKeyboard::N6}, + {Qt::Key_7, Settings::NativeKeyboard::N7}, + {Qt::Key_8, Settings::NativeKeyboard::N8}, + {Qt::Key_9, Settings::NativeKeyboard::N9}, + {Qt::Key_0, Settings::NativeKeyboard::N0}, + {Qt::Key_Return, Settings::NativeKeyboard::Return}, + {Qt::Key_Escape, Settings::NativeKeyboard::Escape}, + {Qt::Key_Backspace, Settings::NativeKeyboard::Backspace}, + {Qt::Key_Tab, Settings::NativeKeyboard::Tab}, + {Qt::Key_Space, Settings::NativeKeyboard::Space}, + {Qt::Key_Minus, Settings::NativeKeyboard::Minus}, + {Qt::Key_Plus, Settings::NativeKeyboard::Plus}, + {Qt::Key_questiondown, Settings::NativeKeyboard::Plus}, + {Qt::Key_BracketLeft, Settings::NativeKeyboard::OpenBracket}, + {Qt::Key_BraceLeft, Settings::NativeKeyboard::OpenBracket}, + {Qt::Key_BracketRight, Settings::NativeKeyboard::CloseBracket}, + {Qt::Key_BraceRight, Settings::NativeKeyboard::CloseBracket}, + {Qt::Key_Bar, Settings::NativeKeyboard::Pipe}, + {Qt::Key_Dead_Tilde, Settings::NativeKeyboard::Tilde}, + {Qt::Key_Ntilde, Settings::NativeKeyboard::Semicolon}, + {Qt::Key_Semicolon, Settings::NativeKeyboard::Semicolon}, + {Qt::Key_Apostrophe, Settings::NativeKeyboard::Quote}, + {Qt::Key_Dead_Grave, Settings::NativeKeyboard::Backquote}, + {Qt::Key_Comma, Settings::NativeKeyboard::Comma}, + {Qt::Key_Period, Settings::NativeKeyboard::Period}, + {Qt::Key_Slash, Settings::NativeKeyboard::Slash}, + {Qt::Key_CapsLock, Settings::NativeKeyboard::CapsLockKey}, + {Qt::Key_F1, Settings::NativeKeyboard::F1}, + {Qt::Key_F2, Settings::NativeKeyboard::F2}, + {Qt::Key_F3, Settings::NativeKeyboard::F3}, + {Qt::Key_F4, Settings::NativeKeyboard::F4}, + {Qt::Key_F5, Settings::NativeKeyboard::F5}, + {Qt::Key_F6, Settings::NativeKeyboard::F6}, + {Qt::Key_F7, Settings::NativeKeyboard::F7}, + {Qt::Key_F8, Settings::NativeKeyboard::F8}, + {Qt::Key_F9, Settings::NativeKeyboard::F9}, + {Qt::Key_F10, Settings::NativeKeyboard::F10}, + {Qt::Key_F11, Settings::NativeKeyboard::F11}, + {Qt::Key_F12, Settings::NativeKeyboard::F12}, + {Qt::Key_Print, Settings::NativeKeyboard::PrintScreen}, + {Qt::Key_ScrollLock, Settings::NativeKeyboard::ScrollLockKey}, + {Qt::Key_Pause, Settings::NativeKeyboard::Pause}, + {Qt::Key_Insert, Settings::NativeKeyboard::Insert}, + {Qt::Key_Home, Settings::NativeKeyboard::Home}, + {Qt::Key_PageUp, Settings::NativeKeyboard::PageUp}, + {Qt::Key_Delete, Settings::NativeKeyboard::Delete}, + {Qt::Key_End, Settings::NativeKeyboard::End}, + {Qt::Key_PageDown, Settings::NativeKeyboard::PageDown}, + {Qt::Key_Right, Settings::NativeKeyboard::Right}, + {Qt::Key_Left, Settings::NativeKeyboard::Left}, + {Qt::Key_Down, Settings::NativeKeyboard::Down}, + {Qt::Key_Up, Settings::NativeKeyboard::Up}, + {Qt::Key_NumLock, Settings::NativeKeyboard::NumLockKey}, + // Numpad keys are missing here + {Qt::Key_F13, Settings::NativeKeyboard::F13}, + {Qt::Key_F14, Settings::NativeKeyboard::F14}, + {Qt::Key_F15, Settings::NativeKeyboard::F15}, + {Qt::Key_F16, Settings::NativeKeyboard::F16}, + {Qt::Key_F17, Settings::NativeKeyboard::F17}, + {Qt::Key_F18, Settings::NativeKeyboard::F18}, + {Qt::Key_F19, Settings::NativeKeyboard::F19}, + {Qt::Key_F20, Settings::NativeKeyboard::F20}, + {Qt::Key_F21, Settings::NativeKeyboard::F21}, + {Qt::Key_F22, Settings::NativeKeyboard::F22}, + {Qt::Key_F23, Settings::NativeKeyboard::F23}, + {Qt::Key_F24, Settings::NativeKeyboard::F24}, + // {Qt::..., Settings::NativeKeyboard::KPComma}, + // {Qt::..., Settings::NativeKeyboard::Ro}, + {Qt::Key_Hiragana_Katakana, Settings::NativeKeyboard::KatakanaHiragana}, + {Qt::Key_yen, Settings::NativeKeyboard::Yen}, + {Qt::Key_Henkan, Settings::NativeKeyboard::Henkan}, + {Qt::Key_Muhenkan, Settings::NativeKeyboard::Muhenkan}, + // {Qt::..., Settings::NativeKeyboard::NumPadCommaPc98}, + {Qt::Key_Hangul, Settings::NativeKeyboard::HangulEnglish}, + {Qt::Key_Hangul_Hanja, Settings::NativeKeyboard::Hanja}, + {Qt::Key_Katakana, Settings::NativeKeyboard::KatakanaKey}, + {Qt::Key_Hiragana, Settings::NativeKeyboard::HiraganaKey}, + {Qt::Key_Zenkaku_Hankaku, Settings::NativeKeyboard::ZenkakuHankaku}, + // Modifier keys are handled by the modifier property + }; + + for (const auto& [qkey, nkey] : key_map) { + if (qt_key == qkey) { + return nkey; + } } + + return Settings::NativeKeyboard::None; } int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers) { @@ -804,6 +729,7 @@ void GRenderWindow::TouchEndEvent() { } void GRenderWindow::InitializeCamera() { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA constexpr auto camera_update_ms = std::chrono::milliseconds{50}; // (50ms, 20Hz) if (!Settings::values.enable_ir_sensor) { return; @@ -838,6 +764,9 @@ void GRenderWindow::InitializeCamera() { return; } + const auto camera_width = input_subsystem->GetCamera()->getImageWidth(); + const auto camera_height = input_subsystem->GetCamera()->getImageHeight(); + camera_data.resize(camera_width * camera_height); camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer); connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, &GRenderWindow::OnCameraCapture); @@ -857,18 +786,22 @@ void GRenderWindow::InitializeCamera() { connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); }); // This timer should be dependent of camera resolution 5ms for every 100 pixels camera_timer->start(camera_update_ms); +#endif } void GRenderWindow::FinalizeCamera() { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA if (camera_timer) { camera_timer->stop(); } if (camera) { camera->unload(); } +#endif } void GRenderWindow::RequestCameraCapture() { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA if (!Settings::values.enable_ir_sensor) { return; } @@ -885,20 +818,26 @@ void GRenderWindow::RequestCameraCapture() { pending_camera_snapshots++; camera_capture->capture(); +#endif } void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) { - constexpr std::size_t camera_width = 320; - constexpr std::size_t camera_height = 240; +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA + // TODO: Capture directly in the format and resolution needed + const auto camera_width = input_subsystem->GetCamera()->getImageWidth(); + const auto camera_height = input_subsystem->GetCamera()->getImageHeight(); const auto converted = - img.scaled(camera_width, camera_height, Qt::AspectRatioMode::IgnoreAspectRatio, + img.scaled(static_cast(camera_width), static_cast(camera_height), + Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation) .mirrored(false, true); - std::vector camera_data{}; - camera_data.resize(camera_width * camera_height); + if (camera_data.size() != camera_width * camera_height) { + camera_data.resize(camera_width * camera_height); + } std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32)); input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data); pending_camera_snapshots = 0; +#endif } bool GRenderWindow::event(QEvent* event) { @@ -963,6 +902,9 @@ bool GRenderWindow::InitRenderTarget() { return false; } break; + case Settings::RendererBackend::Null: + InitializeNull(); + break; } // Update the Window System information with the new render target @@ -1032,6 +974,12 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair minimal bool GRenderWindow::InitializeOpenGL() { #ifdef HAS_OPENGL + if (!QOpenGLContext::supportsThreadedOpenGL()) { + QMessageBox::warning(this, tr("OpenGL not available!"), + tr("OpenGL shared contexts are not supported.")); + return false; + } + // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, // WA_DontShowOnScreen, WA_DeleteOnClose auto child = new OpenGLRenderWidget(this); @@ -1059,6 +1007,11 @@ bool GRenderWindow::InitializeVulkan() { return true; } +void GRenderWindow::InitializeNull() { + child_widget = new NullRenderWidget(this); + main_context = std::make_unique(); +} + bool GRenderWindow::LoadOpenGL() { auto context = CreateSharedContext(); auto scope = context->Acquire(); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index c45ebf1..b24141f 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -14,6 +14,7 @@ #include #include +#include "common/polyfill_thread.h" #include "common/thread.h" #include "core/frontend/emu_window.h" @@ -83,9 +84,10 @@ public: } /** - * Requests for the emulation thread to stop running + * Requests for the emulation thread to immediately stop running */ - void RequestStop() { + void ForceStop() { + LOG_WARNING(Frontend, "Force stopping EmuThread"); stop_source.request_stop(); SetRunning(false); } @@ -218,6 +220,7 @@ private: bool InitializeOpenGL(); bool InitializeVulkan(); + void InitializeNull(); bool LoadOpenGL(); QStringList GetUnsupportedGLExtensions() const; @@ -239,11 +242,14 @@ private: bool first_frame = false; InputCommon::TasInput::TasState last_tas_state; +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA bool is_virtual_camera; int pending_camera_snapshots; + std::vector camera_data; std::unique_ptr camera; std::unique_ptr camera_capture; std::unique_ptr camera_timer; +#endif Core::System& system; diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp index f46fff3..05f49c0 100644 --- a/src/yuzu/compatdb.cpp +++ b/src/yuzu/compatdb.cpp @@ -15,12 +15,22 @@ CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent) : QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), ui{std::make_unique()}, telemetry_session{telemetry_session_} { ui->setupUi(this); - connect(ui->radioButton_Perfect, &QRadioButton::clicked, this, &CompatDB::EnableNext); - connect(ui->radioButton_Great, &QRadioButton::clicked, this, &CompatDB::EnableNext); - connect(ui->radioButton_Okay, &QRadioButton::clicked, this, &CompatDB::EnableNext); - connect(ui->radioButton_Bad, &QRadioButton::clicked, this, &CompatDB::EnableNext); - connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); - connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); + + connect(ui->radioButton_GameBoot_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_GameBoot_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_Gameplay_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_Gameplay_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_NoFreeze_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_NoFreeze_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_Complete_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_Complete_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_Graphical_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_Graphical_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_Graphical_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_Audio_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_Audio_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(ui->radioButton_Audio_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); + connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit); connect(&testcase_watcher, &QFutureWatcher::finished, this, &CompatDB::OnTestcaseSubmitted); @@ -30,29 +40,82 @@ CompatDB::~CompatDB() = default; enum class CompatDBPage { Intro = 0, - Selection = 1, - Final = 2, + GameBoot = 1, + GamePlay = 2, + Freeze = 3, + Completion = 4, + Graphical = 5, + Audio = 6, + Final = 7, }; void CompatDB::Submit() { - QButtonGroup* compatibility = new QButtonGroup(this); - compatibility->addButton(ui->radioButton_Perfect, 0); - compatibility->addButton(ui->radioButton_Great, 1); - compatibility->addButton(ui->radioButton_Okay, 2); - compatibility->addButton(ui->radioButton_Bad, 3); - compatibility->addButton(ui->radioButton_IntroMenu, 4); - compatibility->addButton(ui->radioButton_WontBoot, 5); + QButtonGroup* compatibility_GameBoot = new QButtonGroup(this); + compatibility_GameBoot->addButton(ui->radioButton_GameBoot_Yes, 0); + compatibility_GameBoot->addButton(ui->radioButton_GameBoot_No, 1); + + QButtonGroup* compatibility_Gameplay = new QButtonGroup(this); + compatibility_Gameplay->addButton(ui->radioButton_Gameplay_Yes, 0); + compatibility_Gameplay->addButton(ui->radioButton_Gameplay_No, 1); + + QButtonGroup* compatibility_NoFreeze = new QButtonGroup(this); + compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_Yes, 0); + compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_No, 1); + + QButtonGroup* compatibility_Complete = new QButtonGroup(this); + compatibility_Complete->addButton(ui->radioButton_Complete_Yes, 0); + compatibility_Complete->addButton(ui->radioButton_Complete_No, 1); + + QButtonGroup* compatibility_Graphical = new QButtonGroup(this); + compatibility_Graphical->addButton(ui->radioButton_Graphical_Major, 0); + compatibility_Graphical->addButton(ui->radioButton_Graphical_Minor, 1); + compatibility_Graphical->addButton(ui->radioButton_Graphical_No, 2); + + QButtonGroup* compatibility_Audio = new QButtonGroup(this); + compatibility_Audio->addButton(ui->radioButton_Audio_Major, 0); + compatibility_Graphical->addButton(ui->radioButton_Audio_Minor, 1); + compatibility_Audio->addButton(ui->radioButton_Audio_No, 2); + + const int compatiblity = static_cast(CalculateCompatibility()); + switch ((static_cast(currentId()))) { - case CompatDBPage::Selection: - if (compatibility->checkedId() == -1) { + case CompatDBPage::Intro: + break; + case CompatDBPage::GameBoot: + if (compatibility_GameBoot->checkedId() == -1) { + button(NextButton)->setEnabled(false); + } + break; + case CompatDBPage::GamePlay: + if (compatibility_Gameplay->checkedId() == -1) { + button(NextButton)->setEnabled(false); + } + break; + case CompatDBPage::Freeze: + if (compatibility_NoFreeze->checkedId() == -1) { + button(NextButton)->setEnabled(false); + } + break; + case CompatDBPage::Completion: + if (compatibility_Complete->checkedId() == -1) { + button(NextButton)->setEnabled(false); + } + break; + case CompatDBPage::Graphical: + if (compatibility_Graphical->checkedId() == -1) { + button(NextButton)->setEnabled(false); + } + break; + case CompatDBPage::Audio: + if (compatibility_Audio->checkedId() == -1) { button(NextButton)->setEnabled(false); } break; case CompatDBPage::Final: back(); - LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); + LOG_INFO(Frontend, "Compatibility Rating: {}", compatiblity); telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility", - compatibility->checkedId()); + compatiblity); button(NextButton)->setEnabled(false); button(NextButton)->setText(tr("Submitting")); @@ -63,9 +126,70 @@ void CompatDB::Submit() { break; default: LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); + break; } } +int CompatDB::nextId() const { + switch ((static_cast(currentId()))) { + case CompatDBPage::Intro: + return static_cast(CompatDBPage::GameBoot); + case CompatDBPage::GameBoot: + if (ui->radioButton_GameBoot_No->isChecked()) { + return static_cast(CompatDBPage::Final); + } + return static_cast(CompatDBPage::GamePlay); + case CompatDBPage::GamePlay: + if (ui->radioButton_Gameplay_No->isChecked()) { + return static_cast(CompatDBPage::Final); + } + return static_cast(CompatDBPage::Freeze); + case CompatDBPage::Freeze: + if (ui->radioButton_NoFreeze_No->isChecked()) { + return static_cast(CompatDBPage::Final); + } + return static_cast(CompatDBPage::Completion); + case CompatDBPage::Completion: + if (ui->radioButton_Complete_No->isChecked()) { + return static_cast(CompatDBPage::Final); + } + return static_cast(CompatDBPage::Graphical); + case CompatDBPage::Graphical: + return static_cast(CompatDBPage::Audio); + case CompatDBPage::Audio: + return static_cast(CompatDBPage::Final); + case CompatDBPage::Final: + return -1; + default: + LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); + return static_cast(CompatDBPage::Intro); + } +} + +CompatibilityStatus CompatDB::CalculateCompatibility() const { + if (ui->radioButton_GameBoot_No->isChecked()) { + return CompatibilityStatus::WontBoot; + } + + if (ui->radioButton_Gameplay_No->isChecked()) { + return CompatibilityStatus::IntroMenu; + } + + if (ui->radioButton_NoFreeze_No->isChecked() || ui->radioButton_Complete_No->isChecked()) { + return CompatibilityStatus::Ingame; + } + + if (ui->radioButton_Graphical_Major->isChecked() || ui->radioButton_Audio_Major->isChecked()) { + return CompatibilityStatus::Ingame; + } + + if (ui->radioButton_Graphical_Minor->isChecked() || ui->radioButton_Audio_Minor->isChecked()) { + return CompatibilityStatus::Playable; + } + + return CompatibilityStatus::Perfect; +} + void CompatDB::OnTestcaseSubmitted() { if (!testcase_watcher.result()) { QMessageBox::critical(this, tr("Communication error"), diff --git a/src/yuzu/compatdb.h b/src/yuzu/compatdb.h index 3252fc4..37e1127 100644 --- a/src/yuzu/compatdb.h +++ b/src/yuzu/compatdb.h @@ -12,12 +12,22 @@ namespace Ui { class CompatDB; } +enum class CompatibilityStatus { + Perfect = 0, + Playable = 1, + // Unused: Okay = 2, + Ingame = 3, + IntroMenu = 4, + WontBoot = 5, +}; + class CompatDB : public QWizard { Q_OBJECT public: explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr); ~CompatDB(); + int nextId() const override; private: QFutureWatcher testcase_watcher; @@ -25,6 +35,7 @@ private: std::unique_ptr ui; void Submit(); + CompatibilityStatus CalculateCompatibility() const; void OnTestcaseSubmitted(); void EnableNext(); diff --git a/src/yuzu/compatdb.ui b/src/yuzu/compatdb.ui index 3ca55ed..d11669d 100644 --- a/src/yuzu/compatdb.ui +++ b/src/yuzu/compatdb.ui @@ -58,128 +58,23 @@ - + Report Game Compatibility 1 - - - - - Perfect - - - - - - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - - - true - - - - - - - Great - - - - - - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - - - true - - - - - - - Okay - - - - - - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - - - true - - - - - - - Bad - - - - - - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - - - true - - - - - - - Intro/Menu - - - - - - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - - - true - - - - - - - Won't Boot - - - true - - - false - - - - - - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - - - + - + 10 - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html> + <html><head/><body><p>Does the game boot?</p></body></html> true @@ -187,7 +82,295 @@ - + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + Yes The game starts to output video or audio + + + + + + + No The game doesn't get past the "Launching..." screen + + + + + + + + Report Game Compatibility + + + 2 + + + + + + Yes The game gets past the intro/menu and into gameplay + + + + + + + No The game crashes or freezes while loading or using the menu + + + + + + + + 10 + + + + <html><head/><body><p>Does the game reach gameplay?</p></body></html> + + + true + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + Report Game Compatibility + + + 3 + + + + + + Yes The game works without crashes + + + + + + + No The game crashes or freezes during gameplay + + + + + + + + 10 + + + + <html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html> + + + true + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + Report Game Compatibility + + + 4 + + + + + + Yes The game can be finished without any workarounds + + + + + + + No The game can't progress past a certain area + + + + + + + + 10 + + + + <html><head/><body><p>Is the game completely playable from start to finish?</p></body></html> + + + true + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + Report Game Compatibility + + + 5 + + + + + + Major The game has major graphical errors + + + + + + + Minor The game has minor graphical errors + + + + + + + None Everything is rendered as it looks on the Nintendo Switch + + + + + + + + 10 + + + + <html><head/><body><p>Does the game have any graphical glitches?</p></body></html> + + + true + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + Report Game Compatibility + + + 6 + + + + + + Major The game has major audio errors + + + + + + + Minor The game has minor audio errors + + + + + + + None Audio is played perfectly + + + + + + + + 10 + + + + <html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html> + + + true + + + + + Qt::Vertical @@ -206,7 +389,7 @@ Thank you for your submission! - 2 + 7 diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index a4ed684..2ea4f36 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -124,6 +124,10 @@ void Config::Initialize(const std::string& config_name) { } } +bool Config::IsCustomConfig() { + return type == ConfigType::PerGameConfig; +} + /* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their * usages later in this file. This allows explicit definition of some types that don't work * nicely with the general version. @@ -194,8 +198,20 @@ void Config::ReadPlayerValue(std::size_t player_index) { }(); auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + const auto profile_name = + qt_config->value(QStringLiteral("%1profile_name").arg(player_prefix), QString{}) + .toString() + .toStdString(); + if (profile_name.empty()) { + // Use the global input config + player = Settings::values.players.GetValue(true)[player_index]; + return; + } + player.profile_name = profile_name; + } - if (player_prefix.isEmpty()) { + if (player_prefix.isEmpty() && Settings::IsConfiguringGlobal()) { const auto controller = static_cast( qt_config ->value(QStringLiteral("%1type").arg(player_prefix), @@ -388,9 +404,26 @@ void Config::ReadAudioValues() { void Config::ReadControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); + Settings::values.players.SetGlobal(!IsCustomConfig()); for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { ReadPlayerValue(p); } + ReadGlobalSetting(Settings::values.use_docked_mode); + + // Disable docked mode if handheld is selected + const auto controller_type = Settings::values.players.GetValue()[0].controller_type; + if (controller_type == Settings::ControllerType::Handheld) { + Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); + Settings::values.use_docked_mode.SetValue(false); + } + + ReadGlobalSetting(Settings::values.vibration_enabled); + ReadGlobalSetting(Settings::values.enable_accurate_vibrations); + ReadGlobalSetting(Settings::values.motion_enabled); + if (IsCustomConfig()) { + qt_config->endGroup(); + return; + } ReadDebugValues(); ReadKeyboardValues(); ReadMouseValues(); @@ -412,18 +445,6 @@ void Config::ReadControlValues() { ReadBasicSetting(Settings::values.tas_loop); ReadBasicSetting(Settings::values.pause_tas_on_load); - ReadGlobalSetting(Settings::values.use_docked_mode); - - // Disable docked mode if handheld is selected - const auto controller_type = Settings::values.players.GetValue()[0].controller_type; - if (controller_type == Settings::ControllerType::Handheld) { - Settings::values.use_docked_mode.SetValue(false); - } - - ReadGlobalSetting(Settings::values.vibration_enabled); - ReadGlobalSetting(Settings::values.enable_accurate_vibrations); - ReadGlobalSetting(Settings::values.motion_enabled); - ReadBasicSetting(Settings::values.controller_navigation); qt_config->endGroup(); @@ -546,6 +567,7 @@ void Config::ReadDebuggingValues() { ReadBasicSetting(Settings::values.use_auto_stub); ReadBasicSetting(Settings::values.enable_all_controllers); ReadBasicSetting(Settings::values.create_crash_dumps); + ReadBasicSetting(Settings::values.perform_vulkan_check); qt_config->endGroup(); } @@ -657,6 +679,7 @@ void Config::ReadCpuValues() { ReadBasicSetting(Settings::values.cpuopt_fastmem); ReadBasicSetting(Settings::values.cpuopt_fastmem_exclusives); ReadBasicSetting(Settings::values.cpuopt_recompile_exclusives); + ReadBasicSetting(Settings::values.cpuopt_ignore_memory_aborts); } qt_config->endGroup(); @@ -671,9 +694,9 @@ void Config::ReadRendererValues() { ReadGlobalSetting(Settings::values.aspect_ratio); ReadGlobalSetting(Settings::values.resolution_setup); ReadGlobalSetting(Settings::values.scaling_filter); + ReadGlobalSetting(Settings::values.fsr_sharpening_slider); ReadGlobalSetting(Settings::values.anti_aliasing); ReadGlobalSetting(Settings::values.max_anisotropy); - ReadGlobalSetting(Settings::values.use_speed_limit); ReadGlobalSetting(Settings::values.speed_limit); ReadGlobalSetting(Settings::values.use_disk_shader_cache); ReadGlobalSetting(Settings::values.gpu_accuracy); @@ -772,6 +795,7 @@ void Config::ReadSystemValues() { } else { Settings::values.custom_rtc = std::nullopt; } + ReadBasicSetting(Settings::values.device_name); } ReadGlobalSetting(Settings::values.sound_index); @@ -818,6 +842,9 @@ void Config::ReadUIGamelistValues() { qt_config->beginGroup(QStringLiteral("UIGameList")); ReadBasicSetting(UISettings::values.show_add_ons); + ReadBasicSetting(UISettings::values.show_compat); + ReadBasicSetting(UISettings::values.show_size); + ReadBasicSetting(UISettings::values.show_types); ReadBasicSetting(UISettings::values.game_icon_size); ReadBasicSetting(UISettings::values.folder_icon_size); ReadBasicSetting(UISettings::values.row_1_text_id); @@ -900,7 +927,6 @@ void Config::ReadMultiplayerValues() { void Config::ReadValues() { if (global) { - ReadControlValues(); ReadDataStorageValues(); ReadDebuggingValues(); ReadDisabledAddOnValues(); @@ -909,6 +935,7 @@ void Config::ReadValues() { ReadWebServiceValues(); ReadMiscellaneousValues(); } + ReadControlValues(); ReadCoreValues(); ReadCpuValues(); ReadRendererValues(); @@ -927,12 +954,20 @@ void Config::SavePlayerValue(std::size_t player_index) { }(); const auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + if (player.profile_name.empty()) { + // No custom profile selected + return; + } + WriteSetting(QStringLiteral("%1profile_name").arg(player_prefix), + QString::fromStdString(player.profile_name), QString{}); + } WriteSetting(QStringLiteral("%1type").arg(player_prefix), static_cast(player.controller_type), static_cast(Settings::ControllerType::ProController)); - if (!player_prefix.isEmpty()) { + if (!player_prefix.isEmpty() || !Settings::IsConfiguringGlobal()) { WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, player_index == 0); WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix), @@ -1050,7 +1085,6 @@ void Config::SaveIrCameraValues() { void Config::SaveValues() { if (global) { - SaveControlValues(); SaveDataStorageValues(); SaveDebuggingValues(); SaveDisabledAddOnValues(); @@ -1059,6 +1093,7 @@ void Config::SaveValues() { SaveWebServiceValues(); SaveMiscellaneousValues(); } + SaveControlValues(); SaveCoreValues(); SaveCpuValues(); SaveRendererValues(); @@ -1083,9 +1118,14 @@ void Config::SaveAudioValues() { void Config::SaveControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); + Settings::values.players.SetGlobal(!IsCustomConfig()); for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { SavePlayerValue(p); } + if (IsCustomConfig()) { + qt_config->endGroup(); + return; + } SaveDebugValues(); SaveMouseValues(); SaveTouchscreenValues(); @@ -1162,6 +1202,7 @@ void Config::SaveDebuggingValues() { WriteBasicSetting(Settings::values.disable_macro_jit); WriteBasicSetting(Settings::values.enable_all_controllers); WriteBasicSetting(Settings::values.create_crash_dumps); + WriteBasicSetting(Settings::values.perform_vulkan_check); qt_config->endGroup(); } @@ -1251,6 +1292,7 @@ void Config::SaveCpuValues() { WriteBasicSetting(Settings::values.cpuopt_fastmem); WriteBasicSetting(Settings::values.cpuopt_fastmem_exclusives); WriteBasicSetting(Settings::values.cpuopt_recompile_exclusives); + WriteBasicSetting(Settings::values.cpuopt_ignore_memory_aborts); } qt_config->endGroup(); @@ -1277,12 +1319,15 @@ void Config::SaveRendererValues() { static_cast(Settings::values.scaling_filter.GetValue(global)), static_cast(Settings::values.scaling_filter.GetDefault()), Settings::values.scaling_filter.UsingGlobal()); + WriteSetting(QString::fromStdString(Settings::values.fsr_sharpening_slider.GetLabel()), + static_cast(Settings::values.fsr_sharpening_slider.GetValue(global)), + static_cast(Settings::values.fsr_sharpening_slider.GetDefault()), + Settings::values.fsr_sharpening_slider.UsingGlobal()); WriteSetting(QString::fromStdString(Settings::values.anti_aliasing.GetLabel()), static_cast(Settings::values.anti_aliasing.GetValue(global)), static_cast(Settings::values.anti_aliasing.GetDefault()), Settings::values.anti_aliasing.UsingGlobal()); WriteGlobalSetting(Settings::values.max_anisotropy); - WriteGlobalSetting(Settings::values.use_speed_limit); WriteGlobalSetting(Settings::values.speed_limit); WriteGlobalSetting(Settings::values.use_disk_shader_cache); WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), @@ -1369,6 +1414,7 @@ void Config::SaveSystemValues() { false); WriteSetting(QStringLiteral("custom_rtc"), QVariant::fromValue(Settings::values.custom_rtc.value_or(0)), 0); + WriteBasicSetting(Settings::values.device_name); } WriteGlobalSetting(Settings::values.sound_index); @@ -1412,6 +1458,9 @@ void Config::SaveUIGamelistValues() { qt_config->beginGroup(QStringLiteral("UIGameList")); WriteBasicSetting(UISettings::values.show_add_ons); + WriteBasicSetting(UISettings::values.show_compat); + WriteBasicSetting(UISettings::values.show_size); + WriteBasicSetting(UISettings::values.show_types); WriteBasicSetting(UISettings::values.game_icon_size); WriteBasicSetting(UISettings::values.folder_icon_size); WriteBasicSetting(UISettings::values.row_1_text_id); @@ -1566,6 +1615,13 @@ void Config::SaveControlPlayerValue(std::size_t player_index) { qt_config->endGroup(); } +void Config::ClearControlPlayerValues() { + qt_config->beginGroup(QStringLiteral("Controls")); + // If key is an empty string, all keys in the current group() are removed. + qt_config->remove(QString{}); + qt_config->endGroup(); +} + const std::string& Config::GetConfigFilePath() const { return qt_config_loc; } diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 06fa7d2..7d26e9a 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -34,6 +34,7 @@ public: void ReadControlPlayerValue(std::size_t player_index); void SaveControlPlayerValue(std::size_t player_index); + void ClearControlPlayerValues(); const std::string& GetConfigFilePath() const; @@ -58,6 +59,7 @@ public: private: void Initialize(const std::string& config_name); + bool IsCustomConfig(); void ReadValues(); void ReadPlayerValue(std::size_t player_index); diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index 19b8b15..70cc6f8 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -161,8 +161,8 @@ void ConfigureAudio::InitializeAudioSinkComboBox() { ui->sink_combo_box->clear(); ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); - for (const char* id : AudioCore::Sink::GetSinkIDs()) { - ui->sink_combo_box->addItem(QString::fromUtf8(id)); + for (const auto& id : AudioCore::Sink::GetSinkIDs()) { + ui->sink_combo_box->addItem(QString::fromUtf8(id.data(), static_cast(id.length()))); } } diff --git a/src/yuzu/configuration/configure_camera.cpp b/src/yuzu/configuration/configure_camera.cpp index 2a61de2..d95e966 100644 --- a/src/yuzu/configuration/configure_camera.cpp +++ b/src/yuzu/configuration/configure_camera.cpp @@ -2,8 +2,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include +#include +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA #include #include +#endif #include #include @@ -33,6 +36,7 @@ ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* i ConfigureCamera::~ConfigureCamera() = default; void ConfigureCamera::PreviewCamera() { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA const auto index = ui->ir_sensor_combo_box->currentIndex(); bool camera_found = false; const QList cameras = QCameraInfo::availableCameras(); @@ -101,6 +105,7 @@ void ConfigureCamera::PreviewCamera() { }); camera_timer->start(250); +#endif } void ConfigureCamera::DisplayCapturedFrame(int requestId, const QImage& img) { @@ -133,11 +138,13 @@ void ConfigureCamera::LoadConfiguration() { ui->ir_sensor_combo_box->clear(); input_devices.push_back("Auto"); ui->ir_sensor_combo_box->addItem(tr("Auto")); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA const auto cameras = QCameraInfo::availableCameras(); for (const QCameraInfo& cameraInfo : cameras) { input_devices.push_back(cameraInfo.deviceName().toStdString()); ui->ir_sensor_combo_box->addItem(cameraInfo.description()); } +#endif const auto current_device = Settings::values.ir_sensor_device.GetValue(); diff --git a/src/yuzu/configuration/configure_camera.h b/src/yuzu/configuration/configure_camera.h index db9833b..9a90512 100644 --- a/src/yuzu/configuration/configure_camera.h +++ b/src/yuzu/configuration/configure_camera.h @@ -46,8 +46,10 @@ private: bool is_virtual_camera; int pending_snapshots; +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA std::unique_ptr camera; std::unique_ptr camera_capture; +#endif std::unique_ptr camera_timer; std::vector input_devices; std::unique_ptr ui; diff --git a/src/yuzu/configuration/configure_cpu_debug.cpp b/src/yuzu/configuration/configure_cpu_debug.cpp index 3c302ec..8cfef0c 100644 --- a/src/yuzu/configuration/configure_cpu_debug.cpp +++ b/src/yuzu/configuration/configure_cpu_debug.cpp @@ -45,6 +45,9 @@ void ConfigureCpuDebug::SetConfiguration() { ui->cpuopt_recompile_exclusives->setEnabled(runtime_lock); ui->cpuopt_recompile_exclusives->setChecked( Settings::values.cpuopt_recompile_exclusives.GetValue()); + ui->cpuopt_ignore_memory_aborts->setEnabled(runtime_lock); + ui->cpuopt_ignore_memory_aborts->setChecked( + Settings::values.cpuopt_ignore_memory_aborts.GetValue()); } void ConfigureCpuDebug::ApplyConfiguration() { @@ -59,6 +62,7 @@ void ConfigureCpuDebug::ApplyConfiguration() { Settings::values.cpuopt_fastmem = ui->cpuopt_fastmem->isChecked(); Settings::values.cpuopt_fastmem_exclusives = ui->cpuopt_fastmem_exclusives->isChecked(); Settings::values.cpuopt_recompile_exclusives = ui->cpuopt_recompile_exclusives->isChecked(); + Settings::values.cpuopt_ignore_memory_aborts = ui->cpuopt_ignore_memory_aborts->isChecked(); } void ConfigureCpuDebug::changeEvent(QEvent* event) { diff --git a/src/yuzu/configuration/configure_cpu_debug.ui b/src/yuzu/configuration/configure_cpu_debug.ui index 2bc2688..3010f7f 100644 --- a/src/yuzu/configuration/configure_cpu_debug.ui +++ b/src/yuzu/configuration/configure_cpu_debug.ui @@ -175,6 +175,19 @@ + + + + + <div style="white-space: nowrap">This optimization speeds up memory accesses by allowing invalid memory accesses to succeed.</div> + <div style="white-space: nowrap">Enabling it reduces the overhead of all memory accesses and has no impact on programs that don't access invalid memory.</div> + + + + Enable fallbacks for invalid memory accesses + + + diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 622808e..dacc75a 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -77,6 +77,7 @@ void ConfigureDebug::SetConfiguration() { ui->disable_loop_safety_checks->setChecked( Settings::values.disable_shader_loop_safety_checks.GetValue()); ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue()); + ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue()); #ifdef YUZU_USE_QT_WEB_ENGINE ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue()); @@ -117,6 +118,7 @@ void ConfigureDebug::ApplyConfiguration() { ui->disable_loop_safety_checks->isChecked(); Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); Settings::values.extended_logging = ui->extended_logging->isChecked(); + Settings::values.perform_vulkan_check = ui->perform_vulkan_check->isChecked(); UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked(); Debugger::ToggleConsole(); Common::Log::Filter filter; diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 314d47a..102c8c6 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -313,6 +313,16 @@ + + + + Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu. + + + Perform Startup Vulkan Check + + + diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 87e5d0f..e9388da 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -31,7 +31,7 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren ui->backend->addItem(QStringLiteral("GLSL")); ui->backend->addItem(tr("GLASM (Assembly Shaders, NVIDIA Only)")); - ui->backend->addItem(QStringLiteral("SPIR-V (Experimental, Mesa Only)")); + ui->backend->addItem(tr("SPIR-V (Experimental, Mesa Only)")); SetupPerGameUI(); @@ -57,11 +57,17 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren UpdateBackgroundColorButton(new_bg_color); }); - ui->api->setEnabled(!UISettings::values.has_broken_vulkan); - ui->api_widget->setEnabled(!UISettings::values.has_broken_vulkan || - Settings::IsConfiguringGlobal()); + ui->api->setEnabled(!UISettings::values.has_broken_vulkan && ui->api->isEnabled()); + ui->api_widget->setEnabled( + (!UISettings::values.has_broken_vulkan || Settings::IsConfiguringGlobal()) && + ui->api_widget->isEnabled()); ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); + + connect(ui->fsr_sharpening_slider, &QSlider::valueChanged, this, + &ConfigureGraphics::SetFSRIndicatorText); + ui->fsr_sharpening_combobox->setVisible(!Settings::IsConfiguringGlobal()); + ui->fsr_sharpening_label->setVisible(Settings::IsConfiguringGlobal()); } void ConfigureGraphics::UpdateDeviceSelection(int device) { @@ -109,6 +115,7 @@ void ConfigureGraphics::SetConfiguration() { static_cast(Settings::values.resolution_setup.GetValue())); ui->scaling_filter_combobox->setCurrentIndex( static_cast(Settings::values.scaling_filter.GetValue())); + ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue()); ui->anti_aliasing_combobox->setCurrentIndex( static_cast(Settings::values.anti_aliasing.GetValue())); } else { @@ -146,6 +153,15 @@ void ConfigureGraphics::SetConfiguration() { ConfigurationShared::SetHighlight(ui->anti_aliasing_label, !Settings::values.anti_aliasing.UsingGlobal()); + ui->fsr_sharpening_combobox->setCurrentIndex( + Settings::values.fsr_sharpening_slider.UsingGlobal() ? 0 : 1); + ui->fsr_sharpening_slider->setEnabled( + !Settings::values.fsr_sharpening_slider.UsingGlobal()); + ui->fsr_sharpening_value->setEnabled(!Settings::values.fsr_sharpening_slider.UsingGlobal()); + ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout, + !Settings::values.fsr_sharpening_slider.UsingGlobal()); + ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue()); + ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1); ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal()); ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal()); @@ -154,6 +170,12 @@ void ConfigureGraphics::SetConfiguration() { Settings::values.bg_green.GetValue(), Settings::values.bg_blue.GetValue())); UpdateAPILayout(); + SetFSRIndicatorText(ui->fsr_sharpening_slider->sliderPosition()); +} + +void ConfigureGraphics::SetFSRIndicatorText(int percentage) { + ui->fsr_sharpening_value->setText( + tr("%1%", "FSR sharpening percentage (e.g. 50%)").arg(100 - (percentage / 2))); } void ConfigureGraphics::ApplyConfiguration() { @@ -209,6 +231,7 @@ void ConfigureGraphics::ApplyConfiguration() { if (Settings::values.anti_aliasing.UsingGlobal()) { Settings::values.anti_aliasing.SetValue(anti_aliasing); } + Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value()); } else { if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { Settings::values.resolution_setup.SetGlobal(true); @@ -237,6 +260,7 @@ void ConfigureGraphics::ApplyConfiguration() { Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); switch (GetCurrentGraphicsBackend()) { case Settings::RendererBackend::OpenGL: + case Settings::RendererBackend::Null: Settings::values.shader_backend.SetGlobal(false); Settings::values.vulkan_device.SetGlobal(true); Settings::values.shader_backend.SetValue(shader_backend); @@ -268,6 +292,13 @@ void ConfigureGraphics::ApplyConfiguration() { Settings::values.bg_green.SetValue(static_cast(bg_color.green())); Settings::values.bg_blue.SetValue(static_cast(bg_color.blue())); } + + if (ui->fsr_sharpening_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { + Settings::values.fsr_sharpening_slider.SetGlobal(true); + } else { + Settings::values.fsr_sharpening_slider.SetGlobal(false); + Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value()); + } } } @@ -318,6 +349,10 @@ void ConfigureGraphics::UpdateAPILayout() { ui->device_widget->setVisible(true); ui->backend_widget->setVisible(false); break; + case Settings::RendererBackend::Null: + ui->device_widget->setVisible(false); + ui->backend_widget->setVisible(false); + break; } } @@ -330,7 +365,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() try { vk::InstanceDispatch dld; const Common::DynamicLibrary library = OpenLibrary(); - const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_0); + const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1); const std::vector physical_devices = instance.EnumeratePhysicalDevices(); vulkan_devices.clear(); @@ -379,6 +414,7 @@ void ConfigureGraphics::SetupPerGameUI() { ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); ui->resolution_combobox->setEnabled(Settings::values.resolution_setup.UsingGlobal()); ui->scaling_filter_combobox->setEnabled(Settings::values.scaling_filter.UsingGlobal()); + ui->fsr_sharpening_slider->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal()); ui->anti_aliasing_combobox->setEnabled(Settings::values.anti_aliasing.UsingGlobal()); ui->use_asynchronous_gpu_emulation->setEnabled( Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); @@ -386,6 +422,7 @@ void ConfigureGraphics::SetupPerGameUI() { ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal()); ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal()); ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal()); + ui->fsr_slider_layout->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal()); return; } @@ -395,6 +432,13 @@ void ConfigureGraphics::SetupPerGameUI() { ConfigurationShared::SetHighlight(ui->bg_layout, index == 1); }); + connect(ui->fsr_sharpening_combobox, qOverload(&QComboBox::activated), this, + [this](int index) { + ui->fsr_sharpening_slider->setEnabled(index == 1); + ui->fsr_sharpening_value->setEnabled(index == 1); + ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout, index == 1); + }); + ConfigurationShared::SetColoredTristate( ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache); ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc, diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 70034eb..d98d662 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -42,6 +42,8 @@ private: void RetrieveVulkanDevices(); + void SetFSRIndicatorText(int percentage); + void SetupPerGameUI(); Settings::RendererBackend GetCurrentGraphicsBackend() const; diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 1e4f747..aa02cc6 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -139,6 +139,11 @@ Vulkan + + + None + + @@ -152,6 +157,12 @@ + + + 16777215 + 16777215 + + Graphics Settings @@ -299,6 +310,11 @@ Force 21:9 + + + Force 16:10 + + Stretch to Window @@ -471,11 +487,162 @@ FXAA + + + SMAA + + + + + + true + + + + 0 + 0 + + + + + 6 + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + Use global FSR Sharpness + + + + + Set FSR Sharpness + + + + + + + + + 0 + 0 + + + + FSR Sharpness: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 6 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + 200 + + + 25 + + + Qt::Horizontal + + + true + + + + + + + + 0 + 0 + + + + + 32 + 0 + + + + 100% + + + Qt::AlignCenter + + + + + + + + diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index cb55472..1db374d 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -163,10 +163,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, [this, input_subsystem, &hid_core] { CallConfigureDialog(*this, input_subsystem, hid_core); }); - connect(advanced, &ConfigureInputAdvanced::CallCameraDialog, - [this, input_subsystem, &hid_core] { - CallConfigureDialog(*this, input_subsystem); - }); + connect(advanced, &ConfigureInputAdvanced::CallCameraDialog, [this, input_subsystem] { + CallConfigureDialog(*this, input_subsystem); + }); connect(ui->vibrationButton, &QPushButton::clicked, [this, &hid_core] { CallConfigureDialog(*this, hid_core); }); diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 10f841b..235b813 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -194,4 +194,8 @@ void ConfigureInputAdvanced::UpdateUIEnabled() { ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked()); ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked()); ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked()); +#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) || !defined(YUZU_USE_QT_MULTIMEDIA) + ui->enable_ir_sensor->setEnabled(false); + ui->camera_configure->setEnabled(false); +#endif } diff --git a/src/yuzu/configuration/configure_input_per_game.cpp b/src/yuzu/configuration/configure_input_per_game.cpp new file mode 100644 index 0000000..78e65d4 --- /dev/null +++ b/src/yuzu/configuration/configure_input_per_game.cpp @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "core/core.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "ui_configure_input_per_game.h" +#include "yuzu/configuration/config.h" +#include "yuzu/configuration/configure_input_per_game.h" +#include "yuzu/configuration/input_profiles.h" + +ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, Config* config_, + QWidget* parent) + : QWidget(parent), ui(std::make_unique()), + profiles(std::make_unique()), system{system_}, config{config_} { + ui->setupUi(this); + const std::array labels = { + ui->label_player_1, ui->label_player_2, ui->label_player_3, ui->label_player_4, + ui->label_player_5, ui->label_player_6, ui->label_player_7, ui->label_player_8, + }; + profile_comboboxes = { + ui->profile_player_1, ui->profile_player_2, ui->profile_player_3, ui->profile_player_4, + ui->profile_player_5, ui->profile_player_6, ui->profile_player_7, ui->profile_player_8, + }; + + Settings::values.players.SetGlobal(false); + + const auto& profile_names = profiles->GetInputProfileNames(); + const auto populate_profiles = [this, &profile_names](size_t player_index) { + const auto previous_profile = + Settings::values.players.GetValue()[player_index].profile_name; + + auto* const player_combobox = profile_comboboxes[player_index]; + player_combobox->addItem(tr("Use global input configuration")); + + for (size_t index = 0; index < profile_names.size(); ++index) { + const auto& profile_name = profile_names[index]; + player_combobox->addItem(QString::fromStdString(profile_name)); + if (profile_name == previous_profile) { + // offset by 1 since the first element is the global config + player_combobox->setCurrentIndex(static_cast(index + 1)); + } + } + }; + for (size_t index = 0; index < profile_comboboxes.size(); ++index) { + labels[index]->setText(tr("Player %1 profile").arg(index + 1)); + populate_profiles(index); + } + + LoadConfiguration(); +} + +void ConfigureInputPerGame::ApplyConfiguration() { + LoadConfiguration(); + SaveConfiguration(); +} + +void ConfigureInputPerGame::LoadConfiguration() { + static constexpr size_t HANDHELD_INDEX = 8; + + auto& hid_core = system.HIDCore(); + for (size_t player_index = 0; player_index < profile_comboboxes.size(); ++player_index) { + Settings::values.players.SetGlobal(false); + + auto* emulated_controller = hid_core.GetEmulatedControllerByIndex(player_index); + auto* const player_combobox = profile_comboboxes[player_index]; + + const auto selection_index = player_combobox->currentIndex(); + if (selection_index == 0) { + Settings::values.players.GetValue()[player_index].profile_name = ""; + if (player_index == 0) { + Settings::values.players.GetValue()[HANDHELD_INDEX] = {}; + } + Settings::values.players.SetGlobal(true); + emulated_controller->ReloadFromSettings(); + continue; + } + const auto profile_name = player_combobox->itemText(selection_index).toStdString(); + if (profile_name.empty()) { + continue; + } + auto& player = Settings::values.players.GetValue()[player_index]; + player.profile_name = profile_name; + // Read from the profile into the custom player settings + profiles->LoadProfile(profile_name, player_index); + // Make sure the controller is connected + player.connected = true; + + emulated_controller->ReloadFromSettings(); + + if (player_index > 0) { + continue; + } + // Handle Handheld cases + auto& handheld_player = Settings::values.players.GetValue()[HANDHELD_INDEX]; + auto* handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); + if (player.controller_type == Settings::ControllerType::Handheld) { + handheld_player = player; + } else { + handheld_player = {}; + } + handheld_controller->ReloadFromSettings(); + } +} + +void ConfigureInputPerGame::SaveConfiguration() { + Settings::values.players.SetGlobal(false); + + // Clear all controls from the config in case the user reverted back to globals + config->ClearControlPlayerValues(); + for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) { + config->SaveControlPlayerValue(index); + } +} diff --git a/src/yuzu/configuration/configure_input_per_game.h b/src/yuzu/configuration/configure_input_per_game.h new file mode 100644 index 0000000..660faf5 --- /dev/null +++ b/src/yuzu/configuration/configure_input_per_game.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include + +#include "ui_configure_input_per_game.h" +#include "yuzu/configuration/input_profiles.h" + +class QComboBox; + +namespace Core { +class System; +} // namespace Core + +class Config; + +class ConfigureInputPerGame : public QWidget { + Q_OBJECT + +public: + explicit ConfigureInputPerGame(Core::System& system_, Config* config_, + QWidget* parent = nullptr); + + /// Load and Save configurations to settings file. + void ApplyConfiguration(); + +private: + /// Load configuration from settings file. + void LoadConfiguration(); + + /// Save configuration to settings file. + void SaveConfiguration(); + + std::unique_ptr ui; + std::unique_ptr profiles; + + std::array profile_comboboxes; + + Core::System& system; + Config* config; +}; diff --git a/src/yuzu/configuration/configure_input_per_game.ui b/src/yuzu/configuration/configure_input_per_game.ui new file mode 100644 index 0000000..fbd8eab --- /dev/null +++ b/src/yuzu/configuration/configure_input_per_game.ui @@ -0,0 +1,333 @@ + + + ConfigureInputPerGame + + + + 0 + 0 + 541 + 759 + + + + Form + + + Graphics + + + + + + 0 + + + + + Input Profiles + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Player 1 Profile + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Player 2 Profile + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Player 3 Profile + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Player 4 Profile + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Player 5 Profile + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Player 6 Profile + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Player 7 Profile + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Player 8 Profile + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 9e5a40f..b1575b0 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -855,8 +855,7 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() { return; } - const auto devices = - emulated_controller->GetMappedDevices(Core::HID::EmulatedDeviceIndex::AllDevices); + const auto devices = emulated_controller->GetMappedDevices(); UpdateInputDevices(); if (devices.empty()) { @@ -1553,6 +1552,7 @@ void ConfigureInputPlayer::LoadProfile() { } void ConfigureInputPlayer::SaveProfile() { + static constexpr size_t HANDHELD_INDEX = 8; const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); if (profile_name.isEmpty()) { @@ -1561,7 +1561,12 @@ void ConfigureInputPlayer::SaveProfile() { ApplyConfiguration(); - if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) { + // When we're in handheld mode, only the handheld emulated controller bindings are updated + const bool is_handheld = player_index == 0 && emulated_controller->GetNpadIdType() == + Core::HID::NpadIdType::Handheld; + const auto profile_player_index = is_handheld ? HANDHELD_INDEX : player_index; + + if (!profiles->SaveProfile(profile_name.toStdString(), profile_player_index)) { QMessageBox::critical(this, tr("Save Input Profile"), tr("Failed to save the input profile \"%1\"").arg(profile_name)); UpdateInputProfiles(); diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 79434fd..26f60d1 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -38,7 +38,7 @@ enum class InputType; namespace Ui { class ConfigureInputPlayer; -} +} // namespace Ui namespace Core::HID { class HIDCore; diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index c3cb8f6..93db47c 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -28,7 +28,7 @@ #include "yuzu/configuration/configure_general.h" #include "yuzu/configuration/configure_graphics.h" #include "yuzu/configuration/configure_graphics_advanced.h" -#include "yuzu/configuration/configure_input.h" +#include "yuzu/configuration/configure_input_per_game.h" #include "yuzu/configuration/configure_per_game.h" #include "yuzu/configuration/configure_per_game_addons.h" #include "yuzu/configuration/configure_system.h" @@ -50,6 +50,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st general_tab = std::make_unique(system_, this); graphics_tab = std::make_unique(system_, this); graphics_advanced_tab = std::make_unique(system_, this); + input_tab = std::make_unique(system_, game_config.get(), this); system_tab = std::make_unique(system_, this); ui->setupUi(this); @@ -61,6 +62,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics")); ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics")); ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); + ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles")); setFocusPolicy(Qt::ClickFocus); setWindowTitle(tr("Properties")); @@ -91,6 +93,7 @@ void ConfigurePerGame::ApplyConfiguration() { graphics_tab->ApplyConfiguration(); graphics_advanced_tab->ApplyConfiguration(); audio_tab->ApplyConfiguration(); + input_tab->ApplyConfiguration(); system.ApplySettings(); Settings::LogSettings(); diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index 17a98a0..4ecc435 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -16,12 +16,17 @@ namespace Core { class System; } +namespace InputCommon { +class InputSubsystem; +} + class ConfigurePerGameAddons; class ConfigureAudio; class ConfigureCpu; class ConfigureGeneral; class ConfigureGraphics; class ConfigureGraphicsAdvanced; +class ConfigureInputPerGame; class ConfigureSystem; class QGraphicsScene; @@ -72,5 +77,6 @@ private: std::unique_ptr general_tab; std::unique_ptr graphics_tab; std::unique_ptr graphics_advanced_tab; + std::unique_ptr input_tab; std::unique_ptr system_tab; }; diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index 5c0217b..a470899 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -2,6 +2,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include +#include +#include #include #include #include @@ -108,9 +111,12 @@ ConfigureProfileManager::ConfigureProfileManager(const Core::System& system_, QW connect(ui->pm_add, &QPushButton::clicked, this, &ConfigureProfileManager::AddUser); connect(ui->pm_rename, &QPushButton::clicked, this, &ConfigureProfileManager::RenameUser); - connect(ui->pm_remove, &QPushButton::clicked, this, &ConfigureProfileManager::DeleteUser); + connect(ui->pm_remove, &QPushButton::clicked, this, + &ConfigureProfileManager::ConfirmDeleteUser); connect(ui->pm_set_image, &QPushButton::clicked, this, &ConfigureProfileManager::SetUserImage); + confirm_dialog = new ConfigureProfileManagerDeleteDialog(this); + scene = new QGraphicsScene; ui->current_user_icon->setScene(scene); @@ -230,26 +236,23 @@ void ConfigureProfileManager::RenameUser() { UpdateCurrentUser(); } -void ConfigureProfileManager::DeleteUser() { +void ConfigureProfileManager::ConfirmDeleteUser() { const auto index = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(index); ASSERT(uuid); const auto username = GetAccountUsername(*profile_manager, *uuid); - const auto confirm = QMessageBox::question( - this, tr("Confirm Delete"), - tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); - - if (confirm == QMessageBox::No) { - return; - } + confirm_dialog->SetInfo(username, *uuid, [this, uuid]() { DeleteUser(*uuid); }); + confirm_dialog->show(); +} +void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) { if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) { Settings::values.current_user = 0; } UpdateCurrentUser(); - if (!profile_manager->RemoveUser(*uuid)) { + if (!profile_manager->RemoveUser(uuid)) { return; } @@ -319,3 +322,47 @@ void ConfigureProfileManager::SetUserImage() { new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)}); UpdateCurrentUser(); } + +ConfigureProfileManagerDeleteDialog::ConfigureProfileManagerDeleteDialog(QWidget* parent) + : QDialog{parent} { + auto dialog_vbox_layout = new QVBoxLayout(this); + dialog_button_box = + new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No, Qt::Horizontal, parent); + auto label_message = + new QLabel(tr("Delete this user? All of the user's save data will be deleted."), this); + label_info = new QLabel(this); + auto dialog_hbox_layout_widget = new QWidget(this); + auto dialog_hbox_layout = new QHBoxLayout(dialog_hbox_layout_widget); + icon_scene = new QGraphicsScene(0, 0, 64, 64, this); + auto icon_view = new QGraphicsView(icon_scene, this); + + dialog_hbox_layout_widget->setLayout(dialog_hbox_layout); + icon_view->setMaximumSize(64, 64); + icon_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + icon_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + this->setLayout(dialog_vbox_layout); + this->setWindowTitle(tr("Confirm Delete")); + this->setSizeGripEnabled(false); + dialog_vbox_layout->addWidget(label_message); + dialog_vbox_layout->addWidget(dialog_hbox_layout_widget); + dialog_vbox_layout->addWidget(dialog_button_box); + dialog_hbox_layout->addWidget(icon_view); + dialog_hbox_layout->addWidget(label_info); + + connect(dialog_button_box, &QDialogButtonBox::rejected, this, [this]() { close(); }); +} + +ConfigureProfileManagerDeleteDialog::~ConfigureProfileManagerDeleteDialog() = default; + +void ConfigureProfileManagerDeleteDialog::SetInfo(const QString& username, const Common::UUID& uuid, + std::function accept_callback) { + label_info->setText( + tr("Name: %1\nUUID: %2").arg(username, QString::fromStdString(uuid.FormattedString()))); + icon_scene->clear(); + icon_scene->addPixmap(GetIcon(uuid)); + + connect(dialog_button_box, &QDialogButtonBox::accepted, this, [this, accept_callback]() { + close(); + accept_callback(); + }); +} diff --git a/src/yuzu/configuration/configure_profile_manager.h b/src/yuzu/configuration/configure_profile_manager.h index fe90337..c4b1a33 100644 --- a/src/yuzu/configuration/configure_profile_manager.h +++ b/src/yuzu/configuration/configure_profile_manager.h @@ -3,16 +3,24 @@ #pragma once +#include #include +#include #include #include +namespace Common { +struct UUID; +} + namespace Core { class System; } class QGraphicsScene; +class QDialogButtonBox; +class QLabel; class QStandardItem; class QStandardItemModel; class QTreeView; @@ -26,6 +34,20 @@ namespace Ui { class ConfigureProfileManager; } +class ConfigureProfileManagerDeleteDialog : public QDialog { +public: + explicit ConfigureProfileManagerDeleteDialog(QWidget* parent); + ~ConfigureProfileManagerDeleteDialog(); + + void SetInfo(const QString& username, const Common::UUID& uuid, + std::function accept_callback); + +private: + QDialogButtonBox* dialog_button_box; + QGraphicsScene* icon_scene; + QLabel* label_info; +}; + class ConfigureProfileManager : public QWidget { Q_OBJECT @@ -47,7 +69,8 @@ private: void SelectUser(const QModelIndex& index); void AddUser(); void RenameUser(); - void DeleteUser(); + void ConfirmDeleteUser(); + void DeleteUser(const Common::UUID& uuid); void SetUserImage(); QVBoxLayout* layout; @@ -55,6 +78,8 @@ private: QStandardItemModel* item_model; QGraphicsScene* scene; + ConfigureProfileManagerDeleteDialog* confirm_dialog; + std::vector> list_items; std::unique_ptr ui; diff --git a/src/yuzu/configuration/configure_profile_manager.ui b/src/yuzu/configuration/configure_profile_manager.ui index cfe7478..bd6dea4 100644 --- a/src/yuzu/configuration/configure_profile_manager.ui +++ b/src/yuzu/configuration/configure_profile_manager.ui @@ -57,6 +57,12 @@ 48 + + QFrame::NoFrame + + + QFrame::Plain + Qt::ScrollBarAlwaysOff diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index bc9d9d7..9b14e59 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -72,6 +72,8 @@ void ConfigureSystem::SetConfiguration() { ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); + ui->device_name_edit->setText( + QString::fromUtf8(Settings::values.device_name.GetValue().c_str())); if (Settings::IsConfiguringGlobal()) { ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); @@ -115,6 +117,8 @@ void ConfigureSystem::ApplyConfiguration() { } } + Settings::values.device_name = ui->device_name_edit->text().toStdString(); + if (!enabled) { return; } diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index b234ea8..46892f5 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -432,6 +432,13 @@ + + + + Device Name + + + @@ -476,6 +483,13 @@ + + + + 128 + + + diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 48f71b5..2ebb803 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -72,6 +72,9 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent) // Force game list reload if any of the relevant settings are changed. connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); + connect(ui->show_compat, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); + connect(ui->show_size, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); + connect(ui->show_types, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); connect(ui->game_icon_size_combobox, QOverload::of(&QComboBox::currentIndexChanged), this, &ConfigureUi::RequestGameListUpdate); connect(ui->folder_icon_size_combobox, QOverload::of(&QComboBox::currentIndexChanged), @@ -109,6 +112,9 @@ void ConfigureUi::ApplyConfiguration() { UISettings::values.theme = ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); + UISettings::values.show_compat = ui->show_compat->isChecked(); + UISettings::values.show_size = ui->show_size->isChecked(); + UISettings::values.show_types = ui->show_types->isChecked(); UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt(); UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt(); UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); @@ -129,6 +135,9 @@ void ConfigureUi::SetConfiguration() { ui->language_combobox->setCurrentIndex( ui->language_combobox->findData(UISettings::values.language)); ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); + ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); + ui->show_size->setChecked(UISettings::values.show_size.GetValue()); + ui->show_types->setChecked(UISettings::values.show_types.GetValue()); ui->game_icon_size_combobox->setCurrentIndex( ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue())); ui->folder_icon_size_combobox->setCurrentIndex( diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui index a50df7f..10bb273 100644 --- a/src/yuzu/configuration/configure_ui.ui +++ b/src/yuzu/configuration/configure_ui.ui @@ -7,7 +7,7 @@ 0 0 363 - 507 + 562 @@ -76,6 +76,13 @@ + + + + Show Compatibility List + + + @@ -83,6 +90,20 @@ + + + + Show Size Column + + + + + + + Show File Types Column + + + diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp index 807afbe..9bb69ca 100644 --- a/src/yuzu/configuration/input_profiles.cpp +++ b/src/yuzu/configuration/input_profiles.cpp @@ -67,6 +67,8 @@ std::vector InputProfiles::GetInputProfileNames() { profile_names.push_back(profile_name); } + std::stable_sort(profile_names.begin(), profile_names.end()); + return profile_names; } diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index b127bad..22aa19c 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -335,6 +335,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid RetranslateUI(); tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); + tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); item_model->setSortRole(GameListItemPath::SortRole); connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); @@ -553,6 +554,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); +#ifndef WIN32 + QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); + QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); + QAction* create_applications_menu_shortcut = + shortcut_menu->addAction(tr("Add to Applications Menu")); +#endif context_menu.addSeparator(); QAction* properties = context_menu.addAction(tr("Properties")); @@ -618,6 +625,14 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); +#ifndef WIN32 + connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { + emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); + }); + connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { + emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); + }); +#endif connect(properties, &QAction::triggered, [this, path]() { emit OpenPerGameGeneralRequested(path); }); }; @@ -786,6 +801,9 @@ void GameList::PopulateAsync(QVector& game_dirs) { // Update the columns in case UISettings has changed tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); + tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); + tree_view->setColumnHidden(COLUMN_FILE_TYPE, !UISettings::values.show_types); + tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size); // Delete any rows that might already exist if we're repopulating item_model->removeRows(0, item_model->rowCount()); diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index cdf0850..f7ff93e 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -52,6 +52,11 @@ enum class DumpRomFSTarget { SDMC, }; +enum class GameListShortcutTarget { + Desktop, + Applications, +}; + enum class InstalledEntryType { Game, Update, @@ -108,6 +113,8 @@ signals: const std::string& game_path); void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); void CopyTIDRequested(u64 program_id); + void CreateShortcut(u64 program_id, const std::string& game_path, + GameListShortcutTarget target); void NavigateToGamedbEntryRequested(u64 program_id, const CompatibilityList& compatibility_list); void OpenPerGameGeneralRequested(const std::string& file); diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 6198d1e..1800f09 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -145,12 +145,14 @@ public: const char* tooltip; }; // clang-format off + const auto ingame_status = + CompatStatus{QStringLiteral("#f2d624"), QT_TR_NOOP("Ingame"), QT_TR_NOOP("Game starts, but crashes or major glitches prevent it from being completed.")}; static const std::map status_data = { - {QStringLiteral("0"), {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}}, - {QStringLiteral("1"), {QStringLiteral("#47d35c"), QT_TR_NOOP("Great"), QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}}, - {QStringLiteral("2"), {QStringLiteral("#94b242"), QT_TR_NOOP("Okay"), QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}}, - {QStringLiteral("3"), {QStringLiteral("#f2d624"), QT_TR_NOOP("Bad"), QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}}, - {QStringLiteral("4"), {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}}, + {QStringLiteral("0"), {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game can be played without issues.")}}, + {QStringLiteral("1"), {QStringLiteral("#47d35c"), QT_TR_NOOP("Playable"), QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish.")}}, + {QStringLiteral("2"), ingame_status}, + {QStringLiteral("3"), ingame_status}, // Fallback for the removed "Okay" category + {QStringLiteral("4"), {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game loads, but is unable to progress past the Start Screen.")}}, {QStringLiteral("5"), {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}}, {QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}, }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 334465e..ce721c1 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3,17 +3,21 @@ #include #include +#include +#include +#include #include #include #ifdef __APPLE__ #include // for chdir #endif -#ifdef __linux__ +#ifdef __unix__ #include #include #endif // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. +#include "applets/qt_amiibo_settings.h" #include "applets/qt_controller.h" #include "applets/qt_error.h" #include "applets/qt_profile_select.h" @@ -25,6 +29,7 @@ #include "configuration/configure_tas.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" +#include "core/frontend/applets/cabinet.h" #include "core/frontend/applets/controller.h" #include "core/frontend/applets/general_frontend.h" #include "core/frontend/applets/mii_edit.h" @@ -105,12 +110,12 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/hle/kernel/k_process.h" #include "core/hle/service/am/am.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/nfp/nfp.h" #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" #include "core/perf_stats.h" #include "core/telemetry_session.h" #include "input_common/drivers/tas_input.h" +#include "input_common/drivers/virtual_amiibo.h" #include "input_common/main.h" #include "ui_main.h" #include "util/overlay_dialog.h" @@ -123,6 +128,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/compatibility_list.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_dialog.h" +#include "yuzu/configuration/configure_input_per_game.h" #include "yuzu/debugger/console.h" #include "yuzu/debugger/controller.h" #include "yuzu/debugger/profiler.h" @@ -164,6 +170,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; constexpr int default_mouse_hide_timeout = 2500; constexpr int default_mouse_center_timeout = 10; +constexpr int default_input_update_timeout = 1; /** * "Callouts" are one-time instructional messages shown to the user. In the config settings, there @@ -234,6 +241,7 @@ static void LogRuntimes() { LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name); } #endif + LOG_INFO(Frontend, "Qt Compile: {} Runtime: {}", QT_VERSION_STR, qVersion()); } static QString PrettyProductName() { @@ -261,8 +269,20 @@ static QString PrettyProductName() { return QSysInfo::prettyProductName(); } +#ifdef _WIN32 +static void OverrideWindowsFont() { + // Qt5 chooses these fonts on Windows and they have fairly ugly alphanumeric/cyrllic characters + // Asking to use "MS Shell Dlg 2" gives better other chars while leaving the Chinese Characters. + const QString startup_font = QApplication::font().family(); + const QStringList ugly_fonts = {QStringLiteral("SimSun"), QStringLiteral("PMingLiU")}; + if (ugly_fonts.contains(startup_font)) { + QApplication::setFont(QFont(QStringLiteral("MS Shell Dlg 2"), 9, QFont::Normal)); + } +} +#endif + bool GMainWindow::CheckDarkMode() { -#ifdef __linux__ +#ifdef __unix__ const QPalette test_palette(qApp->palette()); const QColor text_color = test_palette.color(QPalette::Active, QPalette::Text); const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); @@ -270,7 +290,7 @@ bool GMainWindow::CheckDarkMode() { #else // TODO: Windows return false; -#endif // __linux__ +#endif // __unix__ } GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulkan) @@ -278,9 +298,10 @@ GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulkan input_subsystem{std::make_shared()}, config{std::move(config_)}, vfs{std::make_shared()}, provider{std::make_unique()} { -#ifdef __linux__ +#ifdef __unix__ SetupSigInterrupts(); #endif + system->Initialize(); Common::Log::Initialize(); LoadTranslation(); @@ -328,6 +349,7 @@ GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulkan const auto override_build = fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id); const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; + const auto processor_count = std::thread::hardware_concurrency(); LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); LogRuntimes(); @@ -346,7 +368,11 @@ GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulkan } } LOG_INFO(Frontend, "Host CPU: {}", cpu_string); + if (std::optional processor_core = Common::GetProcessorCount()) { + LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core); + } #endif + LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count); LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); @@ -383,6 +409,10 @@ GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulkan mouse_center_timer.setInterval(default_mouse_center_timeout); connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor); + update_input_timer.setInterval(default_input_update_timeout); + connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers); + update_input_timer.start(); + MigrateConfigFiles(); if (has_broken_vulkan) { @@ -495,7 +525,7 @@ GMainWindow::~GMainWindow() { delete render_window; } -#ifdef __linux__ +#ifdef __unix__ ::close(sig_interrupt_fds[0]); ::close(sig_interrupt_fds[1]); #endif @@ -529,6 +559,11 @@ void GMainWindow::RegisterMetaTypes() { // Register applet types + // Cabinet Applet + qRegisterMetaType("Core::Frontend::CabinetParameters"); + qRegisterMetaType>( + "std::shared_ptr"); + // Controller Applet qRegisterMetaType("Core::Frontend::ControllerParameters"); @@ -550,6 +585,21 @@ void GMainWindow::RegisterMetaTypes() { qRegisterMetaType("Core::SystemResultStatus"); } +void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, + std::shared_ptr nfp_device) { + QtAmiiboSettingsDialog dialog(this, parameters, input_subsystem.get(), nfp_device); + + dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | + Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + dialog.setWindowModality(Qt::WindowModal); + if (dialog.exec() == QDialog::Rejected) { + emit AmiiboSettingsFinished(false, {}); + return; + } + + emit AmiiboSettingsFinished(true, dialog.GetName()); +} + void GMainWindow::ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters) { QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get(), *system); @@ -898,7 +948,6 @@ void GMainWindow::InitializeWidgets() { statusBar()->addPermanentWidget(label); } - // TODO (flTobi): Add the widget when multiplayer is fully implemented statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); @@ -965,29 +1014,11 @@ void GMainWindow::InitializeWidgets() { renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton")); renderer_status_button->setCheckable(true); renderer_status_button->setFocusPolicy(Qt::NoFocus); - connect(renderer_status_button, &QPushButton::toggled, [this](bool checked) { - renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL")); - }); - renderer_status_button->toggle(); - + connect(renderer_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleGraphicsAPI); + UpdateAPIText(); + renderer_status_button->setCheckable(true); renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan); - connect(renderer_status_button, &QPushButton::clicked, [this] { - if (emulation_running) { - return; - } - if (renderer_status_button->isChecked()) { - Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Vulkan); - } else { - Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL); - if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { - Settings::values.scaling_filter.SetValue(Settings::ScalingFilter::NearestNeighbor); - UpdateFilterText(); - } - } - - system->ApplySettings(); - }); statusBar()->insertPermanentWidget(0, renderer_status_button); statusBar()->setVisible(true); @@ -1219,6 +1250,7 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, &GMainWindow::OnGameListNavigateToGamedbEntry); + connect(game_list, &GameList::CreateShortcut, this, &GMainWindow::OnGameListCreateShortcut); connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); @@ -1365,7 +1397,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) { } void GMainWindow::SetupPrepareForSleep() { -#ifdef __linux__ +#ifdef __unix__ auto bus = QDBusConnection::systemBus(); if (bus.isConnected()) { const bool success = bus.connect( @@ -1379,7 +1411,7 @@ void GMainWindow::SetupPrepareForSleep() { } else { LOG_WARNING(Frontend, "QDBusConnection system bus is not connected"); } -#endif // __linux__ +#endif // __unix__ } void GMainWindow::OnPrepareForSleep(bool prepare_sleep) { @@ -1401,7 +1433,7 @@ void GMainWindow::OnPrepareForSleep(bool prepare_sleep) { } } -#ifdef __linux__ +#ifdef __unix__ static std::optional HoldWakeLockLinux(u32 window_id = 0) { if (!QDBusConnection::sessionBus().isConnected()) { return {}; @@ -1486,14 +1518,14 @@ void GMainWindow::OnSigInterruptNotifierActivated() { emit SigInterrupt(); } -#endif // __linux__ +#endif // __unix__ void GMainWindow::PreventOSSleep() { #ifdef _WIN32 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); #elif defined(HAVE_SDL2) SDL_DisableScreenSaver(); -#ifdef __linux__ +#ifdef __unix__ auto reply = HoldWakeLockLinux(winId()); if (reply) { wake_lock = std::move(reply.value()); @@ -1507,7 +1539,7 @@ void GMainWindow::AllowOSSleep() { SetThreadExecutionState(ES_CONTINUOUS); #elif defined(HAVE_SDL2) SDL_EnableScreenSaver(); -#ifdef __linux__ +#ifdef __unix__ if (!wake_lock.path().isEmpty()) { ReleaseWakeLockLinux(wake_lock); } @@ -1527,6 +1559,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p system->SetFilesystem(vfs); system->SetAppletFrontendSet({ + std::make_unique(*this), // Amiibo Settings std::make_unique(*this), // Controller Selector std::make_unique(*this), // Error Display nullptr, // Mii Editor @@ -1628,6 +1661,11 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t LOG_INFO(Frontend, "yuzu starting..."); StoreRecentFile(filename); // Put the filename on top of the list + // Save configurations + UpdateUISettings(); + game_list->SaveInterfaceLayout(); + config->Save(); + u64 title_id{0}; last_filename_booted = filename; @@ -1644,14 +1682,10 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t ? Common::FS::PathToUTF8String(file_path.filename()) : fmt::format("{:016X}", title_id); Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); + system->HIDCore().ReloadInputDevices(); system->ApplySettings(); } - // Save configurations - UpdateUISettings(); - game_list->SaveInterfaceLayout(); - config->Save(); - Settings::LogSettings(); if (UISettings::values.select_user_on_boot) { @@ -1675,9 +1709,6 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t system->RegisterExecuteProgramCallback( [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); }); - // Register an Exit callback such that Core can exit the currently running application. - system->RegisterExitCallback([this]() { render_window->Exit(); }); - connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity); // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views @@ -1758,15 +1789,22 @@ void GMainWindow::ShutdownGame() { AllowOSSleep(); + // Disable unlimited frame rate + Settings::values.use_speed_limit.SetValue(true); + system->SetShuttingDown(true); system->DetachDebugger(); discord_rpc->Pause(); - emu_thread->RequestStop(); + + RequestGameExit(); emit EmulationStopping(); // Wait for emulation thread to complete and delete it - emu_thread->wait(); + if (!emu_thread->wait(5000)) { + emu_thread->ForceStop(); + emu_thread->wait(); + } emu_thread = nullptr; emulation_running = false; @@ -1882,6 +1920,8 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target case GameListOpenTarget::SaveData: { open_target = tr("Save Data"); const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); + auto vfs_nand_dir = + vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); if (has_user_save) { // User save data @@ -1908,15 +1948,15 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target ASSERT(user_id); const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( - *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, - program_id, user_id->AsU128(), 0); + *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, + FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0); path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); } else { // Device save data const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath( - *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, - program_id, {}, 0); + *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, + FileSys::SaveDataType::SaveData, program_id, {}, 0); path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path); } @@ -1935,6 +1975,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target } default: UNIMPLEMENTED(); + break; } const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path)); @@ -2002,38 +2043,50 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src return true; } +QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const { + switch (type) { + case InstalledEntryType::Game: + return tr("Error Removing Contents"); + case InstalledEntryType::Update: + return tr("Error Removing Update"); + case InstalledEntryType::AddOnContent: + return tr("Error Removing DLC"); + default: + return QStringLiteral("Error Removing "); + } +} void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { - const QString entry_type = [this, type] { + const QString entry_question = [type] { switch (type) { case InstalledEntryType::Game: - return tr("Contents"); + return tr("Remove Installed Game Contents?"); case InstalledEntryType::Update: - return tr("Update"); + return tr("Remove Installed Game Update?"); case InstalledEntryType::AddOnContent: - return tr("DLC"); + return tr("Remove Installed Game DLC?"); default: - return QString{}; + return QStringLiteral("Remove Installed Game ?"); } }(); - if (QMessageBox::question( - this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { + if (QMessageBox::question(this, tr("Remove Entry"), entry_question, + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No) != QMessageBox::Yes) { return; } switch (type) { case InstalledEntryType::Game: - RemoveBaseContent(program_id, entry_type); + RemoveBaseContent(program_id, type); [[fallthrough]]; case InstalledEntryType::Update: - RemoveUpdateContent(program_id, entry_type); + RemoveUpdateContent(program_id, type); if (type != InstalledEntryType::Game) { break; } [[fallthrough]]; case InstalledEntryType::AddOnContent: - RemoveAddOnContent(program_id, entry_type); + RemoveAddOnContent(program_id, type); break; } Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / @@ -2041,7 +2094,7 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT game_list->PopulateAsync(UISettings::values.game_dirs); } -void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) { +void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) { const auto& fs_controller = system->GetFileSystemController(); const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); @@ -2051,12 +2104,12 @@ void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) { tr("Successfully removed the installed base game.")); } else { QMessageBox::warning( - this, tr("Error Removing %1").arg(entry_type), + this, GetGameListErrorRemoving(type), tr("The base game is not installed in the NAND and cannot be removed.")); } } -void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) { +void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) { const auto update_id = program_id | 0x800; const auto& fs_controller = system->GetFileSystemController(); const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || @@ -2066,12 +2119,12 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) QMessageBox::information(this, tr("Successfully Removed"), tr("Successfully removed the installed update.")); } else { - QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), + QMessageBox::warning(this, GetGameListErrorRemoving(type), tr("There is no update installed for this title.")); } } -void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) { +void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) { u32 count{}; const auto& fs_controller = system->GetFileSystemController(); const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( @@ -2089,7 +2142,7 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) } if (count == 0) { - QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), + QMessageBox::warning(this, GetGameListErrorRemoving(type), tr("There are no DLC installed for this title.")); return; } @@ -2100,7 +2153,7 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, const std::string& game_path) { - const QString question = [this, target] { + const QString question = [target] { switch (target) { case GameListRemoveTarget::GlShaderCache: return tr("Delete OpenGL Transferable Shader Cache?"); @@ -2328,6 +2381,152 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); } +void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, + GameListShortcutTarget target) { + // Get path to yuzu executable + const QStringList args = QApplication::arguments(); + std::filesystem::path yuzu_command = args[0].toStdString(); + +#if defined(__linux__) || defined(__FreeBSD__) + // If relative path, make it an absolute path + if (yuzu_command.c_str()[0] == '.') { + yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; + } + +#if defined(__linux__) + // Warn once if we are making a shortcut to a volatile AppImage + const std::string appimage_ending = + std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); + if (yuzu_command.string().ends_with(appimage_ending) && + !UISettings::values.shortcut_already_warned) { + if (QMessageBox::warning(this, tr("Create Shortcut"), + tr("This will create a shortcut to the current AppImage. This may " + "not work well if you update. Continue?"), + QMessageBox::StandardButton::Ok | + QMessageBox::StandardButton::Cancel) == + QMessageBox::StandardButton::Cancel) { + return; + } + UISettings::values.shortcut_already_warned = true; + } +#endif // __linux__ +#endif // __linux__ || __FreeBSD__ + + std::filesystem::path target_directory{}; + // Determine target directory for shortcut +#if defined(__linux__) || defined(__FreeBSD__) + const char* home = std::getenv("HOME"); + const std::filesystem::path home_path = (home == nullptr ? "~" : home); + const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); + + if (target == GameListShortcutTarget::Desktop) { + target_directory = home_path / "Desktop"; + if (!Common::FS::IsDir(target_directory)) { + QMessageBox::critical( + this, tr("Create Shortcut"), + tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.") + .arg(QString::fromStdString(target_directory)), + QMessageBox::StandardButton::Ok); + return; + } + } else if (target == GameListShortcutTarget::Applications) { + target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) / + "applications"; + if (!Common::FS::CreateDirs(target_directory)) { + QMessageBox::critical(this, tr("Create Shortcut"), + tr("Cannot create shortcut in applications menu. Path \"%1\" " + "does not exist and cannot be created.") + .arg(QString::fromStdString(target_directory)), + QMessageBox::StandardButton::Ok); + return; + } + } +#endif + + const std::string game_file_name = std::filesystem::path(game_path).filename().string(); + // Determine full paths for icon and shortcut +#if defined(__linux__) || defined(__FreeBSD__) + std::filesystem::path system_icons_path = + (xdg_data_home == nullptr ? home_path / ".local/share/" : xdg_data_home) / + "icons/hicolor/256x256"; + if (!Common::FS::CreateDirs(system_icons_path)) { + QMessageBox::critical( + this, tr("Create Icon"), + tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") + .arg(QString::fromStdString(system_icons_path)), + QMessageBox::StandardButton::Ok); + return; + } + std::filesystem::path icon_path = + system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name) + : fmt::format("yuzu-{:016X}.png", program_id)); + const std::filesystem::path shortcut_path = + target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) + : fmt::format("yuzu-{:016X}.desktop", program_id)); +#else + const std::filesystem::path icon_path{}; + const std::filesystem::path shortcut_path{}; +#endif + + // Get title from game file + const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), + system->GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); + + std::string title{fmt::format("{:016X}", program_id)}; + + if (control.first != nullptr) { + title = control.first->GetApplicationName(); + } else { + loader->ReadTitle(title); + } + + // Get icon from game file + std::vector icon_image_file{}; + if (control.second != nullptr) { + icon_image_file = control.second->ReadAllBytes(); + } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { + LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); + } + + QImage icon_jpeg = + QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size())); +#if defined(__linux__) || defined(__FreeBSD__) + // Convert and write the icon as a PNG + if (!icon_jpeg.save(QString::fromStdString(icon_path.string()))) { + LOG_ERROR(Frontend, "Could not write icon as PNG to file"); + } else { + LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); + } +#endif // __linux__ + +#if defined(__linux__) || defined(__FreeBSD__) + const std::string comment = + tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); + const std::string arguments = fmt::format("-g \"{:s}\"", game_path); + const std::string categories = "Game;Emulator;Qt;"; + const std::string keywords = "Switch;Nintendo;"; +#else + const std::string comment{}; + const std::string arguments{}; + const std::string categories{}; + const std::string keywords{}; +#endif + if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), + yuzu_command.string(), arguments, categories, keywords)) { + QMessageBox::critical(this, tr("Create Shortcut"), + tr("Failed to create a shortcut at %1") + .arg(QString::fromStdString(shortcut_path.string()))); + return; + } + + LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string()); + QMessageBox::information( + this, tr("Create Shortcut"), + tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title))); +} + void GMainWindow::OnGameListOpenDirectory(const QString& directory) { std::filesystem::path fs_path; if (directory == QStringLiteral("SDMC")) { @@ -2461,6 +2660,9 @@ void GMainWindow::OnMenuInstallToNAND() { return; } + // Save folder location of the first selected file + UISettings::values.roms_path = QFileInfo(filenames[0]).path(); + int remaining = filenames.size(); // This would only overflow above 2^43 bytes (8.796 TB) @@ -2757,6 +2959,7 @@ void GMainWindow::OnStopGame() { ShutdownGame(); Settings::RestoreGlobalState(system->IsPoweredOn()); + system->HIDCore().ReloadInputDevices(); UpdateStatusButtons(); } @@ -2787,6 +2990,21 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex } void GMainWindow::OnMenuReportCompatibility() { +#if defined(ARCHITECTURE_x86_64) && !defined(__APPLE__) + const auto& caps = Common::GetCPUCaps(); + const bool has_fma = caps.fma || caps.fma4; + const auto processor_count = std::thread::hardware_concurrency(); + const bool has_4threads = processor_count == 0 || processor_count >= 4; + const bool has_8gb_ram = Common::GetMemInfo().TotalPhysicalMemory >= 8_GiB; + const bool has_broken_vulkan = UISettings::values.has_broken_vulkan; + + if (!has_fma || !has_4threads || !has_8gb_ram || has_broken_vulkan) { + QMessageBox::critical(this, tr("Hardware requirements not met"), + tr("Your system does not meet the recommended hardware requirements. " + "Compatibility reporting has been disabled.")); + return; + } + if (!Settings::values.yuzu_token.GetValue().empty() && !Settings::values.yuzu_username.GetValue().empty()) { CompatDB compatdb{system->TelemetrySession(), this}; @@ -2799,6 +3017,11 @@ void GMainWindow::OnMenuReportCompatibility() { "> " "Web.")); } +#else + QMessageBox::critical(this, tr("Hardware requirements not met"), + tr("Your system does not meet the recommended hardware requirements. " + "Compatibility reporting has been disabled.")); +#endif } void GMainWindow::OpenURL(const QUrl& url) { @@ -2844,9 +3067,15 @@ static QScreen* GuessCurrentScreen(QWidget* window) { }); } +bool GMainWindow::UsingExclusiveFullscreen() { + return Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive || + QGuiApplication::platformName() == QStringLiteral("wayland") || + QGuiApplication::platformName() == QStringLiteral("wayland-egl"); +} + void GMainWindow::ShowFullscreen() { - const auto show_fullscreen = [](QWidget* window) { - if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { + const auto show_fullscreen = [this](QWidget* window) { + if (UsingExclusiveFullscreen()) { window->showFullScreen(); return; } @@ -2874,7 +3103,7 @@ void GMainWindow::ShowFullscreen() { void GMainWindow::HideFullscreen() { if (ui->action_Single_Window_Mode->isChecked()) { - if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { + if (UsingExclusiveFullscreen()) { showNormal(); restoreGeometry(UISettings::values.geometry); } else { @@ -2888,7 +3117,7 @@ void GMainWindow::HideFullscreen() { statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); ui->menubar->show(); } else { - if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { + if (UsingExclusiveFullscreen()) { render_window->showNormal(); render_window->restoreGeometry(UISettings::values.renderwindow_geometry); } else { @@ -3152,6 +3381,7 @@ void GMainWindow::OnToggleGpuAccuracy() { case Settings::GPUAccuracy::Extreme: default: { Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); + break; } } @@ -3175,6 +3405,18 @@ void GMainWindow::OnToggleAdaptingFilter() { UpdateFilterText(); } +void GMainWindow::OnToggleGraphicsAPI() { + auto api = Settings::values.renderer_backend.GetValue(); + if (api == Settings::RendererBackend::OpenGL) { + api = Settings::RendererBackend::Vulkan; + } else { + api = Settings::RendererBackend::OpenGL; + } + Settings::values.renderer_backend.SetValue(api); + renderer_status_button->setChecked(api == Settings::RendererBackend::Vulkan); + UpdateAPIText(); +} + void GMainWindow::OnConfigurePerGame() { const u64 title_id = system->GetCurrentProcessProgramID(); OpenPerGameConfiguration(title_id, current_game_path.toStdString()); @@ -3203,6 +3445,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file // Do not cause the global config to write local settings into the config file const bool is_powered_on = system->IsPoweredOn(); Settings::RestoreGlobalState(is_powered_on); + system->HIDCore().ReloadInputDevices(); UISettings::values.configuration_applied = false; @@ -3211,6 +3454,38 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file } } +bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title, + const std::string& comment, const std::string& icon_path, + const std::string& command, const std::string& arguments, + const std::string& categories, const std::string& keywords) { +#if defined(__linux__) || defined(__FreeBSD__) + // This desktop file template was writting referencing + // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html + std::string shortcut_contents{}; + shortcut_contents.append("[Desktop Entry]\n"); + shortcut_contents.append("Type=Application\n"); + shortcut_contents.append("Version=1.0\n"); + shortcut_contents.append(fmt::format("Name={:s}\n", title)); + shortcut_contents.append(fmt::format("Comment={:s}\n", comment)); + shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path)); + shortcut_contents.append(fmt::format("TryExec={:s}\n", command)); + shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments)); + shortcut_contents.append(fmt::format("Categories={:s}\n", categories)); + shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords)); + + std::ofstream shortcut_stream(shortcut_path); + if (!shortcut_stream.is_open()) { + LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path); + return false; + } + shortcut_stream << shortcut_contents; + shortcut_stream.close(); + + return true; +#endif + return false; +} + void GMainWindow::OnLoadAmiibo() { if (emu_thread == nullptr || !emu_thread->IsRunning()) { return; @@ -3219,21 +3494,16 @@ void GMainWindow::OnLoadAmiibo() { return; } - Service::SM::ServiceManager& sm = system->ServiceManager(); - auto nfc = sm.GetService("nfp:user"); - if (nfc == nullptr) { - QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); - return; - } - const auto nfc_state = nfc->GetCurrentState(); - if (nfc_state == Service::NFP::DeviceState::TagFound || - nfc_state == Service::NFP::DeviceState::TagMounted) { - nfc->CloseAmiibo(); + auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); + + // Remove amiibo if one is connected + if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { + virtual_amiibo->CloseAmiibo(); QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); return; } - if (nfc_state != Service::NFP::DeviceState::SearchingForTag) { + if (virtual_amiibo->GetCurrentState() != InputCommon::VirtualAmiibo::State::WaitingForAmiibo) { QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); return; } @@ -3252,24 +3522,30 @@ void GMainWindow::OnLoadAmiibo() { } void GMainWindow::LoadAmiibo(const QString& filename) { - Service::SM::ServiceManager& sm = system->ServiceManager(); - auto nfc = sm.GetService("nfp:user"); - if (nfc == nullptr) { - return; - } - + auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); + const QString title = tr("Error loading Amiibo data"); // Remove amiibo if one is connected - const auto nfc_state = nfc->GetCurrentState(); - if (nfc_state == Service::NFP::DeviceState::TagFound || - nfc_state == Service::NFP::DeviceState::TagMounted) { - nfc->CloseAmiibo(); + if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { + virtual_amiibo->CloseAmiibo(); QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); return; } - if (!nfc->LoadAmiibo(filename.toStdString())) { - QMessageBox::warning(this, tr("Error loading Amiibo data"), - tr("Unable to load Amiibo data.")); + switch (virtual_amiibo->LoadAmiibo(filename.toStdString())) { + case InputCommon::VirtualAmiibo::Info::NotAnAmiibo: + QMessageBox::warning(this, title, tr("The selected file is not a valid amiibo")); + break; + case InputCommon::VirtualAmiibo::Info::UnableToLoad: + QMessageBox::warning(this, title, tr("The selected file is already on use")); + break; + case InputCommon::VirtualAmiibo::Info::WrongDeviceState: + QMessageBox::warning(this, title, tr("The current game is not looking for amiibos")); + break; + case InputCommon::VirtualAmiibo::Info::Unknown: + QMessageBox::warning(this, title, tr("An unknown error occurred")); + break; + default: + break; } } @@ -3450,9 +3726,10 @@ void GMainWindow::UpdateStatusBar() { } if (!Settings::values.use_speed_limit) { game_fps_label->setText( - tr("Game: %1 FPS (Unlocked)").arg(results.average_game_fps, 0, 'f', 0)); + tr("Game: %1 FPS (Unlocked)").arg(std::round(results.average_game_fps), 0, 'f', 0)); } else { - game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); + game_fps_label->setText( + tr("Game: %1 FPS").arg(std::round(results.average_game_fps), 0, 'f', 0)); } emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); @@ -3482,6 +3759,7 @@ void GMainWindow::UpdateGPUAccuracyButton() { default: { gpu_accuracy_button->setText(tr("GPU ERROR")); gpu_accuracy_button->setChecked(true); + break; } } } @@ -3492,6 +3770,21 @@ void GMainWindow::UpdateDockedButton() { dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD")); } +void GMainWindow::UpdateAPIText() { + const auto api = Settings::values.renderer_backend.GetValue(); + switch (api) { + case Settings::RendererBackend::OpenGL: + renderer_status_button->setText(tr("OPENGL")); + break; + case Settings::RendererBackend::Vulkan: + renderer_status_button->setText(tr("VULKAN")); + break; + case Settings::RendererBackend::Null: + renderer_status_button->setText(tr("NULL")); + break; + } +} + void GMainWindow::UpdateFilterText() { const auto filter = Settings::values.scaling_filter.GetValue(); switch (filter) { @@ -3528,6 +3821,9 @@ void GMainWindow::UpdateAAText() { case Settings::AntiAliasing::Fxaa: aa_status_button->setText(tr("FXAA")); break; + case Settings::AntiAliasing::Smaa: + aa_status_button->setText(tr("SMAA")); + break; default: aa_status_button->setText(tr("NO AA")); break; @@ -3537,6 +3833,7 @@ void GMainWindow::UpdateAAText() { void GMainWindow::UpdateStatusButtons() { renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan); + UpdateAPIText(); UpdateGPUAccuracyButton(); UpdateDockedButton(); UpdateFilterText(); @@ -3561,6 +3858,13 @@ void GMainWindow::UpdateUISettings() { UISettings::values.first_start = false; } +void GMainWindow::UpdateInputDrivers() { + if (!input_subsystem) { + return; + } + input_subsystem->PumpEvents(); +} + void GMainWindow::HideMouseCursor() { if (emu_thread == nullptr && UISettings::values.hide_mouse) { mouse_hide_timer.stop(); @@ -3660,6 +3964,7 @@ void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string detai ShutdownGame(); Settings::RestoreGlobalState(system->IsPoweredOn()); + system->HIDCore().ReloadInputDevices(); UpdateStatusButtons(); } } else { @@ -3811,18 +4116,19 @@ void GMainWindow::closeEvent(QCloseEvent* event) { // Unload controllers early controller_dialog->UnloadController(); game_list->UnloadController(); - system->HIDCore().UnloadInputDevices(); // Shutdown session if the emu thread is active... if (emu_thread != nullptr) { ShutdownGame(); Settings::RestoreGlobalState(system->IsPoweredOn()); + system->HIDCore().ReloadInputDevices(); UpdateStatusButtons(); } render_window->close(); multiplayer_state->Close(); + system->HIDCore().UnloadInputDevices(); system->GetRoomNetwork().Shutdown(); QWidget::closeEvent(event); @@ -3961,7 +4267,6 @@ void GMainWindow::UpdateUITheme() { const QString default_theme = QString::fromUtf8(UISettings::themes[static_cast(Config::default_theme)].second); QString current_theme = UISettings::values.theme; - QStringList theme_paths(default_theme_paths); if (current_theme.isEmpty()) { current_theme = default_theme; @@ -3974,7 +4279,7 @@ void GMainWindow::UpdateUITheme() { if (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")) { QIcon::setThemeName(current_theme == QStringLiteral("colorful") ? current_theme : startup_icon_theme); - QIcon::setThemeSearchPaths(theme_paths); + QIcon::setThemeSearchPaths(QStringList(default_theme_paths)); if (CheckDarkMode()) { current_theme = QStringLiteral("default_dark"); } @@ -4052,7 +4357,7 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { } void GMainWindow::changeEvent(QEvent* event) { -#ifdef __linux__ +#ifdef __unix__ // PaletteChange event appears to only reach so far into the GUI, explicitly asking to // UpdateUITheme is a decent work around if (event->type() == QEvent::PaletteChange) { @@ -4067,7 +4372,7 @@ void GMainWindow::changeEvent(QEvent* event) { } last_window_color = window_color; } -#endif // __linux__ +#endif // __unix__ QWidget::changeEvent(event); } @@ -4094,7 +4399,8 @@ int main(int argc, char* argv[]) { } #endif - if (StartupChecks(argv[0], &has_broken_vulkan)) { + if (StartupChecks(argv[0], &has_broken_vulkan, + Settings::values.perform_vulkan_check.GetValue())) { return 0; } @@ -4133,14 +4439,20 @@ int main(int argc, char* argv[]) { QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); QApplication app(argc, argv); +#ifdef _WIN32 + OverrideWindowsFont(); +#endif + // Workaround for QTBUG-85409, for Suzhou numerals the number 1 is actually \u3021 // so we can see if we get \u3008 instead // TL;DR all other number formats are consecutive in unicode code points // This bug is fixed in Qt6, specifically 6.0.0-alpha1 +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) const QLocale locale = QLocale::system(); if (QStringLiteral("\u3008") == locale.toString(1)) { QLocale::setDefault(QLocale::system().name()); } +#endif // Qt changes the locale and causes issues in float conversion using std::to_string() when // generating shaders diff --git a/src/yuzu/main.h b/src/yuzu/main.h index f7aa8e4..1047ba2 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -15,7 +15,7 @@ #include "yuzu/compatibility_list.h" #include "yuzu/hotkeys.h" -#ifdef __linux__ +#ifdef __unix__ #include #include #include @@ -38,6 +38,7 @@ class QProgressDialog; class WaitTreeWidget; enum class GameListOpenTarget; enum class GameListRemoveTarget; +enum class GameListShortcutTarget; enum class DumpRomFSTarget; enum class InstalledEntryType; class GameListPlaceholder; @@ -55,6 +56,7 @@ class System; } // namespace Core namespace Core::Frontend { +struct CabinetParameters; struct ControllerParameters; struct InlineAppearParameters; struct InlineTextParameters; @@ -82,6 +84,10 @@ enum class SwkbdReplyType : u32; enum class WebExitReason : u32; } // namespace Service::AM::Applets +namespace Service::NFP { +class NfpDevice; +} // namespace Service::NFP + namespace Ui { class MainWindow; } @@ -149,6 +155,8 @@ signals: void UpdateInstallProgress(); + void AmiiboSettingsFinished(bool is_success, const std::string& name); + void ControllerSelectorReconfigureFinished(); void ErrorDisplayFinished(); @@ -170,6 +178,8 @@ public slots: void OnExecuteProgram(std::size_t program_index); void OnExit(); void OnSaveConfig(); + void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, + std::shared_ptr nfp_device); void ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters); void SoftwareKeyboardInitialize( @@ -255,7 +265,7 @@ private: void changeEvent(QEvent* event) override; void closeEvent(QCloseEvent* event) override; -#ifdef __linux__ +#ifdef __unix__ void SetupSigInterrupts(); static void HandleSigInterrupt(int); void OnSigInterruptNotifierActivated(); @@ -284,6 +294,8 @@ private slots: void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list); + void OnGameListCreateShortcut(u64 program_id, const std::string& game_path, + GameListShortcutTarget target); void OnGameListOpenDirectory(const QString& directory); void OnGameListAddDirectory(); void OnGameListShowList(bool show); @@ -298,6 +310,7 @@ private slots: void OnTasStartStop(); void OnTasRecord(); void OnTasReset(); + void OnToggleGraphicsAPI(); void OnToggleDockedMode(); void OnToggleGpuAccuracy(); void OnToggleAdaptingFilter(); @@ -310,6 +323,7 @@ private slots: void OnDisplayTitleBars(bool); void InitializeHotkeys(); void ToggleFullscreen(); + bool UsingExclusiveFullscreen(); void ShowFullscreen(); void HideFullscreen(); void ToggleWindowMode(); @@ -324,9 +338,10 @@ private slots: void OnMouseActivity(); private: - void RemoveBaseContent(u64 program_id, const QString& entry_type); - void RemoveUpdateContent(u64 program_id, const QString& entry_type); - void RemoveAddOnContent(u64 program_id, const QString& entry_type); + QString GetGameListErrorRemoving(InstalledEntryType type) const; + void RemoveBaseContent(u64 program_id, InstalledEntryType type); + void RemoveUpdateContent(u64 program_id, InstalledEntryType type); + void RemoveAddOnContent(u64 program_id, InstalledEntryType type); void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); void RemoveAllTransferableShaderCaches(u64 program_id); void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); @@ -337,12 +352,14 @@ private: void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, std::string_view gpu_vendor = {}); void UpdateDockedButton(); + void UpdateAPIText(); void UpdateFilterText(); void UpdateAAText(); void UpdateStatusBar(); void UpdateGPUAccuracyButton(); void UpdateStatusButtons(); void UpdateUISettings(); + void UpdateInputDrivers(); void HideMouseCursor(); void ShowMouseCursor(); void CenterMouseCursor(); @@ -352,6 +369,10 @@ private: bool CheckDarkMode(); QString GetTasStateDescription() const; + bool CreateShortcut(const std::string& shortcut_path, const std::string& title, + const std::string& comment, const std::string& icon_path, + const std::string& command, const std::string& arguments, + const std::string& categories, const std::string& keywords); std::unique_ptr ui; @@ -394,6 +415,7 @@ private: bool auto_muted = false; QTimer mouse_hide_timer; QTimer mouse_center_timer; + QTimer update_input_timer; QString startup_icon_theme; bool os_dark_mode = false; @@ -435,7 +457,7 @@ private: // True if TAS recording dialog is visible bool is_tas_recording_dialog_active{}; -#ifdef __linux__ +#ifdef __unix__ QSocketNotifier* sig_interrupt_notifier; static std::array sig_interrupt_fds; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 74d49db..013ba0c 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -55,7 +55,6 @@ - @@ -232,6 +231,9 @@ Con&figure... + + QAction::PreferencesRole + @@ -364,6 +366,9 @@ &Configure TAS... + + QAction::NoRole + @@ -372,6 +377,9 @@ Configure C&urrent Game... + + QAction::NoRole + diff --git a/src/yuzu/multiplayer/chat_room.h b/src/yuzu/multiplayer/chat_room.h index 01c70fa..dd71ea4 100644 --- a/src/yuzu/multiplayer/chat_room.h +++ b/src/yuzu/multiplayer/chat_room.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp index 10bf0a4..cbd52da 100644 --- a/src/yuzu/multiplayer/direct_connect.cpp +++ b/src/yuzu/multiplayer/direct_connect.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include "common/settings.h" diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp index ae2738a..285bb15 100644 --- a/src/yuzu/multiplayer/state.cpp +++ b/src/yuzu/multiplayer/state.cpp @@ -268,7 +268,7 @@ bool MultiplayerState::OnCloseRoom() { return true; } // Save ban list - UISettings::values.multiplayer_ban_list = std::move(room->GetBanList()); + UISettings::values.multiplayer_ban_list = room->GetBanList(); room->Destroy(); announce_multiplayer_session->Stop(); diff --git a/src/yuzu/multiplayer/validation.h b/src/yuzu/multiplayer/validation.h index dabf860..dd25af2 100644 --- a/src/yuzu/multiplayer/validation.h +++ b/src/yuzu/multiplayer/validation.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include #include @@ -29,19 +29,21 @@ public: private: /// room name can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20 - QRegExp room_name_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$")); - QRegExpValidator room_name; + QRegularExpression room_name_regex = + QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}")); + QRegularExpressionValidator room_name; /// nickname can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20 - QRegExp nickname_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$")); - QRegExpValidator nickname; + const QRegularExpression nickname_regex = + QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}")); + QRegularExpressionValidator nickname; /// ipv4 address only // TODO remove this when we support hostnames in direct connect - QRegExp ip_regex = QRegExp(QStringLiteral( + QRegularExpression ip_regex = QRegularExpression(QStringLiteral( "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|" "2[0-4][0-9]|25[0-5])")); - QRegExpValidator ip; + QRegularExpressionValidator ip; /// port must be between 0 and 65535 QIntValidator port; diff --git a/src/yuzu/precompiled_headers.h b/src/yuzu/precompiled_headers.h new file mode 100644 index 0000000..aabae73 --- /dev/null +++ b/src/yuzu/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index 29b87da..9f702fe 100644 --- a/src/yuzu/startup_checks.cpp +++ b/src/yuzu/startup_checks.cpp @@ -4,16 +4,19 @@ #include "video_core/vulkan_common/vulkan_wrapper.h" #ifdef _WIN32 -#include // for memset, strncpy +#include #include #include #elif defined(YUZU_UNIX) +#include #include +#include +#include #include #include #endif -#include +#include #include "video_core/vulkan_common/vulkan_instance.h" #include "video_core/vulkan_common/vulkan_library.h" #include "yuzu/startup_checks.h" @@ -24,10 +27,10 @@ void CheckVulkan() { Vulkan::vk::InstanceDispatch dld; const Common::DynamicLibrary library = Vulkan::OpenLibrary(); const Vulkan::vk::Instance instance = - Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0); + Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_1); } catch (const Vulkan::vk::Exception& exception) { - std::fprintf(stderr, "Failed to initialize Vulkan: %s\n", exception.what()); + fmt::print(stderr, "Failed to initialize Vulkan: {}\n", exception.what()); } } @@ -49,75 +52,96 @@ bool CheckEnvVars(bool* is_child) { *is_child = true; return false; } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) { - std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", - IS_CHILD_ENV_VAR, GetLastError()); + fmt::print(stderr, "SetEnvironmentVariableA failed to set {} with error {}\n", + IS_CHILD_ENV_VAR, GetLastError()); + return true; + } +#elif defined(YUZU_UNIX) + const char* startup_check_var = getenv(STARTUP_CHECK_ENV_VAR); + if (startup_check_var != nullptr && + std::strncmp(startup_check_var, ENV_VAR_ENABLED_TEXT, 8) == 0) { + CheckVulkan(); return true; } #endif return false; } -bool StartupChecks(const char* arg0, bool* has_broken_vulkan) { +bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulkan_check) { #ifdef _WIN32 // Set the startup variable for child processes const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT); if (!env_var_set) { - std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", - STARTUP_CHECK_ENV_VAR, GetLastError()); + fmt::print(stderr, "SetEnvironmentVariableA failed to set {} with error {}\n", + STARTUP_CHECK_ENV_VAR, GetLastError()); return false; } - PROCESS_INFORMATION process_info; - std::memset(&process_info, '\0', sizeof(process_info)); + if (perform_vulkan_check) { + // Spawn child process that performs Vulkan check + PROCESS_INFORMATION process_info; + std::memset(&process_info, '\0', sizeof(process_info)); - if (!SpawnChild(arg0, &process_info, 0)) { - return false; - } + if (!SpawnChild(arg0, &process_info, 0)) { + return false; + } - // Wait until the processs exits and get exit code from it - WaitForSingleObject(process_info.hProcess, INFINITE); - DWORD exit_code = STILL_ACTIVE; - const int err = GetExitCodeProcess(process_info.hProcess, &exit_code); - if (err == 0) { - std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError()); - } + // Wait until the processs exits and get exit code from it + WaitForSingleObject(process_info.hProcess, INFINITE); + DWORD exit_code = STILL_ACTIVE; + const int err = GetExitCodeProcess(process_info.hProcess, &exit_code); + if (err == 0) { + fmt::print(stderr, "GetExitCodeProcess failed with error {}\n", GetLastError()); + } - // Vulkan is broken if the child crashed (return value is not zero) - *has_broken_vulkan = (exit_code != 0); + // Vulkan is broken if the child crashed (return value is not zero) + *has_broken_vulkan = (exit_code != 0); - if (CloseHandle(process_info.hProcess) == 0) { - std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError()); - } - if (CloseHandle(process_info.hThread) == 0) { - std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError()); + if (CloseHandle(process_info.hProcess) == 0) { + fmt::print(stderr, "CloseHandle failed with error {}\n", GetLastError()); + } + if (CloseHandle(process_info.hThread) == 0) { + fmt::print(stderr, "CloseHandle failed with error {}\n", GetLastError()); + } } if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) { - std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %d\n", - STARTUP_CHECK_ENV_VAR, GetLastError()); + fmt::print(stderr, "SetEnvironmentVariableA failed to clear {} with error {}\n", + STARTUP_CHECK_ENV_VAR, GetLastError()); } #elif defined(YUZU_UNIX) - const pid_t pid = fork(); - if (pid == 0) { - CheckVulkan(); - return true; - } else if (pid == -1) { + const int env_var_set = setenv(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT, 1); + if (env_var_set == -1) { const int err = errno; - std::fprintf(stderr, "fork failed with error %d\n", err); + fmt::print(stderr, "setenv failed to set {} with error {}\n", STARTUP_CHECK_ENV_VAR, err); return false; } - // Get exit code from child process - int status; - const int r_val = wait(&status); - if (r_val == -1) { - const int err = errno; - std::fprintf(stderr, "wait failed with error %d\n", err); - return false; + if (perform_vulkan_check) { + const pid_t pid = SpawnChild(arg0); + if (pid == -1) { + return false; + } + + // Get exit code from child process + int status; + const int r_val = waitpid(pid, &status, 0); + if (r_val == -1) { + const int err = errno; + fmt::print(stderr, "wait failed with error {}\n", err); + return false; + } + // Vulkan is broken if the child crashed (return value is not zero) + *has_broken_vulkan = (status != 0); + } + + const int env_var_cleared = unsetenv(STARTUP_CHECK_ENV_VAR); + if (env_var_cleared == -1) { + const int err = errno; + fmt::print(stderr, "unsetenv failed to clear {} with error {}\n", STARTUP_CHECK_ENV_VAR, + err); } - // Vulkan is broken if the child crashed (return value is not zero) - *has_broken_vulkan = (status != 0); #endif return false; } @@ -130,7 +154,8 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) { startup_info.cb = sizeof(startup_info); char p_name[255]; - std::strncpy(p_name, arg0, 255); + std::strncpy(p_name, arg0, 254); + p_name[254] = '\0'; const bool process_created = CreateProcessA(nullptr, // lpApplicationName p_name, // lpCommandLine @@ -144,10 +169,29 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) { pi // lpProcessInformation ); if (!process_created) { - std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError()); + fmt::print(stderr, "CreateProcessA failed with error {}\n", GetLastError()); return false; } return true; } +#elif defined(YUZU_UNIX) +pid_t SpawnChild(const char* arg0) { + const pid_t pid = fork(); + + if (pid == -1) { + // error + const int err = errno; + fmt::print(stderr, "fork failed with error {}\n", err); + return pid; + } else if (pid == 0) { + // child + execlp(arg0, arg0, nullptr); + const int err = errno; + fmt::print(stderr, "execl failed with error {}\n", err); + _exit(0); + } + + return pid; +} #endif diff --git a/src/yuzu/startup_checks.h b/src/yuzu/startup_checks.h index f2fc2d9..2f86fb8 100644 --- a/src/yuzu/startup_checks.h +++ b/src/yuzu/startup_checks.h @@ -5,6 +5,8 @@ #ifdef _WIN32 #include +#elif defined(YUZU_UNIX) +#include #endif constexpr char IS_CHILD_ENV_VAR[] = "YUZU_IS_CHILD"; @@ -13,8 +15,10 @@ constexpr char ENV_VAR_ENABLED_TEXT[] = "ON"; void CheckVulkan(); bool CheckEnvVars(bool* is_child); -bool StartupChecks(const char* arg0, bool* has_broken_vulkan); +bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulkan_check); #ifdef _WIN32 bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags); +#elif defined(YUZU_UNIX) +pid_t SpawnChild(const char* arg0); #endif diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 753797e..2006b88 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -129,8 +129,16 @@ struct Values { Settings::Setting favorites_expanded{true, "favorites_expanded"}; QVector favorited_ids; + // Compatibility List + Settings::Setting show_compat{false, "show_compat"}; + + // Size & File Types Column + Settings::Setting show_size{true, "show_size"}; + Settings::Setting show_types{true, "show_types"}; + bool configuration_applied; bool reset_to_defaults; + bool shortcut_already_warned{false}; Settings::Setting disable_web_applet{true, "disable_web_applet"}; }; diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 7d8ca3d..f6eeb9d 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -22,8 +22,11 @@ add_executable(yuzu-cmd emu_window/emu_window_sdl2.h emu_window/emu_window_sdl2_gl.cpp emu_window/emu_window_sdl2_gl.h + emu_window/emu_window_sdl2_null.cpp + emu_window/emu_window_sdl2_null.h emu_window/emu_window_sdl2_vk.cpp emu_window/emu_window_sdl2_vk.h + precompiled_headers.h yuzu.cpp yuzu.rc ) @@ -31,21 +34,16 @@ add_executable(yuzu-cmd create_target_directory_groups(yuzu-cmd) target_link_libraries(yuzu-cmd PRIVATE common core input_common) -target_link_libraries(yuzu-cmd PRIVATE inih glad) +target_link_libraries(yuzu-cmd PRIVATE inih::INIReader glad) if (MSVC) target_link_libraries(yuzu-cmd PRIVATE getopt) endif() -target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads) +target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) create_resource("../../dist/yuzu.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon") target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR}) -target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include) - -if (YUZU_USE_EXTERNAL_SDL2) - target_compile_definitions(yuzu-cmd PRIVATE -DYUZU_USE_EXTERNAL_SDL2) - target_include_directories(yuzu-cmd PRIVATE ${PROJECT_BINARY_DIR}/externals/SDL/include) -endif() +target_link_libraries(yuzu-cmd PRIVATE SDL2::SDL2 Vulkan::Headers) if(UNIX AND NOT APPLE) install(TARGETS yuzu-cmd) @@ -55,3 +53,7 @@ if (MSVC) include(CopyYuzuSDLDeps) copy_yuzu_SDL_deps(yuzu-cmd) endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(yuzu-cmd PRIVATE precompiled_headers.h) +endif() diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 66dd0dc..de9b220 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -15,7 +15,7 @@ #pragma clang diagnostic pop #endif -#include +#include #include "common/fs/file.h" #include "common/fs/fs.h" #include "common/fs/path_util.h" @@ -90,7 +90,11 @@ static const std::array, Settings::NativeAnalog::NumAnalogs> template <> void Config::ReadSetting(const std::string& group, Settings::Setting& setting) { - setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); + std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); + if (setting_value.empty()) { + setting_value = setting.GetDefault(); + } + setting = std::move(setting_value); } template <> @@ -282,6 +286,7 @@ void Config::ReadValues() { ReadSetting("Cpu", Settings::values.cpuopt_fastmem); ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives); ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives); + ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts); ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma); ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error); ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr); @@ -299,6 +304,7 @@ void Config::ReadValues() { ReadSetting("Renderer", Settings::values.resolution_setup); ReadSetting("Renderer", Settings::values.scaling_filter); + ReadSetting("Renderer", Settings::values.fsr_sharpening_slider); ReadSetting("Renderer", Settings::values.anti_aliasing); ReadSetting("Renderer", Settings::values.fullscreen_mode); ReadSetting("Renderer", Settings::values.aspect_ratio); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index d214771..6fcf04e 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -6,16 +6,22 @@ namespace DefaultINI { const char* sdl2_config_file = R"( -[ControlsGeneral] + +[ControlsP0] # The input devices and parameters for each Switch native input +# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... # It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." # Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values +# Indicates if this player should be connected at boot +connected= + # for button input, the following devices are available: # - "keyboard" (default) for keyboard input. Required parameters: # - "code": the code of the key to bind # - "sdl" for joystick input using SDL. Required parameters: -# - "joystick": the index of the joystick to bind +# - "guid": SDL identification GUID of the joystick +# - "port": the index of the joystick to bind # - "button"(optional): the index of the button to bind # - "hat"(optional): the index of the hat to bind as direction buttons # - "axis"(optional): the index of the axis to bind @@ -58,12 +64,29 @@ button_screenshot= # - "modifier_scale": a float number representing the applied modifier scale to the analog input. # Must be in range of 0.0-1.0. Defaults to 0.5 # - "sdl" for joystick input using SDL. Required parameters: -# - "joystick": the index of the joystick to bind +# - "guid": SDL identification GUID of the joystick +# - "port": the index of the joystick to bind # - "axis_x": the index of the axis to bind as x-axis (default to 0) # - "axis_y": the index of the axis to bind as y-axis (default to 1) lstick= rstick= +# for motion input, the following devices are available: +# - "keyboard" (default) for emulating random motion input from buttons. Required parameters: +# - "code": the code of the key to bind +# - "sdl" for motion input using SDL. Required parameters: +# - "guid": SDL identification GUID of the joystick +# - "port": the index of the joystick to bind +# - "motion": the index of the motion sensor to bind +# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters: +# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001" +# - "port": the port of the cemu hook server +# - "pad": the index of the joystick +# - "motion": the index of the motion sensor of the joystick to bind +motionleft= +motionright= + +[ControlsGeneral] # To use the debug_pad, prepend `debug_pad_` before each button setting above. # i.e. debug_pad_button_a= @@ -185,6 +208,10 @@ cpuopt_fastmem_exclusives = # 0: Disabled, 1 (default): Enabled cpuopt_recompile_exclusives = +# Enable optimization to ignore invalid memory accesses (faster guest memory access) +# 0: Disabled, 1 (default): Enabled +cpuopt_ignore_memory_aborts = + # Enable unfuse FMA (improve performance on CPUs without FMA) # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. # 0: Disabled, 1 (default): Enabled diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 4ac72c2..31f28a5 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -7,6 +7,7 @@ #include "common/scm_rev.h" #include "common/settings.h" #include "core/core.h" +#include "core/hid/hid_core.h" #include "core/perf_stats.h" #include "input_common/drivers/keyboard.h" #include "input_common/drivers/mouse.h" @@ -26,6 +27,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Co } EmuWindow_SDL2::~EmuWindow_SDL2() { + system.HIDCore().UnloadInputDevices(); input_subsystem->Shutdown(); SDL_Quit(); } @@ -113,7 +115,7 @@ bool EmuWindow_SDL2::IsShown() const { void EmuWindow_SDL2::OnResize() { int width, height; - SDL_GetWindowSize(render_window, &width, &height); + SDL_GL_GetDrawableSize(render_window, &width, &height); UpdateCurrentFramebufferLayout(width, height); } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index 90bb0b4..25c23e2 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h @@ -89,3 +89,5 @@ protected: /// yuzu core instance Core::System& system; }; + +class DummyContext : public Core::Frontend::GraphicsContext {}; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index 9b660c1..ddcb048 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp @@ -104,6 +104,8 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste exit(1); } + strict_context_required = strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0; + SetWindowIcon(); if (fullscreen) { diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp new file mode 100644 index 0000000..259192f --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +#include + +#include "common/logging/log.h" +#include "common/scm_rev.h" +#include "video_core/renderer_null/renderer_null.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" + +#ifdef YUZU_USE_EXTERNAL_SDL2 +// Include this before SDL.h to prevent the external from including a dummy +#define USING_GENERATED_CONFIG_H +#include +#endif + +#include + +EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_, + Core::System& system_, bool fullscreen) + : EmuWindow_SDL2{input_subsystem_, system_} { + const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, + Common::g_scm_branch, Common::g_scm_desc); + render_window = + SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, + SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + + SetWindowIcon(); + + if (fullscreen) { + Fullscreen(); + ShowCursor(false); + } + + OnResize(); + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); + SDL_PumpEvents(); + LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Null)", Common::g_build_name, + Common::g_scm_branch, Common::g_scm_desc); +} + +EmuWindow_SDL2_Null::~EmuWindow_SDL2_Null() = default; + +std::unique_ptr EmuWindow_SDL2_Null::CreateSharedContext() const { + return std::make_unique(); +} diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h new file mode 100644 index 0000000..35aee28 --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/frontend/emu_window.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2.h" + +namespace Core { +class System; +} + +namespace InputCommon { +class InputSubsystem; +} + +class EmuWindow_SDL2_Null final : public EmuWindow_SDL2 { +public: + explicit EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_, + Core::System& system, bool fullscreen); + ~EmuWindow_SDL2_Null() override; + + std::unique_ptr CreateSharedContext() const override; +}; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index 65455c8..9ed47d4 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp @@ -12,12 +12,6 @@ #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" -#ifdef YUZU_USE_EXTERNAL_SDL2 -// Include this before SDL.h to prevent the external from including a dummy -#define USING_GENERATED_CONFIG_H -#include -#endif - #include #include @@ -51,11 +45,6 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste window_info.type = Core::Frontend::WindowSystemType::Windows; window_info.render_surface = reinterpret_cast(wm.info.win.window); break; -#else - case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS: - LOG_CRITICAL(Frontend, "Window manager subsystem Windows not compiled"); - std::exit(EXIT_FAILURE); - break; #endif #ifdef SDL_VIDEO_DRIVER_X11 case SDL_SYSWM_TYPE::SDL_SYSWM_X11: @@ -63,11 +52,6 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste window_info.display_connection = wm.info.x11.display; window_info.render_surface = reinterpret_cast(wm.info.x11.window); break; -#else - case SDL_SYSWM_TYPE::SDL_SYSWM_X11: - LOG_CRITICAL(Frontend, "Window manager subsystem X11 not compiled"); - std::exit(EXIT_FAILURE); - break; #endif #ifdef SDL_VIDEO_DRIVER_WAYLAND case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: @@ -75,15 +59,23 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste window_info.display_connection = wm.info.wl.display; window_info.render_surface = wm.info.wl.surface; break; -#else - case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: - LOG_CRITICAL(Frontend, "Window manager subsystem Wayland not compiled"); - std::exit(EXIT_FAILURE); +#endif +#ifdef SDL_VIDEO_DRIVER_COCOA + case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA: + window_info.type = Core::Frontend::WindowSystemType::Cocoa; + window_info.render_surface = SDL_Metal_CreateView(render_window); + break; +#endif +#ifdef SDL_VIDEO_DRIVER_ANDROID + case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID: + window_info.type = Core::Frontend::WindowSystemType::Android; + window_info.render_surface = reinterpret_cast(wm.info.android.window); break; #endif default: - LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); + LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem); std::exit(EXIT_FAILURE); + break; } OnResize(); diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h index e39ad75..9467d16 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h @@ -24,5 +24,3 @@ public: std::unique_ptr CreateSharedContext() const override; }; - -class DummyContext : public Core::Frontend::GraphicsContext {}; diff --git a/src/yuzu_cmd/precompiled_headers.h b/src/yuzu_cmd/precompiled_headers.h new file mode 100644 index 0000000..aabae73 --- /dev/null +++ b/src/yuzu_cmd/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 3a0f33c..a806497 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -34,6 +34,7 @@ #include "yuzu_cmd/config.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" #ifdef _WIN32 @@ -302,6 +303,8 @@ int main(int argc, char** argv) { } Core::System system{}; + system.Initialize(); + InputCommon::InputSubsystem input_subsystem{}; // Apply the command line arguments @@ -315,6 +318,9 @@ int main(int argc, char** argv) { case Settings::RendererBackend::Vulkan: emu_window = std::make_unique(&input_subsystem, system, fullscreen); break; + case Settings::RendererBackend::Null: + emu_window = std::make_unique(&input_subsystem, system, fullscreen); + break; } system.SetContentProvider(std::make_unique()); @@ -349,6 +355,7 @@ int main(int argc, char** argv) { "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", loader_id, error_id, static_cast(error_id)); } + break; } system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL"); @@ -392,7 +399,7 @@ int main(int argc, char** argv) { } system.DetachDebugger(); void(system.Pause()); - system.Shutdown(); + system.ShutdownMainProcess(); detached_tasks.WaitForAllTasks(); return 0;