From 3b645414c325893aa0a586e61b402fc8f398c4eb Mon Sep 17 00:00:00 2001 From: Ausdauersportler Date: Fri, 15 Nov 2024 22:19:22 +0100 Subject: [PATCH 1/6] NAVI23 EFI build --- .../datasets/video_bios_data.py | 2 ++ .../efi_builder/graphics_audio.py | 22 ++++++++++++++++++- .../sys_patch/patchsets/detect.py | 2 ++ .../sys_patch/patchsets/hardware/base.py | 1 + .../wx_gui/gui_settings.py | 5 ++++- 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/opencore_legacy_patcher/datasets/video_bios_data.py b/opencore_legacy_patcher/datasets/video_bios_data.py index 3ea6623a2b..d533089d1d 100644 --- a/opencore_legacy_patcher/datasets/video_bios_data.py +++ b/opencore_legacy_patcher/datasets/video_bios_data.py @@ -14,3 +14,5 @@ RX5500XT_64K = "55AA70E93703000000000000000000000000000000000000E8020000000049424D70D08E000000000000000000000004203736313239353532300000000000000000000000000000C00200000000000031302F31322F32302030353A3330000032000000E9250400E92F040000008C010A000000000000000B000E81871F50000000000001000100013088791E45A879140210BDC16F1CCFF9FF1F000000000000C4AFF83F000000000000000000407338AB467344730C0005003103D031375500F8801042F0DF03002C232020024802488CE600001EA8000F8008F03EA768747871782100210001002CC04C60192000300003800C0C00003610075280180C0E0A00000004F002000000080000000000084018107001000020000000040000001C040000C800000030000000000000084000000000000000000000001C32001C5E049000000000000000000000000000000000E42709401018006400000000C098070000407A01A000280000008010900118000008008081470100F0A11F11300000000020000000000000003131332D454D32344738472D533030004E4156493134005043495F45585052455353004744445236000D0A4E41564931342047616D696E6720585458204131204B394131325143412E534C42203230323020323333332F31343937342020202020202020202020202020202020202020202020202020200D0A000D0A200D0A0028432920313938382D323031382C20416476616E636564204D6963726F20446576696365732C20496E632E0041544F4D42494F53424B2D414D44205645523031372E3030322E3030302E3030302E303030303030004B394131325143412E534C420032313834313436200035333536363420200020202020202020200054554C5F4E41564931345F454D3234475F53414D5F4744365F38475F393557323032303130313254554C5C636F6E6669672E68000000902800020241544F4D00C0370461020D03B5016A040000000087170124E8021C92C292000074C80000504349520210407300001800000000037000021100000000414D442041544F4D42494F53009C4BB41D00000000000000000000000000000000000000000000000001000000000000000000000000000000000000001E0666506651665266536655665666570E1FA314008C0E0800B220E88F2C0BC07514A20200665F665E665D665B665A66596658071FCBE8A92DE87B2B32D28D3EC002895516E83C11E8FF10E85612E8AD02E835280BC0740CE83900E88353E8FB10E8D853E8A900B480E8752B8AC766C1E0108AE3B02066A3D08EE8AC02E80E11E8A22D665F665E665D665B665A66596658071FCB2E8B1E0800833E4904007504891E47041E2E8E1E3F049CFA66C706080165F000F0C70640006A04891E4200C706B4016A04891EB601C7067C00B25C891E7E00C7060C01C560891E0E01C706A804E456891EAA042E8E1E47048BC3A3E656A3F656A308579D1FC3504D49446A041647000000A000B000B800C000000000BB0000E8611066C1E810A3EE02BB0B00E854108D3EC00266894518C300E8D02C80FCA07505E83D00EB1180FC4F7505E8BD41EB07E81247EB02B401E8D72CCFE8AE2CE82000EB02B401E8C92CCBE8A02C80FC4F7505E89741EB07E8EC46EB02B401E8B12CCB1E0666506651665266536655665666570E1F3C0475292EA11A0066C1E0102EA118008D36B2018A3C80EF30B3028BEC895E0C89460466C1E810894600E928013C057518E8262AE8F9292EA108008BEC895610894600895E04E90C013C067532E8880F66D1E08BEC894614BB0200E8920F884618E87C0F6689460CBB0900E8820F33C0668946002E8B16EE02895610E9D6003C0B75240ADB7511B98000BB02008BEC895E0C894E14E9BD008AC7E8D000E834010F84A900E9AE003C17751DE831127406E88B0DE80C17E8EE09E8FA0E33C98BC88BEC894E14E98D003C1875450AFF7510E8B8118BEC89460CE83B0A894E10EB75E8FC117568BB001CE82C2B660BC0745D23C97459E8D011BE001CE8200F744EBE001CB040E86F0FE881118BEC89460CEB443C82751A80FB0575150AFF750AE8C5108BEC894614EB2DE892107418EB263C8E751280FF01740880FF02750880C102E8EB10EB108BECC6461902EB0F8BECC6461901EB0732E48BEC886619665F665E665D665B665A66596658071FC3518AC8B80100D3E059C3E84A2BC30C01C35051B0B6E643B033E642B005E642E4618AE00C03E6618AC4B9C800E81627E6615958C3001E560E1F803E0200807605C6060200808A0E0200C1E109FCC60621000033F632E4AC02E0E2FBF6D4FEC4882621005E1FC300570BC0750FE82A0C7425E82400F6C30474F3EB118BC8E8152523C17412E81100F6C304740A5F8D5C28E84C060BF6C35FC350E8DF0BE86724E875240AC974690AED74108ACDB502E81726B1028D9C4801E89429E87602F6C3047408E8F20CE8D82274458D5C28E869005683C608E8A4225E80FD02751151E804185980FB00754D51B508E8E805598ADD33C9E8F5160AC0753BE837028AEB8D5C2880CD02E83B00740380CD04E8C6058D9CA600E894290AC0741A5683C608E85A225E8ADDB98000E8C016E8D804740533C9E8A105E8FC0158C351B94900E8B42559C351E81A007514E8B9013C01750DB92000E82700750580C901EB0233C959C353E84629663D00FFFFFF750E83C304E83829663DFFFFFF0075005BC3505232D2E8272902D002D466C1E81002D002D483C304E2EC0AD25A58C3E8DF0A8D9C4C01E83901E88D01B513F6C308750CE8070C8AEA80FD137502B50366508D9CB8026633C08AC5E843286658A9C80E7455E86201F6C308740FE80F058D5C28E804017504E87200C3B502E82301E84601F6C30474218D5C28E84901E894037405B503E80B01E86804E8210180FD007548B502E80D01C38D9C6801B80055E85111B502E8FD00C3A90200742DB504E8E000E80301F6C3087403E8B0048D9C680156E83A268BF7E868045EE8D725E8D3008D5C28E8F725E81804C3B501E8B200B502E8BF00E8D000F6C30475108D9C6801B80055E8FC10B502E81700C3E8C600B5028D5C28E86E007502B501E80400E8E003C38D9CB402B102E8B727B502E88300C38D9C4C01E83800E88C00F6C30474238D5C28E88F00E8C5258D9C6801060E07B90700E86C2407E86300E8EE0A8D5C28E8B6258D5C28E85425B80200E8BEFEC3B96100E81224C35381C30501E8BF2724063C025BC3E810003D0104750A5383C318E8AA275BA801C35383C312E89F275BC38D9CB40232C9E83827C38D9CB402E88C27C38D9CB402B101E82627C38D9CB402B101E83427C366508D9C4801E86E278AD86658C3555751B90800BD0000BB08002BD9D1E38D584EE85327E8E301E2EE8D5C4BE84827B903008BD0BF520A8555067417668B058D9AF401E891268B45048D9AF801E89F2683C50883C708E2DF8D7C5EB9040033ED8BDFE812270BC07409E85D01E8970083C51C83C712E2E95532EDE86FFF8D5C28E84EFF3D0104751083C314E8E9262470C0E8048AE8E854FF5D8D9CA600E8D7260AC074468D9CA800E8CC263C02753B66C1E8103C0472338D9CA80032E403D88BFBB904008BDFE8AE260BC07415E8F900A98000750DE82E0083C51C555883F870730883C712490BC975DA595F5DC3200358023C000100000400033C0000088002E0013C002000518D1DE86B268D9A6801E8DC258D5D02E85E268D9A6A01E889008D5D05E851268D9A6E01E87C008D5D08E844268BC88ADC66C1C8108AD080E20F8AF0C0EE048AECC0ED068AFCC0EF0480E70380E40FC0E4040AD480E23F80E4C0C0EC020AF48BC38D9A7401E881258D9A72018BC1E878258D9A760133C08AC6E86D258D9A780133C08AC2E862258D5D0CE8E4258D9A7A01E80F008D5D0FE8D7258D9A7E01E8482559C38BC88AD466C1C8108AF080E60F8AE8C0ED048BC1E82F2583C3028BC2E82725C38D5D11E8A8258AE0BA0600C0E807740380CA808AC4C0E80324033C02750380CA403C0375128AC4D0E8A801740380E2FDA802740380E2FB8BC28D9A8001E8E624C33C01743D8BD00FB6C6243F043C8D9AF801E8D1240FB6C283C01FC1E0038D9AF401E8C1240FB6DEC1EB06C1E3022EF7A7B20B2EF7B7B00B8D9AF601E8A72483C508C310000A000400030005000400100009008D9CA600E814250AC0741F8D9CA80051B103E81800590BDB7410E8FE2432C0663D00030C0075030BDBC333DBC3665052E8E8243C02753980FC03723466C1E81032E43C04762A03C38BD083C3043BDA731FE8C7248AE080E4E0C0EC05241F38CC740832E4FEC003D8EBE30BDB5A6658C333DB5A6658C3518D9CA600E89D240AC0741B8D9CA800B112E818007410E88B2466C1E81080E4077504B001EB0232C00AC059C3665052E872243C70752B66C1E81032E483C30503C38BD03BDA731AE85A2438C8740D66C1E81032E483C00303D8EBE80BDB5A6658C333DB5A6658C35766518D9C68018BFB8BDFE83D000BC9740B668BD1E8970F83C71CEBEC66595FC3505351B90700538BDAE8102483F8005B740966C1E3108BDAE86020595B58C35153B90700E86D205B59C366505383C306E8E9235B8BC866C1E1105383C302E8DB238BC85B6658C38D9C480132C9E87023C357B92000E848205FC3E88301668BCA6652E8C406E8AA1C665A7410E83A06B300E89619663BCA7303668BD18D9CD003E899230BC075448D9C540266B8483F0000E8E822B5016681FA78690000722583C30466B878690000E8D122B5026681FAF0D20000720E83C30466B8F0D20000E8BA22B503B103E8CF01EB4566B9080000006633D2538D9C54028BD35BE83D230BC074226625FFFF000066D1E0538BDAE88A225B66C1CA10FEC266C1CA1083C20483C302E2D766C1CA108AEAB103E88801E83806A801743F8D9CAE03E8FE223C007434E8CF058AEBE85C0132EDE86301E8A2058D9CBB02E8E32233C98AC8E82801663BC2740E51E84301FEC5E8440159E2ECEB03B300C38D9CBB02E8BF22FEC88AE8E82E018D9C4003B90000E85222E8C202E8D200E80F016652E8AD05E8931B665A7504B300EB58E8F60080FD017707E8FA000AED7447E81E03E8281180FB00751DE8A705A8017407B10DE83F19EB30B501E89E05E8250FB500E89605EB21E8BF0080FD017607D0EDE8BB00EBBAE87600E8B300E8B600FECDE8B700EBAAB308C38D9C2901E835226633D28AD066B88C0A000066F7E2E83105E8171B753D66508D9C2801E816223C146658722E66508D9CD003E807220BC06658741FB908008D9CD0038BC1D1E003D8E8F1210BC07502E2ED6625FFFF000066D1E0660BC07503B8483F668BD0C38D9C2A01E8CF21240F8AE80AED7502B501C3B5008D9C3101E8BB21A8017402B501C35351E81F0032E48AC5C1E002598D9C540203D8E89E215BC3B102E81D00C3B102E80D00C3B101E81100C3B101E80100C3538D9CB802E820215BC3538D9CB802E82E215BC333C9E87F1E753C66BA37F1FFFF6633C0E8E30433C0E84703741CA9C80E74F6E83CF7F6C3047505F6C31874E9E8300050E82CF858EBDFE81C007508E831008BC1E81CF8C38D5D2866C1E3108D5C28B94900E87B1DC36650E8B5048BC86658C3665266BAFFFFFFFF33D0E88A04665AC3505352E8E0FF750933C0E8E3028BC8EB1083F902740BE82F0880FB01750383E1FD5A5B58C3566633C066BA00F0FFFFE8520766C1E610E8C4020BC07403E81C00E832075EC36652668BD0E8D2158D9C1C03E8AD208AC2E80820665AC35051E871025032C0E80004E8CC01E898008D9C4003E88D2038E8740A8AC5E8E41FB002E8E50358E8CA00E8A800E88C008D9C4C01E86E208BC88BD38D9C0003E863203BC17405B002E8C003E83BFC8D9C0003E84B158D9CD802E8A91FE88AFF66C1CE108BFE66C1CE10E89500E8D100E824038AE98D9CDC0232C9E8CC1FE80D036625FF0F0000595866BAFFFFFFFF33D0E8950633D2C35366508D9CD402E805208AC4E8601F66585BC35366508D9CD402E8F21F8AE0E84D1F66585BC3E87302E82E028ADDE893168AE98D9C3C0332C9E8771F0BDBC383EC088BECE85502E81002E819168D9C4C03E8BB1F8A4600E8151F83C408C38D9CD402E8AA1F32C0E8051FC3E80A008D9CD00232C9E83C1FC3B5008D1DE8901F668BD08D9C0203E8861F0BC0741C518BC88D9C0603E8781F66C1E0108BC159663BC27504B500EB02B502C3E82B028D9C4003B103E8FD1EE8D5F78D9C4003B102E8F11EE8950180FD007514E8A6FDE88E02E888FD668BD0E87702E88E02EB21E84A02E86C02E8730180FD02B504750B81FA74407605B50866D1EAE86E02E85F02E82B00C366525232ED83C102B80600F7E16633C98BC85A51668BC28ACF66F7E166C1E0035966F7F1668BC8665AC3B500E8EA01E8CC177503E811FD8D9C480332C9E8701EC353E85F0166C1E210E84D018D9CB802E8B91E8AE88D9CB402E8B01E8AC8E8BA148AEB5BC3575353E8D6FFE82B015BE81F15746680FD00752953E8DBFC668BD0E808F78ACD51E8E7FC8AFD59E871FF5BE8791E6625FFFF0000663BC8733932DBEB3880FD027516E86F01E8621774288D1FE8581E3D7440761E32DBEB1D80FD03751553E8D300B303E82F145BE83D1E3BC2760432DBEB0380CB015B5FC3538BF0E8141923F074098BC6E8B3178BF30BDB5BC351E815FDE80F0059C35150E8F7188BC858E8020059C35753E828190BC074098B1D83C7023BC375F78B1D83FBFF740B83C70285D974F28BC3EB0233C00BC05B5FC333C0E8C3FF7413E8A0FF8BDEB9FB00E8111A83C608E85C18EBE8BBF016B85AA5E82E1DC35366508D9C4003E8AB1D8AE866585BC38D9C6903E89E1DC38D9C6403E8961D0BC0C35366508D9CD402E8891D8AC866585BC35366508D9CD502E8791D8AC866585BC3538D5C14E86C1D8BD05BC35366508D5C16E85F1D8BD066585BC35366508D9C5003E84F1D668BD066585BC35366508D9C5403E83E1D668BD066585BC35366508D5C10E82E1D8BC866585BC36650538D9C4103E81E1D5B8AD86658C3538D5C08E8111D5BC353508D9C4C03E8061D8AC8585BC353508D9C4D03E8F81C8AC8585BC35366508D5C0CE8EA1C8BD066585BC35366508D9C0003E8DA1C6633D28BD066585BC38D5C04E8CB1CC3518D5C0432C9E8651C59C38D9C5003E8B81C668BC2E8121CC3538D9C4003B101E84B1C5BC38D9C5403668BC2E8FB1BC353518AE8E8C2FF24FD0AE8E8C2FF595BC366508D9CD802E8801C668BD06658C353665051668BC8BB0000E8E11523C20BC1E8E3155966585BC3BB0000E8CF156625CA0E0000C3E85918E8E817E8C6177506E84317E8CA17C3B001C355B301E82D1A5DC3665366BB04000000E8050033C0665BC35566C1E310BB0000E8101A5DC3E81819BB0A00E88E15C3BB0A00E87E15668BC8E80519BB0A00662BC1E87815C3E829000BC97422E814FBE824076651668BC8BB0700E85615662500FE0000660BC1E8531566590C01C332C0C3E86B020BC97506E8C3FAE8710251E8BCFA66C1E61033C0E89AFD7409E8090375F633C8EBF25823C866C1EE10C3A8107563E8E30666C1E61033C0E880FD7435E85DFD50518BC8E8F90185C15958740D50E8AAFE24033C035875E0EB0C50E89DFEA8015874D5E86AFB50E8CD0758B500E892FEEBC6E8CB01E825FDE83401B100E8410766C1EE10E88706A8207542A8407503E80F0EE86E060AC0750732C9B501E8B20EE89D01E8F7FCE830FEE816147514E862020ADB740DE818FBE87C07B500E842FEEB08E85907B501E838FEE8E600C3E8E615C3E81EFDE89F0633C9E89F016633D26633C0E86CFEE85200B1FFE8840033C0E8C8FC740AE8A5FC50E8C50758EBF132C9E8C401E8B0150BC0C3E8E6FCE867066633C066BA00F0FFFFE8330151B101E88C015933C0E893FC740AE870FC50E85B0758EBF1E8A70DE87F16C3B80200E85CFC742EB107E8CA11B80200E86CF0F6C3047512E84618833D00750AE8591583E0FDE85C15C366B802000000E8E4FDE84EF2C3E8DD13742338E176048ACCEB0638C173028AC8BB0200E8B5138AE1E8B913B104E87D110C01C30C01C351E8B313740C8ACCBB0200E897138AC48AE159C3E8F105C3E8FC05C3E826177518E88100E8DBFB80F901750E51E85306B501E832FD59E8DFFFC3E8D7FF51E8B006B500E821FD59C3B80200E8B4FB7403E82411C3E8B0043BC17C3A8BC1E8F004E8BA0E8BC851E82005E8E40459E8AD0E3BC872028BC18D5C04E82D1932C9B50FE8F80CE8EC15FEC138C172F28D5C04E89F190BC0C333C0C3BB001833C08AC2C1E00203D8C353BB0300E8F9126625FF0F00005BC35366506651668BC8BB0300E8E3126623C2660BC1E8E312665966585BC36650BB0500E8CC1225FF0F8BC86658C3E8381423C86650BB0500E8B7122500F00BC1E8B8126658C3665053BB0600E8A312A8107509BBF016E825193D5AA55B6658C3665053BB0600E8891280F101C0E10424EF0AC1E885125B6658C3536650BBF816E8FB188AC1E8561866585BC353BBF816E8EB1883E0015BC3536650BB0700E85112F6C4016658B0067402B0085BC353BB0700E83D1280E4FE80FD06740380CC01E838125BC36650BB0600E825122440C0E8068AD86658C3C3506651E871FA83EC048BEC6633C06689460033D28DBC68018D1DE889180BC074228BDFB000E8120174148BDFE885F4668B4600663BC872068BD766894E0083C71CEBD50BD2740A8D9C4C01E83CF4E988006633C0668946008DBCF40133D28D1DE843180BC074586650E8A7F03C01665874053D400673436652668BD08D5D04E82418E85D008BCA665A742F568D9C4C018BF1E814F45EB000E89F00741D8D1DE804188BD98B470666C1E0108B4702663B460072068BD16689460083C708EB9F0BD27416568D9C4C018BF2E8DCF35E538D9C620133C0E846175B83C4040BD2665958C3575351E805133B5502751866C1CA108BDA66C1CA103B5D0675090AC0740E3A451B740983C71CE2DE33D2EB028BD70BD2595B5FC3516652E8D1123A651A75090AC0740E3A451B740983C71CE2ED33FFEB000BFF665A59C3C366516652535366C1CE108BDE66C1CE10E859175B668BD0E860F33BCA720A663BCA7205E89FF8750232C05B665A6659C35657E8A7FF8BF7E835F35F5EC36650536651525756518BDEB90800E86813598D5C088BC166C1E0108AC2E86F16F6C20375248BC1247FBFF71A803DFF0F840F013A05750D668B450166B900050200E9960083C705EBE3F6C20174258BC1E870010F84EB0033C98ACC8AD00AC075128D5C086625FFFF000066C1E008E81E16EBB28AE18ACA80E10CC0E90280E2F0C0EA048AEA80FC70722A80FC75732580EC708AC432E450E830028BD85838D8720533C0E99C00E82802E883160BC00F849000EB1B32C0E8E6FE0F8485008D5C168B05E8E2158B450666C1E0108B450250E8480B66C1E1108BC8588D1CE8B01566508D5C04668BC1E8A515665866C1E8106681E1FFFF000066F7E166508D5C06E82D168BC8665866D3E066C1E810408D5C14E89315BF571B83F9017411BF5F1B81F901017408BF671B83F90275158D5C0C668B05E859158D5C10668B4504E84F150BDB5E5F5A66595B6658C3004001900101400190010280029001038002900104400190010540019001068002900107D0025E010D400190010E800290010F80025E011080025E01118002E001128002E00113800290012320049001322004B002332004C002628002E001FF050A050505000000050B060505000000081008080800000056E8CF1280E4013B04740983C604E2F733C0EB058B44020BC05EC3575350E8B21283E90AE8E00083F8057603B80500D1E003C8FBA583C602E2FAB8FFFFAB585B5FC36653516652E84100E8770066C1E810660FB7D866B80000000366D3E86633D266F7F3E8F109665A59665BC3536650BB0700E86A0E0AC066585BC3536650BB0700E85B0EA80166585BC35366508D5C06E8D8148AC866585BC351B90800E82A1159C3538D5C08E8C21424035BC38D5C09E8B814C35153B1018AE88D5C08E84F145B59C3538D1CE8A2145BC3538D5C04E899145BC3538D5C16E890145BC353665233C0E8C2FC665A751BE834007416E815004083F805730DE8D11348E80F00668BC2E8C7135BC3BB001EE85F14C3BB081EC1E00303D8C3BB001EB93200E89610C351E8E2FF0BC074128BC8BB081EE83B14663BC2740783C308E2F30BDB59C3C300C300BB20DFE8E333E88DF6B501E8E507C3BB00DFE8D433E87EF6B500E8D607C380F900750AE84A108AC8B502E81D090BF6742EE862F6B50151E8C00759E8C3075633F6B500E82D075EB501E8CEF6E8FB08E8C409E841F68D9C0003E8AC07E8F50766C1CE10B501E80B07E800FFA840750732C0B501E87E0866C1CE10C3E818F6B501E84B07B500E87907E8DC07B10FE8690AE82600C3E85200E80CF6B500E82F07B500E85D07E881F6E8C60F38C1740A80F90B7405B503E89208C3B110E83B0AE8B3F580FD007406B101E8A40AC3E8A60580FD017507B502E8D505EB03E8F301B10DE8160AB103E85400C3E8D5F50BD27405B100E8420AB102E84200E85BF6A8017412E870F580FD00750AB10CE8EB09B002E89104B100E8570AB10DE81F00C3B107E84C0AE89BF50BD2750CE809F6E8EB0B7509E8F20AC3B107E8FC09C3E8F7F5E8D90B752451E8270A5980F90C751A33D25683C608E8020C5E0AC0750CB90A00E8620F4283FA1E75E8C380FB010F84C500E8A50566C1E1108D9C3501E8AB12A801740551E82B0459E868F5B004E89C0566BA50014050E87605E8B80A757E66BA5001504F8D9C2D01E87F1224063C027502B602B005E87405E854058D5C2803D8E8910A75570AFF7509B00AE85E0532DBEB4A5133C98ACFE8350503C8E83A0559B007E8470566BA5001103033DBE8640A740F518AEB8D9C4401B102E8D01159EB61E80B0566C1C9102BC166C1C1103D80000F8276FFB008E81205EB43518AEB8D9C4401B102E8A6115966BA5001103033DBE8200AEB2C8D5C2803D9538AE9B132515FE8AEF48AE9B1A0BA0080E8DA0980FF015B740AB1A2E8CF0980FF017503B000C3B001C351B10CE8D2FE59B001E8BB045683C608E8E20A5E0AC07504B310EB3FB002E8A604E838007411B003E89C04B001E810037529E82700752466BA0C0090438D9C3001E8AB0966BA200090418D9C3401E89E09E8BD03B009E86E0433DBC3E82FF466BA000090478D9C2801E88309C3B10CE866FEB100E89408B101E88F088D9CAC0366B800000000E89C10B001E8B202740AB504E8CB03B308E93F01B108E8F607E8D202B109E8EE07E8C5018D9CB8036633C0E87110E86001B99001E8980DB001E86302B502E88D03E87401E89F03E8E001B503E87F0333C9E80001F6C5017534B502E87003E82001F6C5047408B501E86F03E9CC008AFD80E703E8E80080E30338FB750DE84C017508B502E85303E9B000E86701FEC1E885F338D975BB8D9CAC03E8A2103C037405E8ED00EB9B8D9CB0036633C0E8EF0F8D9C2A01E88810A840740CB113E84F07B003E8DA01EB0AB10AE84307B002E8CE01B504E8F802E8DF00E80A03E84B01B505E8EA0233C9E86B0080FD077505E88600752E51B504E8D50259F6C5017507B503E8D602EB34E878008AD5E8E600E8700038EA750CE8B4007507B502E8BB02EB19FEC1E8F1F238D975BC8D9CAC03E80E103C047505E85900EB9CB000E86001B10BE8CB068D9CAD03E8F40FB3000AC07402B308C38D9CC003E80F008AE8C3538D9CC403E804005B8AD8C351E8D10FC0E102D3E8240F59C38D9CC203E8C10FA801C38D9CB803E8730FC38D9CB803E8530FC333C933D2E8E9FF38D576028AD5FEC1E874F238D972EE33C98AEA8D9C5C03E8310FB10BE8CE06C3E84DF28D9CB803E87D0F66BA03018083E89E07C351538D9CB003E8260FFEC5E8090F80FD067207B502E8F60133C95B59C36633C08D9CB003E8AC0EC332EDE865FF80E30CC0EB02B7032AFBC0E3038AEB80FB18720380CD20E84BFF80E30338FB72028ADF80FB03750380CD040AEBE860FFC3E8DBF18D9CC00366BA02029045E82F07C3E8CAF166BA000190428D9CB403E81E07E8BAF166BA030190458D9CB803E80E07E8EEF1E8D407753BE8A2F166BA0A0190408D9CBF03E8F6068D9C2801E8C50E3C147220E887F166BA1000904F8D9CD003E8DB06E877F166BA150190408D9CCC03E8CB06C3E866F166BA02018050E8BE06C3E848F18BC26633D2B98C0AF7F1C3E84BF166BA00068050E8A306C3E83EF166BA11018050B004E89406C3E82FF166BA000690408D9CC803E883068D9CC803E8520EC3E8BBFF3C06740E3C0A740A3C1474063C1E740233C0E811F18AE380CC8050E8F9F05866BA00018061E8500633C9E884F00BC07402B5108D9CBC03E8B80D8AC5E8D8F066BA07018050E83006E810F1E8F60675498D9C4803E8F70D0AC0740EE8B9F066BA0A018050B001E80F068D9C2801E8DE0D3C1472258D9CD003E8D30D0BC0741AE83CEC32C98D9CCC03E8670D8AC5E887F066BA15018050E8DF05C38D9CB403B90E00E8F409E8A3FEE88FFEE838FF3C01751CE8D2FD741733C9E8A8FD80FD07750DFEC1E861F038D975EFB501EB02B500E80100C38D9CAC03B102E8160DC3518D9CAC0332C9E80B0D59C3518D9CAC03B101E8FF0C59C38D9C3201E8520D24077515B964008D9CAC03E8440D3C027403B99001E8CA09C332E4B10433D2F6E18BC8E8C709C3518D5C14E8240D66C1E010E8A40159C3538D9C4401E8130D5BC35366508D9C4401E8070D8BC1E8620C66585BC353518D9C44018AE8B103E8950C595BC333C9E82D0938C17305B500E88C00B500E88000FEC1E81A0938C172E6B1148AE9E8120938C17405B503E8E301FEC1E8FB08041438C176E7C383EC0C8BEC0BF6741EE802F866C1C810668946008D5C04E89C0C668946048D5C08E8920C88460A894E088CD066C1E0108BC4BB2B00E8D80783C40CC383EC048BECB002884601884E008CD066C1E0108BC4BB2500E8B90783C404C3BB2300E80800C3BB1700E80100C383EC048BEC894E008CD066C1E0108BC4E8940783C404C3BB2200E8E3FFC3BB2700E8DCFFC3BB2C00E8D5FFC3E81E0C66A90000FFFF744083EC188BEC5551B9040083C302E8060C6689460083C50483C304E2F183C304E8F40B66C1C81066894600595D884E148CD066C1E0108BC4BB3100E8330783C418C3E83FEE8D9CD002E8CB0B8AE8BB2100E876FFC38D9C3C03E8BB0B8AF8E801EE8ADD538D9C4203E8AC0B8AE85BE813EEE8B0EEE896047502B30183EC048BEC884E00887E01885E02886E038CD066C1E0108BC4BB2A00E8D70683C404C383EC108BEC663DF0FFFF03760666B8F0FFFF03668BC86633C066894604668946006689460866B8000000D0660BC16689460C8CD066C1E0108BC4BB3500E8930683C410C383EC088BEC894E0066895602895E068CD066C1E0108BC4BB2D00E8720683C408C33DC07F7604B8C07FC383C03F24C0C351E8010B6633D2B90A00F7F18BC8E8F40A03C16625FFFF0000593D10277303B81027C332D2C380FD027525E84EEE66BB00080000E89A00E842EE66BB03080000E88E006633D266BB01080000E88200C380FD0375106633C08AC16633D266BBFF000000EB365133D2E8E5EC8AD566C1E210E81EED8AF28AD1668BC2660D00000080E859ED80FB047606660D00000008E88FEDE8DDEC6633DB8AD95983EC108BEC6689460466895E08668BC26633D266BB6400000066F7E366894600BB0C008CD066C1E0108BC4E896058BECB30183C410C383EC108BEC6689560066895E04BB2E008CD066C1E0108BC4E87305B30183C410C3C353516650B9687480FB03740BB9A08C80FB007503B978696633D28BD16658595BC36652B30066F7C20000FFFF751A80FD13741580FD147410B30180FD0E7409B30380F9037402B302665AC3B51180FA23740780FA227402B500C3B50280FA23740780FA227402B301C332C980FD007508C746000B01B101C35033C0E87301041489460058B101C3C38BDAB72280FE217402B721C30C01C3E8780966C1E8100BC0741C5383C306E869095B3DE001720F5383C318E85C095BA88075030C01C332C0C3B50CC3B502C350E82601B1030AC07404B10802C858C380FA227505C746007869C3E8EF0080FDFF743583EC0C8BEC884E01886E0080F90F7505E82400EB0F80F910750A8D9C4803E807098846028CD066C1E0108BC4BB0400E8500483C40CC3C36653E8A4EB668BC26633D266BB8C0A000066F7F3665B8AE8886E098D9C4003E8CF0888460288660366C1E810884608E866EB66895604C383EC208BEC80F904750A884E0132C0884600EB56E830EBE87F00884600884E01E8A4EB88560A80F901741880F90B753A8D9C5C03E88308884602E824EB66895604EB27E83DEB886E08E815EB66895604E83FEB885E03E8ACEA886E0251E81C008ACDB001D2E0598846098CD066C1E0108BC4BB4C00E89C0383C420C38D9C3C03E8370832ED3C03740C3C0972093C0E77052C088AE8C3B5FFC35232C080EE2180FA1E7410B00280FA207409B00480FA217402B00602C65AC3C383EC088BEC897E00895E02895604894E068CD066C1E0108BC4BB3600E83C038BEC8B5E028A7E0183C408C35633F666F7C200008000750A5033C08AC68BF032F65853BB041FE81907668BC2BB001FE810075B66F7C200001000750233DB8BD366C1E210BA001F83EC088BEC66895600884E048AC5FEC8884607C6460500C64606008CD066C1E0108BC4BB4E00E8CC028BEC8A5E058A7E0683C4080ADB743251B9900180FB20740580FB807513B90A008BC60AC0740AB990013C017403B9E803E8CF03598BC6FEC48BF080FC0676980ADB5EC38D5C04E829078BD0B502E814007411B50180FA13750AE8610080FA137502B502C380FA14C3E8F9FF740380FA0EC380FA05C380FA02740380FA04C380FA01740380FA02C380FA027503B201C380FA047503B203C3C3B508E8D8FF7402B504C38D5C09E8CA068AC80AC9750332C0C3B502E8DA028AC52402D0E8C38D5C08E8AF068AD0B60183EC108BEC8956028CD066C1E0108BC4BB4F00E8EF018BEC8A560483C410C35383C34CE853065BC35383C34CE8A5055BC356E896018B4438837D20005EC352500FBCD832FF80FB017505BB0024EB0CFECBB80004F7E30500208BD8585AC357E85600740A8B5D02E808000BDB5FC333DB5FC352510BDB741B56E8360103DE5E33C98A4F01803FFF74083A07740603D9EBF033DB0BDB595AC35651E8150132C98D7C083B5D04740C83C710FEC13A4C0672F133FF0BFF595EC35651E8F50032C98D7C083B1D740C83C710FEC13A4C0672F233FF0BFF595EC35651E8D60032C98D7C083B450C740C83C710FEC13A4C0672F133FF0BFF595EC35750E8DBFF8AC38B5D02E86EFF585FC35332C9B301E8E8FF74038A4F020AC95BC35332EDB302E8D7FF74038A6F020AED5BC350578BDAE868FF750433DBEB0DB0148B5D08E834FF74F28B5F025F58C366505351578D1C6625FFFF0000E8B5048BD8E83B0023C37431E875FF0BFF742A6650668B45048D5C0CE899046633C08B058D5C04E88E046658E885FFE893FF8D5C086633C08BC1E87B045F595B6658C356E818008B44045EC356E80F008944045EC3C3BFE0028B3D8B7D0CC3BEE0028B348B7430C31E0E1FBFE0028B3D8B7D0E83C7041FC3BEE0028B348B7410C3BFE0028B3D8B7D2683C704C3BEE0028B348B741AC3BEE0028B348B743AC3BFE0028B3D8B7D0A83C704B90700C3E80100CB83EC068BEC8946006633C0C646040089460206528AD38BC5E8CD165A0783C406C3E880FF66F7451001000000C350E873FFB8010009451058C3E868FF884510C3665383EC488BEC66895E0066894E048CD066C1E0108BC4BB0000E8A3FF83C448665BE86A01E8A40183EC1C8BECC64601078CD066C1E0108BC4BB0D00E881FF83C41CE80E00BB0400E88FFD66C1E810E8A7FFC383EC1C8BECC64601088CD066C1E0108BC4BB0D00E856FF83C41CC3E8F3FE668B5D08668B4D0CC356E826FF668B44085EC383EC048BEC894E008CD066C1E0108BC4BB3800E826FF8BEC8A6E0283C404C3C3B006C3B001C3B005C3B008C35366506633C0E80D0383C304E2F866585BC332C0C3FCE89D0366C1CB10E8F60266C1CB106681C304000400E2E9C3FC66ADE8E20283C304E2F6C3FCE8780366AB83C304E2F6C36650B86400E810006658C36650B850C3D1E1E803006658C352F7E18BCAE8690166538BD966C1E3108BD866B804B10300B20066EFB2046633C066EF66B808B10300B20066EFB20466ED33C96603D8730A66ED66A90000008075F666ED663BC3730C66C1E8103BC172048BC8EBED665B5AC3BB42ECE8C7026633D266BB6400000066F7F3C3665053BB0700E857FC2500023500020BC05B6658C36651B301E8C400E802FE6633C98B4C0866C1E00A0BC97503B92000662BC1E8EBFD66894404894C086659C3561E0E1FE8DAFD668B440466C1E00A1F5EC3C3BEE0028B348B741E83C604B91C00C356E8A6FD8A6C335EC356E89DFD5381C30901E875028AE85B5381C30001E86A025B8AC5668944345EC356E87DFD668B44345381C30801B1018AE8E8F1015B5381C30001B1018AECE8E4015B5381C3020166C1E810E8AB015B5EC356E84CFD8BFE83C7045EC356E841FD8A442480FD007405886C330C028844245EC332C0C3665383EC048BEC8CD066C1E0108BC466895E00BB0600E84DFD8BEC668B460083C404665BC366508CC83D00C0750CBAC303EC8AF00AF67402EB0E532E8B1E1400B220E8D6008AF45B32D266ED6658C32E8B1E1400C30BC9751A4B7416433BDA77098BC88BC22BD2F7F391F7F38BDA8BD12BC9C33BCA721A75103BD8770C2BC38BD82BC92BD2B80100C32BC92BDB9387CAC355562BF68BEE03DB13C97211453BCA72F577043BD876EFF813F64D7820D1D9D1DB2BC31BD1F572F003F64D780CD1E9D1DB03C313D173F1EBDF03C313D18BD88BCA8BC633D25E5DC39350920BC07402F7E2910BC07404F7E303C858F7E303D1C3526650B4808AC766C1E0108AE38AC224FCBAF80C66EF66585AC352BAFC0C66ED5AC352BAFC0C66EF5AC3E831FF9CFAE8CEFFE8E5FF9DC3E824FF9CFAE8C1FFE8E0FF9DC35266536650E806006658665B5AC366C1E31066C1EB0EE8D7FEE89301C3665066C1E31066C1EB10E801FE6603D86681CB000000806658C3526653E80400665B5AC36650E8AAFEE8D4FFE863016658C35251665366508ACB80E3FC80E103C0E103E88DFEE8B7FFE82D0166D3C8585066D3C0E83B016658665B595AC3665051C0E103E8530066D3C88AC566D3C0E8A8FF596658C3665051C0E103E83B0066D3C8598AE86658C3E85F00C3E844FE66C1E31066C1EB0EE8DF00C3E835FE66C1E302E8D400C3526653E80400665B5AC3E820FEE84AFFE8C000C35166528AEB8ACB80E103C0E10380E3FCE8D9FF80F9007414668BD083C304E8CBFF83EB046692660FADD08ADD665A59C3526653E89CFF665B5AC3526653E8A1FF665B5AC36650555266508BEC8B560C895608E8C4FDB20066ED6689460A66585A5DC36650555266508BEC8B560C895608E8A6FD2EA12D03B218EFB20066ED6689460A66585A5DC3665055528BECE889FDB200668B460A66EF8B560889560C668B4604668946085A5D66586658C3665052E866FDB21866ED2EA32D035A6658C36681FBFF00000077048AD3EB09668BC3B20066EFB20466EDC36681FBFF00000077048AD3EB0A6693B20066EF6693B20466EFC3C3C3558BEC53518BF0268B5C14268B4C16E828008D66FC595B5DC353518BF0268B5C2433C9E81400595BC3558BEC53518BF0268B5C08268B4C0AEBD5525657C80600008BF0894EFE8CD08EC0268B7C268A450132E4B920002BC8B8FFFF8BD0E306D1EAD1D8E2FA8A4D0332ED8BFAE306D1E0D1D7E2FAF7D0F7D72621441C26217C1E8CD08EC0268B7C268A4D0132EDB820002BC18BC866C746FAFFFFFFFFE308D16EFCD15EFAE2F88A4D0232ED8BC38B56FEE306D1EAD1D8E2FA2346FA2356FC8A4D0332EDE306D1E0D1D2E2FA2609441C2609541EC95F5E5AC3558BEC53515257508BD8268B7F268A450132E4B920002BC8B8FFFF8BD0E306D1EAD1D8E2FA8A4D02884EF6C646F7008BFA8B4EF6E306D1E0D1D7E2FA2609471C26097F1E8D66F85F5AE9F1FE535152578BD88CD08EC0268B77268A4C0132EDB820002BC18BC8BEFFFF8BD6E306D1EAD1DEE2FA268B7F268A4D0232ED8BC6E306D1E0D1D2E2FAF7D0F7D22621471C2621571E5FE9CC0553568BD88CD28EC2268B7726803C007473803C0175588A440132E426837F1E00754C263B471C75468A046BF0038A840490260147268CD08EC0268B7726803C0974178A0432E46BF0038BC3FF940290268B77268A0432E4EBD18B44012629472626FF4726268B471C268B771EEB1A268B77268A0432E46BF0038A84049026014726E980FF33C033F68BD65E5BC3535152568BD88D7735268B54022689571426C7471600008BF0268B472033D226014714261154168CD18EC126F6472F060F850A0526807F34007522268B7716260B7714751026D1670826D1570A26D1670826D1570A8BC3E87911E9E104268A473432E4058000992689471C2689571E8BC3E8FFFEE9C70453568BD88D7735268A440232E4C1E002268B37268B3403F0268B4708268B5F0A26890426895C025E5BC35352568BD88CD08D77358EC0268A540280FA407321268B770232F6C1E202268B740403F2268B5708268B470A26891426894402E97F0080FA417512268B4708268B570A2689471026895712EB6880FA407512268B4708268B570A2689470C2689570EEB5180FA42750A268B470826894722EB4280FA43750A268A470826884733EB3380FA467512268B5708268B470A268957182689471AEB1C80FA47750A268B470826894724EB0D80FA487508268B4708268947205E5A5BC353568BD88D7735268A440232E42689471426C7471600005E5BC353568BD8268B7702268B74028A0432E42689471426C747160000268B5F0226FF47025E5BC353568BD8268B7702268B74028B342689771426C747160000268B5F0226834702025E5BC353568BD8268B7702268B74028B048B74022689471426897716268B5F0226834702045E5BC35352568BD8E870FF8BF3268B4718268B571AD1EAD1D8D1EAD1D826014714261154168BC3E82B10E94AFF538BD8268B5F0226FF47025BC3538BD8268B5F0226834702025BC353568BD8E86BFF8BF3268B572033C026015714261144168CD28EC2268B472E32C080E4063D0002750732E433D25E5BC3268B472E32C080E4063D000474EB26807F340075088BC3E82F0F5E5BC3268A473432E42689471C26C7471E00008BC3E8EAFC5E5BC3538BD8E8E2FE268B4714C1E002268B1F268B1F03D8268B07268B57025BC3E236D736ED363C37F3360B372537303736375351568BD8E8B0FE8CD28EC226837F1600752226837F1440731B268B7702268B5F14C1E302268B440403D8268B07268B5702E97B05268B7714268B471683C6C083D0FF757183FE08776C03F62EFFA47436268B4710268B5712E95405268B470C268B570EE94905268B4722EB4B268A4F3332EDBB010033D2E306D1E3D1D2E2FA8BC3E92B05268A4F3332EDB8010033D2E306D1E0D1D2E2FAF7D0F7D2E91105268B4718268B571AE90605268B4724EB08268B4720EB0233C033D2E9F30453568BD8E8F4FD8BF3268B4718268B571AD1EAD1D8D1EAD1D826014714261154168BC3E8160E5E5BC333C033D2C353568BD8E8EBFD268B7714260377228B048B54025E5BC3538BD826C6472B04E8ABFD268B4714268B57165BC3538BD826C6472B01E8BBFDEBE9538BD826C6472B00E8D2FDEBDC538BD883C335268A5F01C0EB0380E30732FF03DBFF97928F5BC35352568BD8268A472C32E48BF003F08BC3FF94AE8F268947082689570A268A472D32E48BF003F08BC3FF94C68F2689470426895706E917FD5351568BD8268A4F2B32ED8BF18A8CF68FE30A26D16F0626D15F04E2F6268A4F2B32ED8BF1C1E6028B8CD68F8BB4D88F26214F0426217706268A4F2A32ED8BF18A8CFE8FE30A26D1670426D15706E2F6268B4F08268B770A26894F1426897716E9CA0353515256578BD88CD08EC0268A472B32E48BF0C1E602268A472A8BF88A8DFE8F32ED8B84D68F8B94D88FE306D1E0D1D2E2FAF7D0F7D22621471426215716268A472A32E48BF08A8CFE8F32EDE30A26D16F0A26D15F08E2F6268A472B8BF0C1E6028B84D68F8B94D88F262147082621570A268A472A32E48BF08A8CFE8F32EDE30A26D1670826D1570AE2F6268B4714268B5716260947082609570A5F5E5A595BC35351568BD8268A4F2B32ED8BF18A8CF68FE30A26D16F0626D15F04E2F6268A4F2B32ED8BF1C1E6028B8CD68F8BB4D88F26214F0426217706268A4F2A32ED8BF18A8CFE8FE30A26D16F0A26D15F08E2F6268A4F2B32ED8BF1C1E6028B8CD68F8BB4D88F26214F082621770AE9BB025152578BD88CD08EC026807F2B00741A268A472C32E48BF003F08BC3FF94AE8F268947082689570AEB10268A472C32E48BF003F08BC3FF94BA8F268A472D32E48BF003F08BC3FF94C68F26894704268957068CD28EC226807F2B00744A268A472B32E48BF0C1E602268A472A8BF88A8DFE8F32ED8B84D68F8B94D88FE306D1E0D1D2E2FAF7D0F7D2262147082621570A8BC3E8F9FD268B4704268B5706260947082609570AEB10268B5704268B4706268957082689470A268A472C32E48BF003F08BC3FF94A28F5F5A59C353515256578BD8268A472A32E48BF08A8CFE8F32EDE30A26D1670426D15706E2F68CD08EC0268A472B32E48BF0C1E602268A472A8BF88A8DFE8F32ED8B84D68F8B94D88FE306D1E0D1D2E2FAF7D0F7D22609470426095706268B4704268B5706262147082621570AE956FE558BEC5152508BD8268A472C32E48BF003F08BC3FF94AE8F268947082689570A268A472B8846FA8BC3E8DAFC2689470426895706268A4F2D32ED8BF103F18BC3FF94C68F26894714268957168BC3E844FF8A46FA32E48BF08A8CF68FE30A26D16F1626D15F14E2F6268A572B32F68BF2C1E6028B84D68F8B94D88F2621471426215716268A472A32E48BF08A8CFE8F32EDE30A26D1671426D15716E2F6268B4714268B5716260947082609570A268A472C32E48BF003F08BC3FF94A28F8D66FC5A595DC3518BD8E856FC268A4F2B32ED8BF18A8CF68FE30A26D16F0626D15F04E2F68BC3E8AEFE268A4F2C32ED8BF103F18BC3FF94A28F59C38BD8E822FC8BC3E855FC268B4704268B7706260947082609770A268A472C32E48BF003F08BC3FF94A28FC353568BD8E8F5FB8BC3E828FC268B4704268B7706263147082631770A268A472C32E48BF003F08BC3FF94A28F5E5BC35351568BD8E8C5FB8BC3E8F8FB268B4F04E30A26D1670826D1570AE2F68BC3E846FC268A4F2C32ED8BF103F18BC3FF94A28F5E595BC35351568BD8E88FFB8BC3E8C2FB268B4F04E30A26D16F0A26D15F08E2F6EBC8528BD8E872FB8BC3E8A5FB8BF3268B4704268B5706260147082611540A8BC3E8F1FB268A472C32E48BF003F08BC3FF94A28F5AC3528BD8E83EFB8BC3E871FB8BF3268B4704268B5706262947082619540AEBCA51528BF0E81EFB8BC6E855FC268B4408268B540A268B5C04268B4C06E8DDF22689440C2689540E5A59C3528BD0E8F5FA8BC2E82CFC8BC2E85C085AC35152578BF0E8E1FA8BC6E818FC268B4408268B540A268B5C04268B4C06E81CF22689440C2689540E8CD78EC7268B4408268B540A268B5C04268B4C06E8FDF126895C1026894C12E910FD528BD0E899FA8BC2E8D0FB8BC2E8D3075AC351528BD8E886FA8BC3E8BDFB8CD18EC1268B4708268B570A263B5706750E263B4704750826C64730015A59C38BD0268B470A263B47067208750A263B5704730433C0EB03B80200268847305A59C35152578BD8268A472C32E48BF003F08BC3FF94AE8F268947082689570A8CD08EC0268A472B32E48BF0C1E6028BF88A8DF68F32ED8B84D68F8B94D88FE306D1E0D1D2E2FAF7D0F7D2262147082621570AE94DFC558BEC51525783EC068BD8268A472B32E48BF0C1E6028BF88A8DF68F32ED8B84D68F8946F48B84D88F8946F6E308D166F4D156F6E2F88B7EF48B46F68946F8268A472C32E48BF003F08BC3FF94AE8F268947082689570A8BC3E847F926894704268957068BC7F7D08B56F6F7D2262347082623570A268947142689571626217F088B46F62621470A8CD18D77358EC126803C197312268B4F04E30A26D1670826D1570AE2F6EB10268B4F04E30A26D16F0A26D15F08E2F626217F088B46F82621470A268B5714268B4716260957082609470A268A472C32E48BF003F08BC3FF94A28F8D66FA5FE9A3FC528BD8E8FDF88BC3E834FA268B4708268B570A268557067506268547047405B80300EB03B80100268847305AC3535152568BD8268A4F2D32ED8BF103F1FF94C68F2689470426895706268A472B32E48BF08A8CF68FE30A26D16F0626D15F04E2F6268A472B8BF0C1E6028B84D68F8B94D88F2621470426215706E9BDF9528BD8E8AAFF268B4704268B5706268947182689571A5AC351528BD8E891FF8CD08EC0268B7702268B7402813C5A5A745B268B7702268B7402803C6375E1268B770226FF44028BC3E821F8268947082689570A8BC3E8FAF726894714268957168CD18EC1268B5704268B4706263B470A75AD263B570875A7268B7702268B04260347148BDE268947025A59C3268B5F0226834702025A59C352578BD88CD08EC0268B7702268B74028A540184D2750826C747220000EB2F80FAFF7514268B7702268B3F268B34262B750226897722EB16268B07E8E3028BF08AC232E403C003F08B0426894722268B5F0226834702025F5AC38BD82680672FF9268B7702268B74028A440126884734268B5F022683470203C38BD8268B7702268B74028B740126897720EBE3538BD8268A472C32E424032680672FF9C1E0092609472EE94FF58BD8268B7702268B74028A440132E42689470426C7470600008BC3E84004268B5F022683470202C38BD8268B7702268B74028A440132E42689470426C7470600008BC3E80804EBD653568BD8268B7702268B74028A440132E42689470426C7470600008BC3E80604E986F453568BD8268B7702268B74028A440132E42689470426C7470600008BC3E8F003E963F453568BD8268B77028BDE268B5F028B5F0183C30326015C025E5BC351528BD8268B77022683440202268B07E8A0018BC8268B77288A440132E403C08BF103F0833C00744F268B77288A540132F68BC3E8B00126884731268B7702268B348B4404C1E80832E4247FC1E80232E4241F2680672F07C1E00B2609472E268B37268B472EC1E80BC1E00226010426C647320126894F285A59C3538BD826C64732825BC3578BD88CD68EC626807F2C05740A268A472C263A4730751E268B7702268B7402268B7F02268B3D8B740103F7268B5F02268977025FC3268B5F0226834702035FC35356578BD88CD68EC626807F3001740A268A4730263A472C7520268B7702268B7402268B7F02268B3D8B740103F7268B5F02268977025F5E5BC3268B5F022683470203EBF15356578BD88CD08EC026807F300174E5EBC3558BEC535152578BD8268B7702268B74028A0426C6473D008CD18EC126807F3D087320268B7702268A573D32F6268B7C0203FA8BF303F28A152688543526FE473DEBD5268B7F028AD032F68BF2C1E6028A94239026015502268B7F288A550180E2072688572D268B7F288A5501C0EA0380E2072688572B268B7F288A5501C0EA062688572A8A9422902688572C8D66F85F5A595B5DC353568BD8268B77028BC68B744803F08BD88B741E8BC603C383C0045E5BC353568BD8268B77028BC68B744803F08BD88B7420EBE08AC2C31607558BEC5351565783EC408BF88946B8E8D3FF8BD88946E0268B45028B5F2E03D8895EDE8346DE048BC7E89BFF8946E032F68D46B8E8C4FF8AD032F603D28B5EE003DA833F000F8465018846E98066E7F9C646EC00C746D8000066C746D000000000C746F60000C646EA01EB063C010F858C008A56E98AC232E403C08B5EE003D8833F007475268B45028B1F03D88B470432E48BC883C10932ED80E1FE83F9007407D1E933C050E2FD8BC48BD88946BA85C0744183C008268947048AC232E403C08B5EE003D8268B450203078B5EBA2689078B5EBA268B0783C006268947028B5EBA8B76F6268977068B5EBA895EF6C646EA00EB0AC646EA87EB04C646EA838A46EA32E483F8100F8F99008866EA8A46EA32E485C00F8F83008CD08B5EBA8EC0268B5F02895EE08B5EBA268B5F02803F5B754AC646EA108B5EBA268B5F06895EF68B5EBA268B1F8B470432E48BC883C10932ED80E1FE03E18B5EF6895EBA85DB74AC268B1F8B4704C1E80832E4247FC1E802C1E0028B5EB8262907EB918D46B8E8AFFD8AD08D46B8E82E008ADA32FFC1E3028D46B8FF972090E972FF837EF6000F85C9FE807EEA1074058A46EAEB0232C08D66F85F5EE90BFEC353518BD8268A4F0432EDE886E8595BC353518BD8268A4F0432EDE881E8595BC3538BD8268A470432E4E6805BC353518BD8268A4F0432EDE89ED7595BC3538BD866506652665166268B470866268B571066268B4F0466F7F1662689470C66268957106659665A66585BC3538BD866506652665166268B470866268B4F0466F7E1662689470C66268957106659665A66585BC353578BD8268B5F14E838EB8BD066C1E810925F5BC353578BD8268B4714268B57168BD887D366C1E3108BDAC1E302E836EBEBD85352578BD8268B4708268B570A9266C1E0108BC2268B5F14E85AEA5F5A5BC353528BD8268B471C268B571E9266C1E0108BC2268B5F268B5F01E839EA5A5BC35352568BF0268B5C268B5F01E8C2EA8BD066C1E810922689441C2689541E5E5A5BC3535152578BD8268B4708268B570A9266C1E0108BC28BC8268B4714268B57168BD887D366C1E3108BDAC1E3028BC1E816EA5F5A595BC38BD066C1E81092C31E0666506651665266536655665666570E1F0AC07506E80701E9A8003C017506E83701E99E003C027506E85F02E994003C03750BE891038BEC895E0CE985003C047502EB7D3C05750AE80E048BEC895610EB713C067510E88C038BEC895E0C894E14895610EB5D3C077502EB553C08750AE8D0038BEC887E0DEB493C097502EB413C0A7510E865048BEC894E14897E008C461CEB2F3C0B7502EB273C10750AE85A048BEC895E0CEB1B3C117502EB133C15750FE880048BEC895E0CEB07B84F01EB0232C08BEC894618665F665E665D665B665A66596658071FC3501E2E8E1E47048CD8A3E656A3F656A308572E8E1E3F04A3AA041F58CB0030312E303000564553410003000300000100000000000000000302110C0200009C01000034470000B93F008BDF83C704FC6633C0F366AB8BFBBE3A47B92200F3A4061F8C4F088C47108C4F188C4F1C8C4F2083C722897F0E0E1FE8F9D3B04F32E4C3565157B910006633C0F366AB5F598CDB061FC705BB00C6450207C745044000C745064000C6451B06C6451801C6451A01C6451E0166C7453E0084D7178EDBE8CDCC660BC00F84E100061F668945288CC866C1E010B8F34A6689450C81F9FF810F84C20080E503B201538EDBBE801CE8A5D15B061F0F84B10053E821D466894512C7451608108D5C0CE8BAE86689451F668945368D5C10E8ACE8668945236689453A8D5C08E89EE80AC075035BEB7BE8ECD366C1E8103D5802740D3D5E017408770AC6451708EB04C645170E2EA141048945088D5C04E86DE8668BC866C1E910D3E0894510894532B008D2E08845198D5C14E851E85B3D0003772F538BD8B8000333D2F7F35BFEC888451D8845358845348EDB5753BE801CE85DCC5B5F0BC97505061F8325FE32E4EB02B403B04F8EDB5EC30081FBFF8174680AFF75048BC3EB21F6C70875618BCBB201BE201CE8C7D07455E828D30AC0750E66C1E81080E7800AC7E8D502EB3AE8A4CE7406E815CDE864C6BE201CE8D4CB742DE8A500E82C0066BB201C001CE8EBD2BE001CE8EED266C1E8108BD832C0F6C7807402B040E804CCB04F32E4EB04B04FB403C3560633C08EC0BE201CE8E6D28BC866C1E8108BD0C1E90326890E4A04C1EA04FECA268816840426C60651040026C60650040026C60649046226C7064C0400A026C60685041026C7060C01C5608CC826A30E01075EC3010F000A000000000040050FFF000102030405060708090A0B0C0D0E0F01000F00FCBAC403B80001EFB90400B401BE9649AC86E0EF8AE0FEC4E2F6B80003EFB90900BACE0332E4BE9A498AC4EEAC42EE4AFEC4E2F5BADA03ECB91400BAC00332E4BEA3498AC4EEACEEFEC4E2F7C3BE001CE808D266C1E8108BD8E8D8D17505E8C4D17504B84F00C3B84F03C3E8C6D17453BE001C80FB00741880FB017505E804D2EB1D80FB0275108BC1E8B7D1D3E88BC8E8B4CC742EEB0880FB037524E85BD1E8A1D18BD8D3E35033C0BA00033BDA7705BAFFFFEB04F7F38BD0588BC8B84F00C332C0C3B04FB403C3000ADB75078AEFE882CDEB05E867CD8AF8B04F32E4C300E852D1742CBE001CE871D166C1E810F6C440751D0AFF7505E81E00EB0F80FF017514BB1300E8EBE5D1E88BD0B04F32E4C3B04FB403C332C0C35253665081E2FF02D1E28BC24066C1E0108BC2BB1300E81CE5BB1200E816E566585B5AC39C1E0666506651665266536655665666570E1FE834E6E88AFFE853E68BEC895610894618665F665E665D665B665A66596658071F9DCB0E07BFC88E2E8B0EC68EB84F00C3000ADB7506B704B310EB2680FB017514E859CC752153B1010AFF7402B100E86ECB5BEB0D80FB02750DE886CC8AF8C0E702B04F32E4C3B84F03C30080E3010ADB7505BB0201EB0EE822CC750E33C053E80FBB5B7405B04F32E4C3B04FB403C380FC1D7203CD42C31E0666506651665266536655665666570E1F500FB6C4D1E08BF0582EFF94E04B665F665E665D665B665A66596658071FC30E1F500FB6C4D1E08BF0582EFF94E04BC31A4C3085498566850D4D697C9B7C0E7D917D1F7F237F2C82FD828083E7838285E4858D88264FD48CCE4BCE4BCE4BCE4BCE4BCE4BA48DA771FB722E8E1E3F0480268704F3508AE080E47FE8C4013BD3750258C3BACC03ECB2B4B13026F6470901740CB2D4B120A8017549B509EB06A8017441B50B53E870410AFF5B750B80268804F0082E8804EB2B58800E87040880261004CF080E100489166304C70685040800C606840418C7060C01B2588C0E0E01CD42C380261004CF080E10045853500E1F33C98AC8E8FBCA7406E86CC9E8BBC2B208BE401CE8F4CCBE401CE823C8585B0F846EFF2E8E1E3F040E07E819290E1F5366BB401C001CE82FCFBE001CB020E858C85B2E8E1E3F040E07E8222EE82400E82E3DE8D101E88E01E8B43E0E1FBE001CB010E834C8E8FD2DE8F52DC38BECC6461B00C3E82F00E80A00E8BD00E85200E8620AC35383C30A8B166304B91900B81130EF32C0268A27EF43FEC0E2F75B80C20632C0EEC353BAC40383C305B90400B001268A273C01750380CC20EF43FEC0E2F05B268A4709BAC203EEB2C4B80003EFC38BF383C623BADA0326F64709017502B2BAF606890408741D83C610ECB90400B410B2C080FC1174078AC4EE268A04EE46FEC4E2EFC353EC8BDEB9140032E4B2C08AC4EEFEC4268A07EE43E2F48AC4EE32C0EE1E06E84F2D0BED74091F1EB91000F3A446A4071F5BC35383C337B9090032C0BACE03268A27EF43FEC0E2F75BC3BBE4500E078BD30AE47D01C3E80100C333F680FC037F3C4AF6068904107520A08804240F3C027E243C0874203C06741C3C077418B040F6E403D881C30004C3B040D0ECF6E403D881C38004C3B040F6E403D8C380FC077511F606890410750581C30001C381C30005C3BF664EBE874E33C9E84E00C304800005800006C0000D80020EC0020F800310C00311400512800513C005624001538A1E490480FB077F1032FFD1E32E8B87A34EA26504882666045BC32C3028302D3029302A302E301E3F29302E382575072E035D0133F6C383C7033BFE75EDC31E062E8E1E3F04F6068704807549833E4C04007442A04904B900202E8B3E450426F6473301750E26F6470901751B2E8B3E4304EB143C067E0B2E8B3E4104268A6737B5406633C0EB0666B8200720078EC76633FFF366AB802687047F071FC32E8E1E3F0480FB107503E97A0180FB207503E99B0180FB32751FBACC030AC07509EC0C02B2C2EEE9F300FEC80F85F300EC24FDB2C2EEE9E40080FB3575283C807508800E890440E9D300F6068904400F84D0000AC00F84CA00FEC87506E89807E9BA00E9BD0080FB307403E9960050E8303E8AEFBACC03EC8AC8580AC07530B708F6C10175120AED750BA0100424303C307510B702E98B00A0100424303C307502B702802689046F800E890480EB363C017541802689046FB709F6C1017515B70BA0100424303C30741BB7090AED7415B705EB11A0100424303C307508B70B0AED7402B7038A1E880480E3F00ADF881E8804EB293C02752B802689046F800E890410EBB480FB31751B0AC0740B3C01750C800E890408EB0580268904F78BECC6461A12C380FB3375163C00740B3C0175EC80268904FDEBE5800E890402EBDE80FB3475173C00750780268704FEEBCE3C017507800E870401EBC3C380FB367526BB20FF3C017409BB00DF3C007402EBADE80200EBA89CFABAC403B001EE42EC22C70AC3EE9DC3C3B303B700BACC03ECA8017502B701A088048AE0B104D2EC250F0F8BC88BEC895E0E894E16C6461A12C39CFAC706140094768C0E16009DC3281808000809030002632D2728902BA0BF1F00C70607000000009C8E8F141F96B9A3FF0001020304050607101112131415161708000F000000000000100E00FF501808001001030002635F4F50825581BF1F00C70607000000009C8E8F281F96B9A3FF0001020304050607101112131415161708000F000000000000100E00FF281808004009030002632D2728902B80BF1F00C10000000000009C8E8F140096B9A2FF00131517020406071011121314151617010003000000000000300F00FF501808004001010006635F4F50825480BF1F00C10000000000009C8E8F280096B9C2FF00171717171717171717171717171717010001000000000000000D00FF50180E001000030003A65F4F50825581BF1F004D0B0C0000000083855D280D63BAA3FF000808080808080810181818181818180E000F080000000000100A00FF501D1000A0010F000AE35F4F508254800B3E0040000000000000EA8CDF5000E704E3FF000102030405060708090A0B0C0D0E0F01000F00100000000040050FFF0005111C080B14280E182D322024383F0005080B0E1114181C2024282D32383F070C10151A181615131C252F38332E2A2527292A2C2319102325272A2C2B2A29292D32373B3937343233343535312C272F303233343433333235383A3D3C3A39383839393A373431030507090B0B0A09080D11151917151311111213140F0B071010121314131313121416181A191817161717171815141115151617171717171618191A1B1B1A19191919191A181716020304050706060505070A0C0E0D0C0B090A0A0B0B09060409090A0B0B0B0B0B0A0C0D0E0F0F0E0D0D0D0D0D0E0C0B0A0C0C0C0D0D0D0D0D0C0D0E0F0F0F0F0E0E0E0E0E0E0E0D0C00000000000000002818080020090F0006632D2728902B80BF1F00C00000000000009C8E8F140096B9E3FF0001020304050607101112131415161701000F00000000000000050FFF5018080040010F0006635F4F50825480BF1F00C00000000000009C8E8F280096B9E3FF0001020304050607101112131415161701000F00000000000000050FFF0005111C080B252802071B200F14282C0C11252A141E32360F13272C1B203439060B1F2413182C30090D2126151A2E3313172B301F24383D0E182D322024383F0005111C080B14180005111C080B14180E182D322024383F0E182D322024383F0005111C080B14180005111C080B14180E182D322024383F0E182D322024383F50180E0080010F0006A25F4F50825480BF1F004000000000000083855D280F63BAE3FF000800001818000000080000001800000B0005000000000000000505FF50180E0080010F0006A35F4F50825480BF1F004000000000000083855D280F63BAE3FF000102030405140738393A3B3C3D3E3F01000F00000000000000050FFF28180E000809030002A32D2728902BA0BF1F004D0B0C0000000083855D141F63BAA3FF000102030405140738393A3B3C3D3E3F08000F000000000000100E00FF50180E001001030002A35F4F50825581BF1F004D0B0C0000000083855D281F63BAA3FF000102030405140738393A3B3C3D3E3F08000F000000000000100E00FF281810000808030002672D2728902BA0BF1F004F0D0E000000009C8E8F141F96B9A3FF000102030405140738393A3B3C3D3E3F0C000F080000000000100E00FF501810001000030002675F4F50825581BF1F004F0D0E000000009C8E8F281F96B9A3FF000102030405140738393A3B3C3D3E3F0C000F080000000000100E00FF501810001000030002665F4F50825581BF1F004F0D0E000000009C8E8F280F96B9A3FF000808080808080810181818181818180E000F080000000000100A00FF501D1000A0010F0006E35F4F508254800B3E0040000000000000EA8CDF2800E704C3FF003F3F3F3F3F3F3F3F3F3F3F3F3F3F3F01000F000000000000000501FF501D1000A0010F0006E35F4F508254800B3E0040000000000000EA8CDF2800E704E3FF000102030405140738393A3B3C3D3E3F01000F00000000000000050FFF2818080020010F000E635F4F50825480BF1F00410000000000009C8E8F284096B9A3FF000102030405060708090A0B0C0D0E0F41000F00000000000040050FFFE45000C0000000000000000000000000045700C00000000000000000000000001A00A28E00C000000000000000000000000000000000000000001EE810002E8E1E3F04E8FE36E8D536E8761A1FC32E8E1E3F04C706870460F9C6068A040BA089040C112417A28904B401E8731EA0890480261004CF800E100420C7066304D4030E1FE871B8F7C102007503E8F9B7B80300E857F4C31E2E8E1E3F040E07EB001FC3F6068904087401C353BAC803ECB0FFB2C6EEB2C8803E6304B4752FBE6658B9400033DB9CFA8AC3EE8BFBC1EF0383E70303FE2E8A0542EE2E8A4504EE2E8A4508EEFEC34AE2DF9DE98500268A472BF6068904067431B9F800BF64523C087411B94000BFE4533C3874073C3F7403BF245433DB9CFA8AC3EE2E8A0142EEEEEEFEC34AE2F19DEB493C0874253C38742E3C3F742AB9080033DB51E8CBD3B90800F7C31000740383C718E8670059E2EAEB20E8B4D3B9100033DBE85700EB13B9400033DB51E828008AC3E8B72FFEC359E2F25BC3002A002A002A002A153F153F153F153F002A003F002A003F002A003F8BFBC1EF0283E70F2E8AB556588BFBD1EF83E70F2E8AAD56588BFB83E70F2E8A8D5658C39C8AC3FAEE2E8A0542EE472E8A05EE472E8A05EE47FEC34AE2E79DC300000000000000007E81A581BD99817E7CFED6BAC6FE7C00C6EEFEFE7C38100010387CFE7C381000103810EEEE103800387CFEFE6C10380000183C7E3C180000FFE7C381C3E7FFFF00183C66663C1800FFE7C39999C3E7FF1E0E1E3678CCCC787EC3C37E187E18181E1A1E181870F0603E3E3636F6661E0CDB3C66E7663CDB0080C0F0F8F0C0800002061E3E1E060200183C7E187E3C180066666666660066007FDB7B3B1B1B1B003C66386C6C38CC7800000000FEFEFE00183C7E187E3C187E183C7E1818181800181818187E3C180000181CFE1C180000003070FE703000000000C0C0C0FE0000002466FF662400000010387C7CFE000000FE7C7C381000000000000000000000183C3C18180018006C6C6C00000000006C6CFE6CFE6C6C00187EC07C06FC180000C60C183060C600386C3876CCCC760018183000000000001830606060301800603018181830600000EE7CFE7CEE00000018187E181800000000000018183000000000FE000000000000000000383800060C183060C080007CC6CEDEF6E67C001878181818187E007CC60C183066FE007CC6063C06C67C000C1C3C6CFE0C0C00FEC0FC0606C67C007CC6C0FCC6C67C00FEC6060C181818007CC6C67CC6C67C007CC6C67E06C67C00001C1C00001C1C0000181800001818300C18306030180C000000FE0000FE00006030180C183060007CC6060C180018007CC6C6DEDCC07E00386CC6C6FEC6C600FC66667C6666FC003C66C0C0C0663C00F86C6666666CF800FEC2C0F8C0C2FE00FE62607C6060F0007CC6C0C0DEC67C00C6C6C6FEC6C6C6003C18181818183C003C181818D8D87000C6CCD8F0D8CCC600F06060606062FE00C6EEFED6D6C6C600C6E6E6F6DECEC6007CC6C6C6C6C67C00FC66667C6060F0007CC6C6C6C6D67C06FCC6C6FCD8CCC6007CC6C07C06C67C007E5A181818183C00C6C6C6C6C6C67C00C6C6C6C66C381000C6C6D6D6FEEEC600C66C3838386CC6006666663C18183C00FE860C183062FE007C60606060607C00C06030180C0602007C0C0C0C0C0C7C0010386CC60000000000000000000000FF30301800000000000000780C7CCC7E00E0607C666666FC0000007CC6C0C67C001C0C7CCCCCCC7E0000007CC6FEC07C001C3630FC30307800000076CEC67E067CE0607C666666E6001800381818183C000C001C0C0C0CCC78E060666C786CE6001818181818181C0000006CFED6D6C6000000DC666666660000007CC6C6C67C000000DC66667C60F0000076CCCC7C0C1E0000DC666060F00000007CC07C067C003030FC3030361C000000CCCCCCCC76000000C6C66C3810000000C6C6D6FE6C000000C66C386CC6000000C6C6CE76067C0000FC983064FC000E18187018180E0018181800181818007018180E1818700076DC000000000000001038386C6CFE003C66C0663C18CC7800C600C6C6CE76000E007CC6FEC07C007CC6780C7CCC7E00C600780C7CCC7E00E000780C7CCC7E003838780C7CCC7E0000007CC07C186C387CC67CC6FEC07C00C6007CC6FEC07C00E0007CC6FEC07C006600381818183C007CC6381818183C000000381818183C00C6386CC6FEC6C6003838007CC6FEC6000E00FEC0F8C0FE0000006C9A7ED86E007ED8D8FED8D8DE007CC6007CC6C67C0000C6007CC6C67C0000E0007CC6C67C007CC600C6C6CE760000E000C6C6CE760018003C1818183C00C6386CC6C66C3800C600C6C6C6C67C0000187ED8D87E1800386C60F066F66C00C3663C7E183C18003E63380E633E001C003E613C867C001C0E00780C7CCC7E001C00381818183C00000E007CC6C67C00000E00CCCCDC760000FC00BC6666E600FE00C6E6F6CEC6003E003E6067633D003E0076CEC67E067C1800183060663C000000007C606000000000007C0C0C0000C0CCD8307C360C3EC0CCD8306C3C7E0C180018183C3C180000366CD86C36000000D86C366CD80000228822882288228855AA55AA55AA55AADD77DD77DD77DD77181818181818181818181818F81818181818F818F818181836363636F636363600000000FE3636360000F818F81818183636F606F636363636363636363636360000FE06F63636363636F606FE00000036363636FE0000001818F818F800000000000000F8181818181818181F00000018181818FF00000000000000FF181818181818181F18181800000000FF00000018181818FF18181818181F181F1818183636363637363636363637303F00000000003F30373636363636F700FF0000000000FF00F736363636363730373636360000FF00FF0000003636F700F73636361818FF00FF00000036363636FF0000000000FF00FF18181800000000FF363636363636363F00000018181F181F00000000001F181F181818000000003F36363636363636FF3636361818FF18FF18181818181818F8000000000000001F181818FFFFFFFFFFFFFFFF00000000FFFFFFFFF0F0F0F0F0F0F0F00F0F0F0F0F0F0F0FFFFFFFFF00000000000066DCD8DC66000078CCF8CCC6CC0000FE62606060E00000FE6C6C6C6C6C00FEC6603060C6FE00007ED8CCCCD8700000666666667CC0000076DC1818183800FE386CC66C38FE00386CC6FEC66C3800386CC6C66C6CEE003E603866C6CC780000007EDBDB7E0000067CDEF6E67CC0003860C0F8C06038007CC6C6C6C6C6C60000FE00FE00FE000018187E1818007E0030180C1830007E000C1830180C007E000C1E1818181818181818181818783000000018007E0018000076DC0076DC00007CC6C67C00000000000000181800000000000000180000001F181818F8381800D86C6C6C0000000070D830F80000000000007C7C7C7C000000000000000000001D000000002466FF66240000000000000010000000000000000000000000000000000000007E81A58181BD9981817E0000000000007CFEFED6FEFEBAC6FE7C000000000000006CEEFEFEFEFE7C38100000000000000010387CFE7C3810000000000000000000103838106CEE6C103800000000000010387C7CFEFEFE6C1038000000000000000000183C3C3C18000000000000FFFFFFFFFFE7C3C3C3E7FFFFFFFFFFFF00000000183C6666663C180000000000FFFFFFFFE7C3999999C3E7FFFFFFFFFF00001E0E1E3678CCCCCCCC780000000000003C6666663C187E1818180000000000001E1A1E1818181878F8700000000000003E363E363676F6660E1E0C000000000018DB7E3C66663C7EDB180000000000000080E0F0FCFEFCF0E08000000000000000020E3E7EFE7E3E0E02000000000000183C7E181818187E3C18000000000000666666666666660066660000000000007FDBDBDBDB7B1B1B1B1B0000000000007CC6C6607CF6DE7C0CC6C67C00000000000000000000FEFEFEFE000000000000183C7E1818187E3C187E000000000000183C7E18181818181818000000000000181818181818187E3C180000000000000000000C0EFF0E0C00000000000000000000003070FE7030000000000000000000000000C0C0C0FE00000000000000000000002466FF6624000000000000000000103838387C7CFEFE0000000000000000FEFE7C7C7C3838100000000000000000000000000000000000000000000000183C3C3C3C1818001818000000000036363636140000000000000000000000006C6C6CFE6C6CFE6C6C6C00000000000018187CC6C0783C06C67C18180000000000000062660C183066C6000000000000386C3830767ECCCCCC7600000000000C0C0C18000000000000000000000000000C18303030303030180C00000000000030180C0C0C0C0C0C18300000000000000000006C38FE386C000000000000000000000018187E18180000000000000000000000000000000C0C0C1800000000000000000000FE0000000000000000000000000000000000001818000000000000000002060C183060C0800000000000007CC6C6CEDEF6E6C6C67C0000000000001878181818181818187E0000000000007CC6C6060C183060C6FE0000000000007CC606063C060606C67C0000000000000C1C3C6CCCCCFE0C0C1E000000000000FEC0C0C0FC060606C67C0000000000007CC6C0C0FCC6C6C6C67C000000000000FEC6060C1830303030300000000000007CC6C6C67CC6C6C6C67C0000000000007CC6C6C6C67E0606C67C0000000000000000000C0C00000C0C000000000000000000000C0C00000C0C0C180000000000000C183060C06030180C00000000000000000000FE00FE000000000000000000006030180C060C1830600000000000007CC6C60C1818180018180000000000007CC6C6C6DEDEDEDCC07E000000000000386CC6C6C6FEC6C6C6C6000000000000FC6666667C66666666FC0000000000003C66C2C0C0C0C0C2663C000000000000F86C6666666666666CF8000000000000FE6660647C64606066FE000000000000FE6660647C64606060F00000000000007CC6C6C0C0C0CEC6C67C000000000000C6C6C6C6FEC6C6C6C6C60000000000003C18181818181818183C0000000000003C181818181818D8D870000000000000C6C6CCD8F0F0D8CCC6C6000000000000F06060606060606266FE000000000000C6C6EEEEFED6D6D6C6C6000000000000C6C6E6E6F6DECECEC6C60000000000007CC6C6C6C6C6C6C6C67C000000000000FC666666667C606060F00000000000007CC6C6C6C6C6C6D6D67C060000000000FC6666667C786C6666E60000000000007CC6C0C0701C0606C67C0000000000007E5A181818181818183C000000000000C6C6C6C6C6C6C6C6C67C000000000000C6C6C6C6C6C6C66C3810000000000000C6C6C6D6D6D6FEEEC6C6000000000000C6C6C66C38386CC6C6C600000000000066666666663C1818183C000000000000FEC6860C183060C2C6FE0000000000007C60606060606060607C000000000000000080C06030180C06020000000000007C0C0C0C0C0C0C0C0C7C000000000010386CC6000000000000000000000000000000000000000000000000FF0000001818180C00000000000000000000000000000000780C7CCCCCDC76000000000000E060607C6666666666FC0000000000000000007CC6C0C0C0C67C0000000000001C0C0C7CCCCCCCCCCC7E0000000000000000007CC6C6FEC0C67C0000000000001C363030FC303030307800000000000000000076CEC6C6CE7606C67C00000000E060607C6666666666E60000000000001818003818181818183C0000000000000C0C001C0C0C0C0C0CCCCC7800000000E0606066666C786C66E60000000000001818181818181818181C0000000000000000006CFED6D6C6C6C6000000000000000000DC6666666666660000000000000000007CC6C6C6C6C67C000000000000000000DC666666667C6060F00000000000000076CCCCCCCC7C0C0C1E00000000000000DC6660606060F00000000000000000007CC6C07C06C67C000000000000303030FC30303030361C000000000000000000CCCCCCCCCCCC76000000000000000000C6C6C6C66C3810000000000000000000C6C6D6D6D6FE6C000000000000000000C6C66C386CC6C6000000000000000000C6C6C6C6CE7606C67C00000000000000FE860C183062FE0000000000000E18181870181818180E00000000000018181818001818181818000000000000701818180E181818187000000000000076DC00000000000000000000000000000000001038386C6CFE000000000000003C66C0C0C0C6663C180CCC3800000000C60000C6C6C6C6C6CE7600000000000C1830007CC6C6FEC0C67C00000000003078CC00780C7CCCCCDC76000000000000CC0000780C7CCCCCDC76000000000060301800780C7CCCCCDC760000000000386C3800780C7CCCCCDC7600000000000000007CC6C0C0C67C180C6C380000003078CC007CC6C6FEC0C67C000000000000CC00007CC6C6FEC0C67C000000000030180C007CC6C6FEC0C67C0000000000006600003818181818183C0000000000183C66003818181818183C0000000000000000003818181818183C0000000000C600386CC6C6C6FEC6C6C600000000386C3800386CC6C6FEC6C6C6000000000C183000FE60607C606060FE000000000000000066DB1B7FD8D8DF760000000000007ED8D8D8D8FED8D8D8DE00000000003078CC007CC6C6C6C6C67C000000000000C600007CC6C6C6C6C67C000000000030180C007CC6C6C6C6C67C00000000003078CC00C6C6C6C6C6CE76000000000060301800C6C6C6C6C6CE76000000000018003C181818181818183C0000000000C6007CC6C6C6C6C6C6C67C0000000000C600C6C6C6C6C6C6C6C67C00000000000018187CC6C0C0C67C18180000000000386C6060F060606066F66C0000000000666666663C187E183C18180000000000003E6363301C0663633E001C00000000000000003E63380E633E001C000000000C183000780C7CCCCCDC7600000000000C1830003818181818183C00000000000C1830007CC6C6C6C6C67C000000000018306000CCCCCCCCCCDC7600000000000076DC00BC6666666666E6000000000076DC00C6C6E6F6DECEC6C60000000000211E001E3360606763331D0000000000423C003B6666663E06663C00000000000030300030303060C6C67C00000000000000000000007E6060600000000000000000000000007E060606000000000000606062666C183060DC360C183E000000606062666C18366EDE367E06060000000018180018183C3C3C3C18000000000000000000366CD86C360000000000000000000000D86C366CD800000000000011441144114411441144114411441144AA55AA55AA55AA55AA55AA55AA55AA55DD77DD77DD77DD77DD77DD77DD77DD771818181818181818181818181818181818181818181818F818181818181818181818181818F818F8181818181818181836363636363636F6363636363636363600000000000000FE36363636363636360000000000F818F818181818181818183636363636F606F63636363636363636363636363636363636363636363636360000000000FE06F636363636363636363636363636F606FE000000000000000036363636363636FE00000000000000001818181818F818F8000000000000000000000000000000F81818181818181818181818181818181F000000000000000018181818181818FF000000000000000000000000000000FF1818181818181818181818181818181F181818181818181800000000000000FF000000000000000018181818181818FF181818181818181818181818181F181F181818181818181836363636363636373636363636363636363636363637303F000000000000000000000000003F303736363636363636363636363636F700FF00000000000000000000000000FF00F73636363636363636363636363637303736363636363636360000000000FF00FF00000000000000003636363636F700F736363636363636361818181818FF00FF000000000000000036363636363636FF00000000000000000000000000FF00FF181818181818181800000000000000FF3636363636363636363636363636363F000000000000000018181818181F181F000000000000000000000000001F181F1818181818181818000000000000003F363636363636363636363636363636FF36363636363636361818181818FF18FF181818181818181818181818181818F80000000000000000000000000000001F1818181818181818FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFFF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F00F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0FFFFFFFFFFFFFFFFF00000000000000000000000076DCD8D8D8D8DC7600000000000078CCCCD8FCC6C6C6C6CC000000000000FE666260606060606060000000000000000000FE6C6C6C6C6C6C000000000000FEC6623018183062C6FE0000000000000000007ED8CCCCCCD870000000000000000066666666667C60C080000000000000000076DC1818181818000000000000FE38386CC6C66C3838FE00000000000000386CC6C6FEC6C66C38000000000000386CC6C6C6C66C6C6CEE0000000000003E60603C66C6C6C6CC780000000000000000007EDBDBDB7E000000000000000002067CCEDEF6F67C60C0000000000000001C3060607C6060301C0000000000007CC6C6C6C6C6C6C6C6C60000000000000000FE0000FE0000FE00000000000000000018187E181800007E00000000000030180C060C183000007E0000000000000C18306030180C00007E00000000000000000C1E1A18181818181818181818181818181818181818587830000000000000001818007E001818000000000000000000000076DC0076DC0000000000000078CCCC7800000000000000000000000000000000001818000000000000000000000000000000180000000000000000001F1818181818D8D87838180000000000D86C6C6C6C6C0000000000000000000070D8183060F8000000000000000000000000007E7E7E7E7E7E0000000000000000000000000000000000000000001D00000000002466FF66240000000000002F0000000103060C183060C080000000003000007EC3C3C3DBDBC3C3C37E000000004D0000C3C3E7FFFFDBDBC3C3C300000000540000FF99181818181818183C00000000560000C3C3C3C3C3C3C3663C1800000000570000C3C3C3C3DBDBDBDBFF6600000000580000C3C3C3663C3C66C3C3C300000000590000C3C3C3C3663C1818183C000000005C00000080C06030180C060301000000006D000000000066FFDBDBDBC3C300000000760000000000C3C3C3C3663C1800000000770000000000C3C3DBDBDBFF66000000000000C560C30BDB7403E946015732C0B94000F3AA5FBB928E26891D268C4D022E8E1E3F0457BE4904B91E0083C704FC8A042688054647E2F75FA08404FEC026884522A185042689452357E8D91B5F26885D2526887D268A1E4904E8FC05740580FB137F1D32FFD1E32E8A876A8E32E40AC0740140268945272E8A876B8E26884529A185048A168404FEC2F6E2B2003DC8007E15B2013D5E017E0EB2023D90017E07B2033DE0017E002688552A8A2687048AC42401C0E00480E402D0E40AE080F410A08904240A0AE0E89408B2C0B030EE42EC2408C0E0020AC40C012688452DB00326884531E877081E0632D2C41EA8048CC18CCE3BCE743426C577048CD90BF1740380CA1026C5770C8CD90BF1740380CA0826C577088CD90BF1740380CA0426C577108CD90BF1740380CA01071F26885532B003BAC403E8E4048AE0C0EC028AD48AF2D0EE81E204042503030BC22689452B8BECC6461A1BC30AC0740FFEC87436FEC874568BECC6461A00C3B82000F6C101740383C060F6C102740383C03AF6C104740305030383C03FC1E8068BEC89460EC6461A1CC38BFB83C720F6C1017403E88500F6C1027403E8EF00F6C1047403E8F8018BECC6461A1CC3F6C1017406E81615E82501F6C1027403E88501F6C1047403E81202EBDC8AC4EE42ECAAFEC44AE2F5C3E86B04BDC003B9150032E4EC87EA8AC4EE42ECAAFEC44A87EAE2F0EC87EAB020EEC3268A0588044746E2F7C3268A25EF47FEC0E2F7C3B2C08AC4EE268A0547FEC4EEE2F4C39CFA51578BC72BC32689078BF7E80A04EC2688440126895441B2CEEC26884402B2CCEC26884409B2CAEC26884404B2C4EC26880483C705B90400B401E870FF268A04EE8BFE83C70A268B544132E4B91900E85BFF268A4401EEE85FFFB2CE32E4B90900E849FF268A4402EE5F5983C7609DC39CFA8BC72BC32689470251572E8E1E3F04A010042430AABE4904B91E00F3A4BE8404B107F3A4BEA804B104F3A4BE1400B104F3A4BE7400B104F3A4BE7C00B104F3A4BE0C01B104F3A45F5983C73A9DC39CFA268B3F03FB8BF7518BFE83C705BAC403B80001EFB001B90400E807FF47B2C2268A4409EEB2C4B80003EF268A04EE268B5441B81100EFB11932C0E8E6FE268A4401EE80C206268A4404EEECB11532E4E8DBFEB020EEB10932C0B2CEE8C5FE268A4402EE599DC39CFA268B7F0203FB06512E8E1E3F04268A0580261004CF0806100447BE4904B91E00E88EFEBE8404B107E886FEBEA804B104E87EFEBE1400B104E876FEBE7400B104E86EFEBE7C00B104E866FEBE0C01B104E85EFE59079DC39CFA8BC72BC326894704E8A605B2C0B034EE42EC268805BAC803EC26884501504A4AEC2688450283C703B900034232C0EE4242ECAAE2FC584AEE9DC39CFA268B770403F3E86C05B2C0B034EE268A04EEBAC603268A4402EE56424283C60332C0EE42B90003268A04EE46E2F95EFECA268A4401EE9DC3BACC03EC24FE0AC4B2C2EEC3E8A812B2C086C4EEEB00EB0086C4EEEB00EB00B020EEC3508AE080E480802687047F08268704247F8AE03C077E1E26F647330175173C2374133C32740F3C33740BB40326F64709017502B40788264904BAB403800E87040226F64709017407B2D480268704FD89166304C7064E040000C606620400B90800BF5004FC061E0733C0F3AB07268A0732E4A34A04268A4701A28404268A4702A38504268B4703A34C04268B471486C4A3600458C35232E433D2B00DCD17F6C429750832E433D2B00ACD175AC3061E602E8E1E3F042E830E490400756E803E0005017467C6060005018A1E620432FFD1E38BFB8B8550048A3E62045032F68A0E840432ED41518B0E4A0432D2E8A6FFF6C429753D515289955004B408E8B0D40AC07502B02033D232E4CD17F6C429751F5AFEC259E2DEFEC659E2CAE877FF32C0A200055889855004E8AF03611F07CF5A5959B0FFEBEA522AF538C67D0232C02E8E0641045AC3A08504F6E38AF78BD88BCABAC403B8020FEFB402E86D008BD132ED8AE68AC6F3AA03FD8ACA4B75F732E4E85700C3538A1E62040BC075040ADB7403E835035B8BF82BD181C201018AC332E4C38AC632F62BEA0ADB742A2AC3F62685048BC852B401E82000B2C4B8020FEF5A1E061F8BC18BCAF3A403F503FD4875F51FE881FFC38AD8EBF8BACE03B005EE42EC24FC0AC4EE4AC39CFAEE42EB00EC4A9DC31E2E8E1E3F048B16630480C2061FC31E2E8E1E3F048B1663041FC350BACC03ECB2D4A8017502B2B458C3E8EEFF80C206C31E2E8E1E3F04803E4904037413803E490402740C803E4904017405803E4904001FC3502E8E064304A0100424303C3074052E8E064504580BC07403E8E10103064E0481C201012BD132ED8B2E4A048BF88BF08BC5F6E3D1E0D1E50ADB75028ADE38F37E028ADEC3FC8AD88BC1E8B3FF03F03A164A047406061FE81B00C3061FE84000C3FC8AD88AC18AE6E895FFF7DD2BF0061FE80100C3538ACE2ACB32ED32F62BEA2BEAE30E8AE18BCAF3A503F503FDFECC75F458B0208ACAF3AB03FDFECB75F6C3538AC62AC37406F6E28BC8F3A5588BC8F6E291B020F3ABC38BD8E82E002E8E1E45048AC322DFD3E3A8807403301CC3F6D78A3422F70ADE881CC38AFCE80C002E8E1E45048A04D2E822C7C3B028F6E2F6C201740305D81F8BF1C1EE0280FF057F0C03F0F6D180E103D0E1B703C3D1EE03F0F6D180E107B701C3FC8AD82E8E0645048BC1E8860075028ADE03F0E82400C3FC8AD82E8E0645048AC18AE6E86D0075028ADEF7DD81C6F00081C7F0002BF0E80100C38ACE2ACBE32C518ACA5657D1E9F3A513C9F3A48ACA2BF12BF981C6002081C70020D1E9F3A513C9F3A45F5E03F503FD59E2D48AC78AE78ACA57D1E9F3AB13C9F3AA8ACA2BF981C70020D1E9F3AB13C9F3AA5F03FDFECB75DEC3E8B00081C201012BD138F3760232DB51B102D2E6D2E35932EDBD5000803E4904067404D0E2D1E0061F8BF88BF0B050F6E30ADBC352538ADF32FFD1E38B875004E80E0050D1EBA14C04F7E35B03C35B5AC3528BD0A04A04F6E432F603C2D1E05AC38BE883E50F2E8AA6427A8BE8C1ED0483E50F2E8A86427AC300030C0F30333C3FC0C3CCCFF0F3FCFF268B0486E0BA0080F6C4C074020AD6D0EEC1E00275F288560045C38B1663048AC586E0EF8AE1FEC0EFC3528BD08AC4F6264A04C1E00232F603C25AC3538ADF32FFD1E38B875004D1EBE802005BC3528BD032F6528AC4F6268504F7264A045A03C20ADB740803064C04FECB75F85AC3528BD0A04A04F6E432F603C2D1E003064E04D1F88BC8B40EE891FF5AC38BC2F7264A048BF9C1EF0303F80AFF740A8AC732E4F7264C0403F880E107B480D2ECC3E8F0FCECC3E8F8FFB2C0B020EEC3E8EFFFB2C032C0EEC3C43EA80426C47D048CC50BEFC353B00EE895FC8AE0B00FE88EFC8BD8B80EAAEFB80F55EFB00EE87FFC3CAA740DB00FE876FC3C55740432C05BC3B00E8AE7EFFEC08AE3EFB0010AC05BC38AEE8A368504061F0ADB74258AC52AC3F6E68BC85232F6C1E2028BC2D1E22BD5518BC8F3A52BFA2BF259E2F45AE80500C38ADDEBF88AC6F6E38BC88AC78AE032F6C1E2028BDAD1E28BF12BD58BCBF3AB2BFA4E75F7C32E8E1E3F04890E60048AC52460A8207406B9001EE99400F6068704010F858B00803E4904077406E819FC7401C3F6068704087403CD42C38A3685048AD6FECA38CD7E1F0AC974648AE98ACEFEC9EB5C38D174588ACE80FD037C048AE9D0EDFEC9EB4980F9037E448AE12AE50AE4750A38CA7438FECA38CA74328AC50AC138F07E0A80FC027E1280FD027FC880FC037DBF8AD6FECA38D174138ACEFEC9FEC98AE92AEC80FE087F04FEC1FEC5B40AE805FEC32E8E1E3F04A2620432E48BF0F7264C04A34E048BC8E87AFB7407803E4904077702D1F9B40CE8DCFDD1E68B845004E827FEC32E8E1E3F048A264904E8359F7407C70650040000C3E848FB740B80FC0774067708E877FCC3E89FFBC380FC137C03741FC3E84EFA8AD88BC1E885FA8B2E4A0452F6268504F7E58BF703F05AE890FAC3E830FA8AD88BC1E867FAC1E703528B2E4A04C1E503F7268504F7E58BF703F05AE855FEC32E8E1E3F048A264904E8E1FA740C80FC077407774080FC037F04E853FBC3E81EFCC3E8EAF98AD88AC18AE6FEC4E81DFA2B3E4A04C1E70352F7268504F7264A04C1E0038BF72BF05A8B2E4A04C1E503F7DDE800FEC380FC137C0374C6C3E8AFF98AD88AC18AE6FEC4E8E2F9528B2E4A04F7268504F7E52BFD8BF72BF05AF7DDE8E9F9C32E8E1E3F042E8E064504E85DFA7411803E4904077C182E8E0643047403E9B100E849FC8BD8268B078BEC89461AC3A15004E8B7FC8BF0BB08002BE38BEC803E490406751FB90400268A0488460045268A8400208846004583C650E2EBB80002B280EB17D1E6B90400E856FC81C60020E84FFC81EEB01FE2F0EBE22BEBC43E0C0132F680FB0E750926837DFE107502B680161F8BF58BCBF6C6807401475657F3A65F5E7423FEC003FBF6C680740147FECA75E2FECC740F2E8E1E3F04C43E7C00B080B280EBCB32C08BE503E38BEC89461AC3803E4904137C057502EB5AC32E8E064104E818FC8BF08B1E85042BE38BECB80508B90500BACE03803E49040F721AF6068704607513B40AF7C601007502B405B007EFB80518B90501EF518BCB268A04F6D08846004503364A04E2F158EFB8000132D2E944FF2E8E064104A150048BD032F6528AC432E4F7264A04F72685045A03C28BF08B1E85042BE38BEC538B3E4A044FC1E703C1E603B9080032E4268A04D0E40AC0740380CC0146E2F18866004503F74B75E35BB8000132D2E9ECFE33EDEB03BD01002E8E1E3F042E8E0645048A264904E8C4F8740F2E8E06430480FC0774057C3DE90B028AE38BF08ADF32FFD1E38B8750048BD0A04A04F6E432F603C28BF8D1E70ADB740BD0EBA14C0403F8FECB75FA8BC60BED7503F3ABC3AA47E2FCC3502E8E064504A15004E8EEFA8BF8A050048A264A048BE858A8807408247FC5367C00EB04C5360C018AFC32E4C1E00303F080FF067469D1E78AF3B05580E303F6E38AD88AF88BC550B204F6C6807527ACE855FA23C3ABACE84EFA23C3268985FE1F83C74EFECA75E783EE0881EF3E0158E87200E2D2C3ACE82EFA23C3263105ACE825FA23C3263185002083C750FECA75E583EE0881EF3E0158E84900E2A9C38BC550B204F6C380741FAC263005AC263085002083C750FECA7FEF83EE0881EF3F0158E82000E2DAC3ACAAAC268885FF1F83C74FFECA7FF183EE0881EF3F0158E80300E2BDC3FEC038E0720632C081C7F000C38A168504F6E28BF032F68BEA660FB6065104F6E2660FB7164A0483FA647503BA680083FA5A7503BA600081FAAF007503BAB00052F7E2660FB7F8660FB60650046603F866C1CF1003FA66C1C70381CF00FF66C1C7105A8AE24AC1E203A0500450C5060C0103F02E8E064104585751509CFC8BCDE83B00B508AC8AE08AC7D0E473028AC32688056647FECD75EF6603FAE2E29D58595F83C708FEC038E0720E5132C08BCD4903FA83C708E2F9592BF5E2BCC3065233C08EC066C1C7108BC766C1C710E8A49A741638E074120FB6D0E88BC98AE066C1C7108BF866C1C7105A07C380FC137C0B0F8418FF80FC620F8411FFE80100C380FC11750680E38080CB0F50538ADF32FFD1E38B9750045B2E8E064104E807F98BF88B2E85045832F6528B364A04568A268504C5360C0180FC0E7509837CFE107503B41046F6E403F0B603F6C380756DB2C4B8020FEF5A5850524A8AE0575151578BCD32C0AA03FAE2FB5F47FEC438D4760A32E48BCD4903FA47E2FB59E2E0595FBAC403B0028AE3EF5B4B5A51578BCDAC268A25AA03FBE2F75F472BF5423BD3760A33D28BCD4903FB47E2FB59E2DDBACE03B80300EFB2C4B8020FEFC3B2CEB80318EFEBBC2E8E1E3F04803E6304B4740BBACC03ECA8017504CD42C3C3E8D1F80AFF751C8AE380E41F80266604E008266604E82D008A1E660480E320B105D2EBE891F57419A0660424DF80E30174020C20A2660424100C020AD8E85300C3E87EF8C3535080E30F8AFBD0E381E310070ADFE860F5740EB4008AC3E82CF30BED740326881D8AC3E80B000BED740426885D10585BC38AE01E2E8E1E3F04803E4904331F7F088AC4B411E8FEF2C3E830F8C39CFAE89F05B2C0B90300B4018AC4EE8AC3EE0BED74044726881DFEC480C302E2EBB020EE9DC32E8E1E3F048A264904E8F2F4740A80FC0777067403E8C0F5C380FC137C03744CC38AD8E8BBF7BACE03B008EF2E8E1E4104F6C380751CB2C4B802FFEF8A25C605008AE3EF8A25B4FF8825EFB2CEB808FFEFC3B80318EFB2C4B0028AE3EF8A25B4FF8825EFB2CEB80300EFEBE1502E8E064104A14A04C1E003F7E203C18BF858268805C32E8E1E3F048A264904E86FF4740F80FC07770B7408E85FF58BEC89461AC380FC137C037429C3E835F78ACC2E8E1E4104BACE0332FFB80403EFD0E78A1D22D9740380CF01FECC7DF08BEC887E1AC3A14A04C1E003F7E203C18BF82E8E1E41048A058BEC88461AC32E8E1E3F048A3E62048ACFD0E132EDBE500403F18B148A0E49043C0D764A2E8E064504E8EEF375338A1E4A048AF88AC6F6E38ACA03C8D1E1030E4E048BF98AC7AAD1E941FEC238DA735989148B166304B00E8AE5EF8AE1FEC0EFC32E8E06430480F90774C3E98300750432D2EB0F3C0A751D3A3684047503E99300FEC68914E892F3750580F9077F058BC2E84CF6C33C077504E8C581C33C0875830AD274DEFECAEBDA32FF32D22BCB3A3684047406FEC603CBEB9589148B166304B00E8AE5EF8AE1FEC0EF2BFB2BFB268A6502508B3E4E048BF3D1E603F7A08404F6E38BC8061FF3A58BCB58B020F3ABC3B90100B40AE8BCC6FEC23A164A0474038914C332FF32D289143A368404742EFEC6EBED89142E8E064504E8FCF2740E2E8E06430480F907740432FFEB108BC2E8ADF58BF9D1E74F8B1E4A04EB9133C98A3684048A164A04FECAB001E970F72E8E1E3F04F6068704087403CD42C3E876F68BECC6461B00C32E8E1E3F048ADF32FFD1E389975004D0EB381E620475058BC2E85CF5C32E8E1E3F048ADF32FFD1E38B9750048B0E60048BEC894E16895612C32E8E1E3F04A0870424800A0649048A264A04F606870408752DE85DF27407803E4904077F21518ACC8A2E8404BEDB8581FEE48574102E3B0C740583C603EBF024802E0A4402598A3E62048BEC89461A895E0E894E16895612C3841823842B33842A322E8E1E3F043C047F1B5052BACC03ECA8015A587408803E6304D47408C3803E6304B475F83C1C73F432E48BE8D1E52EFFA617864F866F868186B686008600860086DB86F386F78600860086008600860086008625870086298744870086838700869187AC87B387BF87E387803E4904137415E8BFF48BC386E0E870EF0BED74078AC732FF268801E893F4C3E8A6F48AC7E844FC0BED74F026887D11EBEA803E49041374E306E88CF41F0BED74078BF2B91100F3A48BF2E8DE0132E4B91000B2C09CFA8AC4EEACEE9DFEC4E2F4ACE807FCEBB580FB0177B0E843F4B2C0B030E8FBF024F780266504DF80FB0074070C08800E650420EEEB90E89D018AC3B2C0E8DBF08BC8EEB020EE8BEC884E0FE978FFB311EBE48BFAE805F4B9100032E4B2C08AC49CFAEE42EC268805FEC44AEE9D47E2EE9CFAB011EE42EC9D268805E8E3F3EB5AE8CE00C38BFA518BC3268A35268A6D01268A4D02E8CA0083C7034359E2E8C30ADB751AE8B6F3B2C0B030E86EF0247FF6C70174020C80B430E870EEEB1DFECB751CE898F3B2C0B030E850F0EEA8807503C0E702B4348AC7E851EEE87FF3C38AC3E8D1008BEC894E16887613C38BFA518BC3E8C30026883526886D0126884D0283C7034359E2E8C3BAC6038AC3EEC3BAC603EC32E48BEC89460EC3E83FF3B2C0B034E8F7EF8AF8EEB030E8EFEFB301A8807505B300C0EF028BEC895E0EEB9C518BC3E87300E82A008AC3E852004359E2EEC3F6068904027403E816008AC3E83900C3F6068904027403E806008AC3E82E00C350B01EF6E650B03BF6E550B00BF6E15903C15903C1B164F6F180FC327C02FEC08AF08AE88AC858C352E839005A538BDABAC8039CFAEE428AC7EE8AC5EE8AC1EE9D5BC3E81F00539CFABAC703EE4242EC8AF8EB00EB00EC8AE8EB00EB00EC8AC88AF79D5BC35051E881F2B9FF7FECA8087502E2F95958C32E8E1E3F048BF8240F81E7F000C1EF0383FF067F182EFFA5A788AF88C4886289BE893C0374083C047F03E8CF01C3BAC403B0038AE3EFC33C0477F2505350528B166304B007E8EBEE8AE0D0EC80E401A840740380CC02B012E8D8EE40A38504B009E8CFEEA8807404D12E8504A185048BD03DF401731BBAE0013DC2017313BA90013D7C01730BBA5E013D4A017303BAC800891685045A58C6068404FFE85D015B588ADF32FF0AC0740EB30E3C017408B3083C027402B3108ACB8AE981E9010280F9087E0481E90101871E850453E85BF25BE88502C30AC07411FEC8741AFEC87438FEC8743CFEC87440C3892E7C008C067E00C38CC88EC0FECA0ADB740732FF2E8A97A289890E850488168404892E0C018C060E01C3000D182AE80903B90E00EBD6BDB258B90800EBCABDC560B91000EBC20AC075BD8B0E85048A16840480FF07773580FF0177100AFF7506C42E7C00EB26C42E0C01EB2080FF02750B8CCD8EC52E8B2EA471EB100E078ADF32FF80EB02D1E32E8BAF158A8BC58BEC894E1689561289460A8C461EC3C560B258B25CB260C560C57053068A264904E8C7C3268A4702268A6733F6C4017422BDC5603C107D0EE871028C060E013C0E7D07BDB2588C0E0E01892E0C01E81001EB2DB4843C107D13B4813C0E740D7F15B4023C08740580CC80EB0A26F6470501740380E47F86C432DBE80600E8C901075BC38BF88BF2BACE03ECB80500EFB80604EFB2C4ECB80204EFB80407EF8BD68BC750247F0AC074220E0733D2B900013C047507B710BDC560EB10FEC87507B70EE8E801EB05B708BDB258E82D0058A8807400BAC403B80203EFB80402EFB2CCECA801B8060E7502B40AB2CE50EC58EFB004EE32C0EEB80510EFC3FC0BC97501C380FF0E750A26837EFE10750380CD801E061F2E8E0641048BFAC1E705B0405380E303F6E35BF6C3047402042086E08BD003FA8BF5E32832C086FB32FF51F6C5807401468BCBF3A483FB207407B920002BCBF3AA59F6C580740146FEC975DE1FC38A264904500657C43EA80426C47D0C8CC50BEF74308BEF83C707268A053CFF742438E0740347EBF28BFD268A05FEC8A28404268B4501A3850426C47D03893E0C018C060E015F0758C326F64733017401C3C41EA80426C45F088CC00BC3741FBF0B00268A013CFF7415473806490475F2268A0732E4870685048BD8E80100C38B1663048A268504FECC80E41F9CFA80FAD4740BB014EE42EC24E00AC4EE4AB009EE42EC24E00AC4EE9D8AC88BC38A168504F6F2803E8404FF7507A28404FE0E8404F6E2F6C1807402D1E0488AE0B0128B166304EFA04A048A268404FEC4F6E4D1E0050001A34C04C38A264904500657C43EA80426C47D088CC50BEF744A8BEF83C70B268A053CFF743E38E0740347EBF28BFD268A3D268A5D01268B4D02268B5504268A450A3CFF7402FEC850A08404FEC0F6268504A3850458A2840426C47D068BEF32C0E8DBFD5F0758C3505351522EA149040BC074092E8B2EA4710E07EB08B83011B702E8C7BE5A595B58C33C047C01C3E3FD532E8E1E3F0486FB32FFD1E38BFB81C750048B355B8915568BF05153268A4600453C0D7F1F750432D2EB403C0A74323C077505E83179EB623C0875080AD2745AFECAEB27F7C602007405268A5E0045B90100B409E864BEFEC23A164A04720C32D289153A3684047306FEC68915EB2B50E8ADEA7508B80008E840BEEB09803E49040774F132E4863E6204538AFC55E825005D5B863E6204588B155B59E2155AF7C60100750289158B053A3E62047503E834EDC3E964FF33C98A3684048A164A04FECAB80106E8F3BDC32E8E1E3F040AC0750DE818008BEC895E0EC6461A1AC33C017509E841008BECC6461A1AC3A08A04BFA28E2E3A057204BBFFFFC332E4D1E003F82E8B5D040ADB74050AFF7504C386FBC3A0100424303C307406F6C30175EFC3F6C30174E9C3BFA28E2E8A0D32C083C7042E3B1D741286FB2E3B1D740B83C702FEC038C87EEBB0FFA28A04C3BB0800F606890402740EBB0700BACC03ECA8017418BB0800BAB403E8DFEC7421B701E88CE980FAD4741786FBC3BAD403E8CAEC740CB702E877E980FAB4740286FBC30F080F080F080F08030103010101000801010101010101010F010F080F0400020F0201010F01FF01FFE00F00000000070208FF0E00003F00100108000000000100020201000404010005020500060106050600080108000702070607CA003400900096000000000000005F57C390506652E8F4FFFFFF81EF100000002E8B0766BAF80CEF66BAFC0CED32C0668BF8665A58C366B84F03665266565357500AFF7544E8C8FFFFFF6633F666BB5300E848000000C1E810F6C440752B6681E2FF0266D1E2668BC26640C1E010668BC266BB1300E83000000066BB1200E827000000665832E46650585F5B665E665AC39066B84F03C39066B84F03C3906652E811000000ED665AC3906652E805000000EF665AC390668BD75033C0668BC36603C6C1E002EF6683C20458C3AA379D379D379D378837883788378837CA3341346B34AC35AB35AB35F1355636863643376C376C37E335D635D635D635D635D635F1355636863643377137B7376C376C37FFFFFFFFFFFF0000FFFF0000FFFF0000FF000000FF000000FF000000FF000000000008100008101800081018CE3101CE3102DC4503BC4503F33203A73203CF3104E83104F83104CE3103000000007B3900027B3901027B3902027B3903027B3904027B390502753B0002753B0102753B0202753B0302753B0402753B0502AA3B0002AA3B0102AA3B0202AA3B0302AA3B0402AA3B0502E53D0002E53D0102E53D0202E53D0302E53D0402E53D0502E53D0002E53D0102E53D0202E53D0302E53D0402E53D0502AC3C0002AC3C0102AC3C0202AC3C0302AC3C0402AC3C0502E83C0002E83C0102E83C0202E83C0302E83C0402E83C0502593C0002593C0102593C0202593C0302593C0402593C05028D3C00028D3C01028D3C02028D3C03028D3C04028D3C05023D40000070400100704002005D4000004B3F0002443D0002443D0102443D0202443D0302443D0402443D0502633F0002B8410500B8410100B8410000B8410200F9410000F94102003E420000CE3E0002CE3E0102CE3E0202CE3E0302CE3E0402CE3E05028A400000B240000033410000AE410000923D0002923D0102923D0202923D0302923D0402923D0502AD410001AD410001B13A0002B13A0102B13A0202B13A0302B13A0402B13A0502D2400000AE410000AE410000AE410000E33F0000D53B0002D53B0102D53B0202D53B0302D53B0402D53B0502043C0002043C0102043C0202043C0302043C0402043C05023A3C00023A3C01023A3C02023A3C03023A3C04023A3C0502F540000018410000D63C0102D63C0202323D0102323D0202A60002010C930000000000000CAB00003293000000004294000000000C95449900000000000000000000000028A07E93EC93D0981494000020C3000000000000000000000000E4A056A1CAA100000EA2000054C20000DEA2B4A474A5D2A614A7BABF0000000028A70000DEBC00000ABFEEA80000C6AA0000000000006CC13ABE0000000000000000000000000000000000000000000000000000000020AE2CAF62BA82BC00004A000201000000004CD144C314D20CC440C448C668DD92C800009CC456CAA8C43AC8C2CA00001CC548DD000034DD000030C50AC678DC0000000010C680D2000000000000C8DC000000002500020100083700004BE50004491900020502060000005215523D5218521A0D655300025B004B000101000037000042210063001F006301390063033F00630245005A5A5B030540000005042D1940001505400201020C00400200000D005B020000E30D5B020000E70D5B020000E80D5B006E0002010C083700003D2500004422003D250006445D003D2500044458005538005B01050000616001003CE5010080492A000105000062600100010101000156004003A940000105000061600100010201004002E500015B02E500015B01050000616001003CE50100804965005B280002010000370000010500006CA80100020000010002010100010500006DA8010002000201005B2E0002010000370000521603014100150541100102600F41030140001B0540100102620F400105F30FE10A602D5BCA0002010404370000660C030C4100002D0A41422D0D4204003D240006004431002D0D4208003E0A42414519005520005B5600430324430500030446000033054601000000010200004603000001000D020100442D054602000000010200004603244304004BE5000149AA0007020100452D05460100000001020000464A02010044449000032943000EA200444398000329430008A2004533054603000000010200004601020100005B0D020100440104000000003DA5000144C400070201004543C9000D020100445BC303010710083700003D2501FF44A1033D250119469D033D250102479D033D25011445A1033D05000000000044FD01661B5600023DA501024941005608404353003DA50103494E00560840435300030C4018003E0D400000446200030A02404366000EE501804BE5010249740002A9020255280203010000030546A86100003DA50100448D0003054650C300005CA50F5EF3085528013E0500404B4C0047A7001B05000143BC002C6501013E0500A025260048BC001505000143A700142D01044BE5018049EB00210502800000007C0240007E0240467C0540100000007E0540350C00000302024043EE00560002270200462105410000010027024046030A42403E054100000000491601030D40010056080143400103024046030201410302034127024041030240033E054100000000491E0127020103030A014027024603030A0301030A0140038A0142270200463305401000000015054005038A034066FF2D0D42AB035600400321400133254014152540012D0A4240030C4800004A25065E044423025600404BE50130449B014A25F15D0144DC0143A3014A25F15D0149DC010300400B5E090D40F0FF3E1A400349DC013C02075E0149DC013C0A0A5E0349DC010300400C5E092540703E29400149DC013C02085E0244E0010EE501013A00004BE501014475034BE5010249FD013D25020446FD0155280252233A000002010302020502000003040221020132250214524D020102033D050000000000449D033DA50100444E0202010302020502000F030002210201322502144BE501304448020EE50201524D0201020366FF2D0D42AB035600400321400133254014152540012D0A4240030C480000030540000038003E0500C0C62D0047A0025EA540CF203E0500E067350047A0025EA540CF103E050000093D0047A00209A540CF5CB20F5EC34003054020001C243DA5010044BB0203054010000C125C9A105E0300405C1A0B5E0F00035CA20B5E8F40560040032940010F2540014BE5018049E5020F6540015C0A0C5E8BFE40078D0D5E0FE03E8D010000490A033E0D030000490A0307E50E5EFE430F030DE50E5E014AE50E5E014927034A650C5E0149270307E50E5EEF432C030DE50E5E100102075E010102085E027C050200EC04007E0240462705400A0000000102095E40010A0A5E033A00003DA50100449D0303010002020502000103040221020132250214524D020202003A00003D250204469D030225030202E103020261030102B9030108A503040EB1030202010200522E3A00005B0225010B4375037A18000000D800B001880260033804000000010002000300040005007300020100003700003D65000144720056004003214000212540DC030A48400105B63A000100005C052D3BCFFFFFFF200000005C05403BCFFFFFFF200000005600400321400021254080030A48405C050150FFFCFFFF0003000056004003214000150540195C025050FFFFFFF1403A00005B5B00E4060201080437000042290063071302630817005A5A5B0D25C839025C253447FC010125320100010542350000000001054035000000000105463500000000010544350000000001054A3500000000010548350000000001054E350000000001054C3500000000010552350000000001055035000000000105613500000000010563350000000001056535000000000105673500000000010569350000000001057235010000000105DF39021000000105B33A000000000105B13A001B5D050105063B350013000105073B361100000105083B41E601000105093B0000000001050A3B0000082C01050C3B0204000001051F3B5E4E010001050A3B0000082C01050D3BA401000001050F3BA40100000105143B000200000105153B49D200000105183B200000000105193B240D000001051C3B0000000001051D3B000000000105ED3A000092060105EE3AAA0800E00105C939FE0100000105CA391E0500000105CB39BA0400000105CC39240400000105CD39A20800000105CE39FE0100000105CF391E0500000105D039BA0400000105D139240400000105D239A20800000105D339FE0100000105D4391E0500000105D539BA0400000105D639240400000105D739A20800000105D839FE0100000105D9391E0500000105DA39BA0400000105DB39240400000105DC39A20800000D25C839020D25DA4101012533470001253147000125324700012545480001658B4FF05B01051F35FFFFFFFF010520351F00000001053B016402120001053001A0861100010531013C000108018D8338640001258238010125320100010542350000000001054035000000000105463500000000010544350000000001054A3500000000010548350000000001054E350000000001054C3500000000010552350000000001055035000000000105613500000000010563350000000001056535000000000105673500000000010569350000000001057235010000000105DF39021000000105B33A0000000001058F3B1000000001056B3C200000000105473D300000000105233E40000000550001200D01DC00030A48400105B13A001B5D050105063B350013000105073B361100000105083B41E601000105093B0000000001050A3B0000082C01050C3B0204000001051F3B5E4E010001050A3B0000082C01050D3BA401000001050F3BA40100000105143B000200000105153B49D200000105183B200000000105193B240D000001051C3B0000000001051D3B000000000105ED3A000092060105EE3AAA0800E02C0501010000003D0D01050045FE025608480105C939FE0100000105CA391E0500000105CB39BA0400000105CC39240400000105CD39A20800000105CE39FE0100000105CF391E0500000105D039BA0400000105D139240400000105D239A20800000105D339FE0100000105D4391E0500000105D539BA0400000105D639240400000105D739A20800000105D839FE0100000105D9391E0500000105DA39BA0400000105DB39240400000105DC39A20800000D25C8390255000166FF2D0D42DA06030C4800000D25DA4101200D011B00030A4840012133470101213147010121324701200D010400030A48400121454801200D011000030A4840030D4000F00F614001016A8B4F402C0D0101002D0D4202003D25010545600456084855000152172C2501013D25010545BE040205010502000052296608030C410C003E0D41000044020501A5150103010513011027000001011401010D251501014A6515010244FA040101420101010146010101014A010101014E01010101520101550001524E5C05DD5D00F0FFFFAA0A00005C056353FFFFFF00000000305C056553FFFFFF00000000305C056753FFFFFF00000000305C056953FFFFFF00000000305C056B53FFFFFF00000000305C056D53FFFFFF00000000305C053155EEFFC0FF110001005C053156EEFFC0FF110001005C053157EEFFC0FF110001005C053159EEFFC0FF110001005C05315AEEFFC0FF1100010001059A550100000001059A560100000001059A570100000001059A590100000001059A5A0100000002250107523302050100070000550002524D2C2501013D25010645E805661603A40006002D0D420800030A00423EA50000447906030A4200030C0104003E0D0121214425063E0D012122496D06030C40080066162D0A424003244000003E2540FF446D063E25401449650603044002004C054000010000446D063E0D01212244620601A5596101436D06436D062D0C4201004330062D0D00100033A500014305066616030C4104004C0D410200448D060DA54C0002521601015339020101543903010555390000000003014101150541180102C40041030141011B0541080102C900410300415539010259394103014003330140022D02404101025A39405B7A0A0000006B01D60241040B29BC000201000466FF5608472D0D42740003214700092547F042224763005B00636056006320360063904E0063A03E0063B046005A5A5B2D0D423000435B002D0D422400435B002D0D423C00435B002D0D421800435B002D0D420C0003214700092547072D0A42472D0A4247030C4800005B7ABC000000800000018001000280020000000100020003000400050000DC00B801940270034C0400006B01D60241040B29762A0000D800B00188026003380400005A00B4000E016801C20172000101000037000056004003214000210D406B01030248400125AC41013D650002494C00010DBD4101005400BB410D25AE41010105AD411100000007A5AC41FE0105AF4111011101436E000105B141000000010105B541000000010105AF41110111015400AD41010DBD4100013A00005B7300010100003700005214030041C041331A4141038A4041030041C141331A4141030A40413E0540000000004936000305407008000F0E2500B052143D650000446D000102164D400105174D000000000105184D000000000105194D000000005C05144D8FFF0FFF310000005B0725144DFE5B0043000101000037000052143D650001443A000D65015003542001504AA5015001491B0054200F5056004020250001030A484007E5E8397F433F000D250150013A00005B00CF000201000037000042290063001B0063011B006302AA005A5A5B66FF5608402D0D42C30003214000152540012D0A4240030C4800003D6500004472000D25CC00013A0000018DC000000251020105C600000010000105C80000005000010DC30002005420CA000105C20000010000431A000725CC00FE3A00000D25CA00010125C2000107A5C100FE0D25D500010D65D500014A65D500014493005408D5005400C6005400C800431A005C65D400FC0201A1C000002BA5C000010DA5C100015B7A0C00000002002C002D002E002F0000D501030104003700006608030546102700003DA50002491E00020200465B0305400C0000002121400066FF2D0D4281012D0A4240030440000003044104000304000800010200004003884001000318400100098D40FF013E8D40000049990001020000410300400100090540FF0100003E0D4000004982000305460071020043BC00210240460300410100090541001E00001B05410943B4007C0240467E054000000100030041010009054100F000001B05410C75024041030246403DA5000149C800020200465B3DE50000490B0156004003294000210D400A00030241002D024041010200004003004101000905410000F0071B05411402E200413DE50000490B01020500102700005B5600400339400055380066FF2D0D4259013E0C400200472C012D0D420800431C01330C400400210C4006002D0C40000003094200550000020A00407C054640420F007E014000020200405B7A7C00FFFF070000000000C8003F000800190040065F0040003200800C7E006000640000327F007F006400116C0100006C01004C6C01000D6E0100006E0100486E01000D70010000700100487001000D6E0100006E0100486E01000D7E0100007E0100487E01000E72010000720100497201000EB0010000B0010049B0010000BF0002030400370000560046560000032100005520003D650003442C003D65000D46B40002290000322500080E25006052145C222855F80002220000030D41010066FF2D0D42BB002D0A424103240000003DE2000048A0003E250004446F000322460033254601152546040F254601333900003E250001448C003E250002448500032546210365002F439000036500130379460033654601152D46030F65460152140102014D460152004D005400054D5400FF4C3A00005B7A040003020104005E01010404083700003D6502014927014BA50203492A0052250205000000000002050100050200432F005528025225033800C400034800C9000E2502905214082502070D05B43A111101005C25B33A0B0103214102152541040D22B33A410D65B63A015C25DB3AFD01030241001B054108010ACB3A41010ACF3A41150500180102CA3A000102CE3A004BA5020349A0002B05CA3A000010002B05CE3A00005000010DA73A8000010DB13A0003010D0C3B010301050A3B0000082801050B3B0000080001E5EF3A0801E5EE3AE05400A93A0119AA3A000189AA3A000119AE3A000189AE3A00070DA53AFFF866FF2D0D424F01030C4000003E0D40FFFF440E013D8C010000440E012D0D42030043F1000124A53A02000109C73A010725B33AFE0725DB3AFE3A00005B0E25029052143D650200493C010D25B33A014348013D6502104448010109C73A013A00005B7A0F00010001010103010502020008FFFF0842000101000037000052143D65000144210054204B504A654B5001491600433E005C4D1B50FEFF00014AA5015001443E000D254B50014A654B50014436003A00005B1300010300003700000E2500A052143A00005B00C6010101001856004003214005210D408000030A4840370000010D47500D003D8D010D00462C000119475001018D48504001010D49502C01030941002D194100038A41412D8D4101001B1D4102011A485041330D410100010AEA4F415408EC4F0199EC4F02031941003309410256184103B14104331A4141018AEB4F4103194100330941022D1A41412D0941004B2504104498002D094100010AEB4F4103214104092541021B2541010122ED4F41030941012D194101330D410100010AEF4F415408F74F0199F74F03031941013309410356184103B94104331A4141018AF64F4103194101330941032D1A41412D0941014B25042044FC002D094101010AF64F4103214104092541041B2541020122F84F413D65050044210107650150EF4326010D65015010030046EB4F020002F64F56004003214005210D400100030A4840560040030940023D650500495301330D4002001505400F5600413D8D020D00486B01030D410D00331941020F0240410FE540800102E8394056004003214005210D406B01030A48400109C341000189C341010109C441000189C441010102C041460101C141025400BE410131BE410401B1BE41045400BF410139BF410401B9BF41043A00005BD80101010000370000560041033941010925410F3E254107491C005B560040036240410102585340030A464266FF2D0D42C001152541022D0A4241030C4800000705905D00000100030C48020003054050C300005600410321410027024041150540100325400201026253400105635340000030030A42465608480D255A530154005953560041037141010FE5418001027253410369410003A5410101027253414B25010144D5000371410003A5410201027253410379410003A541030102725341030540003101002DA9400101026E5340430801037141010F65410103A5410201027253410305400011010001026E53400305400130000003A9400101026F53405CA55853CF100D2558530102650002560040032940012D254004210540C80000005600410321410027024041030A4140330D4101003E0D41000044AC0151644A255B53044432014A655B533149AC01026500014B25010149AC0101057253010003803D65010149730102A800725343AC0156004103194100560040030A40413B02400328407253036840725303A840725303E8407253040200402DA541042D0D4104003EA94101457A010D255A530201255853025101540058535B7A18000000000004000200080004000C0006001000080014000A004600020200043700005530003225000120250008560848032248400DA5D5530102A000D45308A500034A25D45302443C005C0DD553FFFE01004342000D0DD55301013A00005B1403010500003700000E25006052140825000F422900630F530063110902631266026301CC026300F5026308D8016309B901630AC0016313C8016314D001630BE001630CBC02630DE801631001025A5A43B50154009A55010D2B551F003DA500004472003DA5000244A2013DA5000549A2015400C9553D250200498100022502023225020101E1C955024A25CC550149B5017B0501008000007E05408C0A000056004603294602270240460765D055FE0105D155008000000102D255400DE5D355100D65285504510107652855FB5101032140021525400501E2CA55405420CE5502A0022855030A464808A5020756004003314002210D408000038A4640030A4840038840EA4F030840EF4F2D054001000100039841EB4F031841F64F030A484601020C564001020D5641031A4846030040EB4F331A4040330840BE41330840BE41030041F64F331A4141330841BF41330841BF41030A4846010A0F5641018A0F5640031A4846030040EC4F338A4040030041F74F338A41414A25ED4F014485010FE540804A25F84F014491010FE54180030A4846011A0E5641019A0E564043B50107E528552F0D65285504510107652855FB51013A00005B5420D85543B5010125D8550143B5010125D8550243B5010125D8550343B5010725C855EF43B5010D25C8551043B5010D65D05501510A0725CD55FE510A010DCC55010251C843B5010D25DE551043B50101696F55023DA500034925024A2535551049250201A56F5502432A0201B16F55003D250200443F0232250201142502045CE16F558F023DA50005444D023DA50000495E0202A5000003394000332540010122CB554002E002705543B50102E00270554BE5020144B50102B0006F553DA50000448C023DA50005448C0202E5000443950202E000CB552CE500013DA5000244A3023DA5000349AC0254007055500243B50102A50000033143004A626F554444B5015408CC5551C80D25CD5501513243B5010D257055013DA5000044E102010DA1550F0143B501033943000302404433254001010AA1554043B5015400A15507257055FE070D6F55FF8007253555EF5420C8555420D85543B5010C01010604003700004229006307EA00630125006300C000630BED006304F2005A5A43050102650010524D3D65000044E6000265001152044BE5020149E6003DA50000495700030100000265000F553800524D020200000265000A550802524D3DA500004970000265000B524D4379000265000E524D43AD0020050164000000020501141E00022C2101000301000002020040520C26050064000000020201400202000002650001550802524D5105026500015204510A02650009524D43E6000265001252044BE5020144E60002650008524D0265000052043DA5000049E60002650000524D3A00005B43E600524D43E6006606022900000268004E00028C00200052335B0229000052335B350B020208003700003D25000344220B66FF2D0D42290B56004003214000152540012D0A4240030C4800003D650010497400026500013D25000445220B4AA5F95D0144220B026500004AA5155E0249220B0D25165E010705155EFFFFFBFF3DE50004456D004AA5155E0144220B0265000143220B3D2500044885000305000101010143C5004AA5F95D01447B004AA5155E0249220B0D25165E010705155EFFFFFBFF030500010100003DE5000144C500030500010100003DE5000244C500030500010101014229006307EF00630ADD0463014D056300DA0663097C086308EC09630B9F0A630E4D0A630F0E015A5A5B0105FD5D000000000105EE5D004000005C05F05DFFFF0FFF0000400043220B4AA5F95D0149220B4A25005E014942013C25F95D004942013C71F95D0049420108E50001030040F15D092540013E39400044220B5420F95D074DF15DDFFE4A65F15D40494C010D05035E010101010D25005E010DA5005E200D25005E010D05035E0101010103394000092540015C22F15DFE405C65005EC1060D25025E080DE5005EA05C05065EBBBBFFFF333300005C05035EF7F7F7F70202020266072D0C420A005600402025000C2D0A4240032440090003624040038A40405C02045EF8F8F8F8400321430003A246443DA500004415020305400400000066072D0C42040003044100003E054100000000444C024CA4460600490D022D0D420C0043EE010324400700434C020305400500000066072D0C42080003044100003E054100000000444C024CA44604004942022D0D4244004323022D0D42080003244005001505400E5C02005EFF3FFEFF400765005EFE3DA500004900035C050B5EFFFF8FFF000020000D050C5E010100005C050B5E0F00FFFF401600000D050E5E000000010105075E0500666601050A5E020000000105095EF32001000105085EF3DF000001050D5E000000005C050B5EFFFFF8FF000000005C050C5E8FFFFFFF200000000D050E5E0000001007050C5EFBFFFFFF01050F5E3E0008000105105E3E0018240105115E3E0000000105125E3E0034004390035C050B5EFFFF8FFF000010000D050C5E010100005C050B5E0F00FFFF600A00000D050E5E000000010105075E010052F854000A5E0105095EE90900000105085E5C0F000001050D5E000000005C050B5EFFFFF8FF0000040007050C5E8FFFFFFF0D050E5E000000100D050C5E0400000001050F5E3E000C000105105E3E0080880105115E3E0000000105125E3E0034000DA5065E080D25F35D010D65F15D204A65F15D40499F030DA5F15D010725005EFE0705035EFEFEFEFE4AE5005E1044B90366072D0C420C0003044000003E0540FFFFFFFF44EB030104F45D00000104F55D04002D0D42080043C8030105F45D382000000105F55D0C0000005600014A05035E2020202044240451022D0501010000003E0501A086010049FE030125F95D0343220B0D05005E00010000030040045E0905400700000021054003000000030541212000002D0241400102F45D41030041F55D0105F45D840000000102F55D41030541222000002D0241400102F45D41030041F55D0105F45D850000000102F55D413D25000445C004030040005E09054000C001001B05400A0105F45D21000000030041F55D0925410F0F2241400F2541800105F45D210000000102F55D415600413DA5000044CF04030D4101015CA20B5EF8410D65005E0143220B030A464866072D0C420A005600402025000C2D0A424020250002030A484001042E5D000007E52D5DFC030A4846030540010101013D250004452F054AA5F95D01442F054AA5065E01492F05090540FFFFFEFE0D02035E40510503024000690540FFFFFFFF0702035E4051C851C843220B4A25FC5D0149220B3C25F95D0049220B4C250001446C050D05065E040000004C650001447B050D05065E400000004CA50001448A050D05065E000400004CE500014499050D05065E0040000003024000150540040D02035E405600014A25FC5D0149220B0302400015054005030041035E09024140090540202020203E02414044EB0551022D0501010000003E0501A086010049A9050125F95D0443220B0302400015054004690540FFFFFFFF0702035E405600014A25FC5D0149220B0302400015054005030041035E090241403E05410000000044400651022D0501010000003E0501A08601004902060125F95D0543220B5600014A25FC5D0149220B4A250C5E80496E0651022D0501010000003E0501A08601004943060125F95D0643220B030A414856004003214000030A48400D25600101510A030A48410D25E85D034A25E85D04448D060D65F15D124A65F15D04449A064C25000144AE060D65F05D104C65000144BA060D65F05D204CA5000144C6060D65F05D404CE5000144D2060D65F05D800171F95D0043220B4A05065E4444000044220B0765F15DFD0725E85DFD030A414856004003214000030A484007256001FE030A48414C2500014416070705065EFBFFFFFF4C6500014425070705065EBFFFFFFF4CA500014434070705065EFFFBFFFF4CE500014443070705065EFFBFFFFF07650C5EFE5400085E5400095E030040065E4C2500014467075E0540FCFFFFFF030000004C6500014479075E0540CFFFFFFF300000004CA50001448B075E0540FFFCFFFF000300004CE50001449D075E0540FFCFFFFF003000003C02065E404451080102065E4003024000150540040D02035E405600014A25FC5D0149FC070302400015054005030041035E09024140090540202020203E02414044FC0751022D0501010000003E0501A086010049BA070125F95D0743220B0302400015054004690540FFFFFFFF0702035E405600014A25FC5D014951080302400015054005030041035E090241403E05410000000044510851022D0501010000003E0501A08601004913080125F95D0843220B0302400015054002690540FFFFFFFF0702035E400302400015054001090540020202020D02035E4043220B4A25FC5D0149220B56004020250002030A4148030248400DE52E5D10030A48413DA5000044A8080D65F05DF00DA5F05D010DA5E95D013DA5000044C4084A05065E3333000044D5090302400015054001690540FFFFFFFF0702035E400302400015054002090540040404040D02035E40030040065E4C25000144FF08090540FCFFFFFF4C650001440D09090540CFFFFFFF4CA50001441B09090540FFFCFFFF4CE50001442909090540FFCFFFFF0102065E4003024000150540040D02035E405600014A25FC5D0149220B0302400015054005030041035E09024140090540202020203E02414044800951022D0501010000003E0501A0860100493E090125F95D0943220B0302400015054004690540FFFFFFFF0702035E405600014A25FC5D0149220B0302400015054005030041035E090241403E05410000000044D50951022D0501010000003E0501A08601004997090125F95D0A43220B0302400015054003090540080808080D02035E4043220B0302400015054003690540FFFFFFFF0702035E4007A5F05DFE4C25000144110A0765F05DEF4C650001441D0A0765F05DDF4CA5000144290A0765F05DBF4CE5000144350A0765F05D7F07A5E95DFE56004020250002030A484007E52E5DEF43220B0321430003A2464466072D0C4204003DA5000344710A66072D0C4206003DA5000249220B03044100003E05410000000044220B3D020141468F0A4CA446060049970A2D0D420C0043710A030441080043FE0A08A5001B66072D0C4208000321430003A2464403044100003E05410000000044220B3D02014146D00A4CA446040049D80A2D0D42440043B20A2D0D4208003DA400000044F90A03244100003E25411844F90A2D0D42060043DD0A03044101005C020F5E0000FCFF415C02105E0000FCFF415C02115E0000FCFF415C02125E0000FCFF413A00005B7A0C000000D800B001880260033804001F02010200003700003D0D00000044CE01560041032141010925410F150D4102030A404266FF2D0D4207022D0A4241030C4802000DA5905D015408905D030C48000003F94101153D41045CBA10548F41030A42404A251054014981000D0D105401010D251054104A2510542044670007251054EF4A251054204974000D25135440560041030941003B0241033B41001B25410401A211544103694101152D4104012A1154415600400FE54080037340000102165440560040036B40000102165440036340000102165440037B40000965400F01021654403E254104471001332541042D054604000000036340000102165440036B40000102165440037340000102165440037B4000010216544043D7000D251354020D25115401032940012125400A2D2540644A251454014942015114332540013E2540004926010265010143CA014A051454F08FFF004454010265010243CA0101051654010000800300401654026A014002B801145408A5011F3DA50100467C010265010343CA0132A501013D8D00000044CA013DA5010044CA0103314101031940005618403B02400328401654036840165403A840165403E8401654040200403E25410447CA01332541042D054604000000439D013A00005B66FF2D0D420702030C480000018D18543200010519547A1C020001051A5410113D102D0D4204002C2500013D25000649D5015608485B7A1800000000001C0004003800080054000C00700010008C001400005C00010200040225010403314100090D410F00150D410166FF2D0D4250002D0A4241030C48000007A5905DFE010D905D111150024A0D935D0100494100022501135408905D5400925D3A00005B7A0C000000040008000C00100014005B01020104043700004221006307290063034E006302600063048400630C1201630D1B015A5A435A01010D455D9F0F66060124475D3000014C475D31000324463B003E254600445A015420475D5B0DE5475D015C05435DFFFFFFFD110003005B0DE5475D014A25445D024472000DA5435D010DA5435D025C05435DEFFFFFFD010000005B4A055300000200004494000D254B5D013D8D00000044110103054680F0FA0256004103194100210541FF0F0000270246403E05410000000044C4002D0D400100018A455D4056004103194100210240412702464001A54A5D0C010A4A5D4015054004560041032941002D0D410100210240411B054008010A485D400DE5485DC007254B5DFE4A654B5D014909015B0105435D000003005B0DE5475D014A05435D010000004441014A05435D10000000494E014A05445D010000004936015C05435DFFFFFEFF000002005B5C05435DEFFFFDFD010000005B00D00002010000010500C2000000E001053E26430000005C057E1238FFFFFF430000005C057E18F8FFFFFF030000005C057F12F8FFFFFF030000005C057F18F8FFFFFF03000000010517742D0B150003004026743E0540F900000049700001050BF000001200010525CE3021210343CF003E0540F6000000498D0001050BF000002100010525CE2301102343CF003E0540F000000049AA0001050BF000003300010525CE3120312043CF003E05400F00000049C70001050BF00000CC00010525CE2031203143CF00010525CE203120135BAF0003020C003700000301000003010101030102025216030146001B0546080202000002020101020202023D6502004936002C2202463DE502004941002CA202463D65020249480003014003090540000000FC3E0540000000D0496E00010563C000000040550000552002437200540063C0010164C00056004003214002010265C040010166C00103314002010267C0400101E8C0033D65020144A6003DE5020144A60051024AE5A0218049A6005B00B201020200003700003D250101448D003D250102448E003D2501034433003D2501004993013D050000000000495E003A00005B523C03004024013EF94000445A004AA524011044410003F9400001022401404AA52401104452005538005B5420A453523C03004024013E3940004489004AA52401084470000339400001022401404AA52401084481005538005B5B66FF2D0D429A0156004103394101152541022D0A42413D65010B49010120050064000000030C48020003004140010F254110090D41DFFF030C4800004A0501500100000044EA00030C4802003C0241014049EA003C02400141449301030C48020007254001EF0102410140010A400141439301560040038D400C003D650118442401038D403E003D65011944240103A9400133A540140331400109254003152540040F0D4001015600410329410133254114030C4800004A05015001000000447101030C4802004A254001104971013C22430141497101031A48403C0A000140449301031A484007250001FE030C4802000122430141070D4001EFFF031A4840010A0001403A00005B7A180000000000800004000001080080010C000002100080021400E700010808000301000002050005000100522920050064000000030246400202000003010000553800030540003200003D05000000000044640003024046270140003E05409C180000475300030540003200003E0540C8000000466400030540C800000066FF2D0D42C7003E0C400200477B002D0D420800436B00330C400000030C0106003E0D011900499A00270D403200210D400200439F00270C4006002D0C40040002E20040330C400400210C4006002D0C40000027024640020A004002B200405B7A2000C8004006080019004006800C40003200800C381860006400003200327F00640000CB00010100003700003D65000144610056004020250010030A484007258F4FFD4A258F4F04492000521407254650FE4A65465001492F0003214000210D40DC00030A48400725B43AFE56004003214000210D406B01030A484007258541EF43C70056004020250010030A48400D258F4F024A258F4F0444710052140D254650014A6546500144800056004003214000210D40DC00030A48400D25B43A0156004003214000210D406B01030A48400D2585411056004003214000210D405A00030A48400D254C4D013A00005B00240001010000370000030041E30D330541010000001B05410B01020600410D251F0E035BC8000201D6098002A000E0012D00100060000A0002000000000008080600123CA00F2003000158021C00280080000100040000000000000000006A3C6419000440010003260018008800030006000000000000000600553C302A00050802C003280060007000010003000000000000000000E93C302A0005980100042A0030007000010003000000000000000000EA3C8F2F7805D0011A04270058009000030004000000000000000200EB3C483F40063002B00432004000C000010003000000000000000000EC3C3400020100000000002A002A00002A2A2A00002A002A2A15002A2A2A15151515153F153F15153F3F3F15153F153F3F3F153F3F3F5C00020100000000000000000000000000000000000000000000000006000000C8320000000034210000B80B000000007D0000010000000000FFFF0000000000000000000000000000000000000000000000000000000000000000000C00020100000000800000007400010110010612110116121301066A1401166A1601065517011655190106EA1A0116EA650116E966010AE921010A1222010A6A23010A5524010AEA450116EB46010AEB750116EC76010AECD2011670D4010A70D8011671D9010A71E2011672E4010A72E8011673E9010A73F5011674F6010A74140002010200080080000002000400084000FFFFDA0001048A0E05001331580021220000B70000000800000014326B001E210000BE0000000200000013337E001E220000C5000000000200001334910020210000CC000000000400001335A40021210000D300000000080000010491000204020003040200230602000000FF010492000204030003040200230602000000FF010493000204040003040200230602000000FF010494000204050003040200230602000000FF010490000204010003040200230602000000FF14060F000000FF14060F000000FF14060F000000FF14060F000000FF14060F000000FF050001010000380004030400000024F40000102788130000000000000000C2014E0C01010000550000001027000000020501000606000000000000000000F20105030E00360046005A01A201307500000C003F053A400000409C00000C003F053E000000FFFFFF000C003F05F380000000000000FFFFFF000C003F053E00000000000000483F00003F004400001600000004011F00000004022C00000004033E00000004081B500000040925600000040A35900000041021B0000004112FF0000004182A40010004786900003F004400001600000004011F00000004022C00000004033E00000004081B500000040925600000040A35900000041021B0000004112FF0000004182A40010004F0D200003F004400001E00000004012600000004023000000004033E00000004082240000004092C600000040A37700000041027900000041132C0000004182E00010004683C01003F004400001E00000004012600000004023000000004033E00000004082240000004092C600000040A37700000041027900000041132C0000004182E0001000400000000000102030203000100070500000102030203000100070500000102030203000100070500000102030203000100070500000102030203000100070500000102030203000100070500083000009B0000000730000046250000083100009B0000000731000046250000083200009B0000000732000046250000083300009B00000007330000462500005600000005000000FFFFFFFF0000000058000205010A01000C02040264C0000065C0000066C0000067C00000E8C00000A021000042EC0000102700000C04000000000000000000000000000000000000000000000000000000000000000000000000000000000000C401020500000000000000000000000000000000245053505B4809DF160000000000000000000000400400000000030000000000010000000060000000300400000000000400000000E0000000000000000000000500000000010000000A0300000000000700000040040000400B0300000000000900000008000000E4C90000000000001100000000100100001005000000000012000000005000000020060000000000140000000030000000100300000000002100000010000000000B030000000000230000000050000000000200000000002D00000000F0000000400300000000002E0000000080000000900400000000003500000038000000ECC900000000000036000000004000000070060000000000370000000010000000B0060000000000380000004004000000050300000000003A0000000062000000C00600000000003B0000000003000000220700000000003D00000000400000002507000000000003000000006000000065070000000000420000000008000000C5070000000000010000000000000004010000000000000000000001012C0000C540736900550069000000900100007C99050000000000000000000000000000000000000000006C000201B55D000000000100B55D000008080200B55D000010100300B55D000018140400B55D00001A180500B55D00001C1C0600915D000008089000955D000008089100995D0000080892009D5D000008089300A15D000008089400A55D000008089500A95D0000080897008A060C0001E2011D0900007E3A00007D00080000001B000000000000000000007100000000000000000000000000010000000A0000006C070000F30400003E040000F3040000F30400006B030000F304000004050000040500002A0300000000000000000000000000000000000000000000000000002C010000300100003001000030010000300100006400000030010000340100002C0100002C010000000000000000000000000000000000000000000000000000800000000E0000001E00000001010101010101010100010101010000000000000000000000000000000000009808000098080000980800007E040000980800007E040000980800007E040000A203000014000000AC0D0000AC0D00006400000069000000020000000000000001000000010000000100000064000000640000006400000064000000640000006400000064000000640000006400000064000000000000000000000000000000200300002003000020030000BC02000020030000BC02000020030000BC02000071020000320000002003000020030000190000003200000000000000000000000000000000000000000000001900000023000000190000002300000019000000230000001900000023000000190000002300000000000000000000000000000008000000FBEFFFA7230600005F000000000000000000000000000000790000000000000000000000000000000F000000660000006E006900690073007300730073000000000000000000000000000000FC700000FF0100004B004B000000000000000000A50AA50AF00AF00AF8116810800000000000020000000000000000000DE04D3EFCDEA6BD7498273F000002000000803F000000000CE5243E8CDB083EE145073F010104000000803F000000000CE5243E8CDB083EE145073F010002002289923FBD35B03CF77520BE2731803F1D38E73D01000200331B643FC8EF0D3E000000004A7B033FDAACBA3E01000200BADA5A3F9CE1463E764FAE3FE86A03C03199C23F010002007BA05D3FBB0F003E3945C73D780BA43E0000C03E0200020000000000000000000000000000000000000000000200020000000000000000000000000000000000000000002C016C07E803E803E803E803E803E803E803E803E803E803E803E803E803E8033001F304E803E803E803E803E803E80330013E04B004B004B004B004B004B0043001F304E803E803E803E803E803E8036400F40171026B033001F304E803E803E803E803E803E80334010405E803E803E803E803E803E8032C010405E803E803E803E803E803E8032C012A03200320032003200320032003D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D001D0016C07F3046B033E04F304F304040504052A03D001000303033001FB01F00AF00A280A480D480D480D8813181518151815F401F401F40100000000D0010000010200005B00000800000003040451006B02000000000000000032003C0000009001900190019001900190019001900190011E00DC05800CAC0D5500DC0501000102000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000EC4CA13C6EC0E73C81785D3D73637A3D583934BC1361C33C000000000000000000000000000000000000000000000000000000000000000000000000000000000C010C0101010000000000000C010C0100020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000001E0046000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000CB00000000000000000000000000000000000000190019006F060000C5060000350700006F060000C5060000350700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C8000405000000000000000000000100000000000000000000010200000000000000000000020301000000000000000000030401000000000000000000040504000000000000000000050604000000000000000000060704000000000000000000070000E718E7180002010300000000960000002A000000160000001B000000FF0005000500FF00FFFFFF0001201F0001201F00011421000000000090010000008001000000000000000000000000000000000000000000000000000000000000000000000000006C0003030000021130750000CC55010000200000312C00004C000000200352034605BC0200000000800000000000000000000000000000000000000000000000DC05000084038403460584030000000000000000000000000000000000000000000000000000000000000000F70902044B0057003F061F072B090000000000000100080400200000FF00000000000000000000000000000033000070080472008108000000040400C00000004B345A4146333235424D0000000000FFFFFF00000000001C000000514C01005F4C01005E4C0100604C0100614C01008140010082400100834001008440010085400100864001008740010088400100894001008A4001008C4001008D4001008E400100904001009140010094400100954001009640010097400100A3400100A440010098400100A1400100A8610000000000002501000000000000BA08000040000000090905030D0005000202000208000000050303000500000011040500000001010100010100111800DB0100000A0A00001E0000001E00000043512A020F0000000C000A0A080300011F2A0C001E00000009F1130F1E0000000000000050C30000000000002D04000000000000BA080000400000000A0F0905170008000202000208000000050404000800000011040500000001010100010100121800B60300000A0A00003C0000003C000000455136021E0000000C000A0A090300011F2A0C003C000000C9F1130F3C00000000000000F82401000000000045080000000000003A090000400000000D170D0723000C00040400020C000000050505000C00000021060500000001010100010100151800910500000A0A00005A0000005A000000485146022D0000000C000A0A0C0300011F2A0C005A00000089F2130F5A00000000000000CC55010000000000550A0000000000004A090000400000000F1B0F0929000E000404000210000000050606000E000000218805000000010101000101001718007F0600000A0A0000690000006900000049515002350000000C000A0A0E0300011F2A0C00690000000AF3130F6900000000000000A0860100000000005D0C0000000000005A09000040000000101E0F092D000F0004040002100000000506060010000000210806000000010101000101001818006C0700000A0A000078000000780000004A5156023C0000000C000A0A0F0300011F2A0C00780000004AF3130F780000000000000048E80100000000007E00000000000000EA090000400200001426130D3900130006060002140000000607070014000000318A06000000010101000101001B1800470900000D0D000096000000960000004D5166024B0000000C000A0D130400011F2A0C00960000000AF4130F960000000000000040130200000000000704000000000000FA09000040030000152D170F4400170006060002180000000708080018000000310C07000000010101000101001B1800180A00000F0F0000B4000000B40000004F5178025A0000000C000A0F140500011F2A0C00B4000000CBF4130FB400000000000000785D0200000000000F04000000000000FA09000040030000162D170F4400170006060002180000000708080018000000310C07000000010101000101001C1800810B00000F0F0000B4000000B40000004F5178025A0000000C000A0F150500011F2A0C00B4000000CBF4130FB40000000000000020BF0200000000001F080000000000008A0900004003000018341B114E001A000808000220000000070909001C000000318E07000000010101000101001E18005C0D000012120000D2000000D200000052518602690000000C000A12170500011F2A0C00D20000008BF5130FD200000000000000B024030000000000280B0000000000009A090000400300001A3C1F135A001E000808000220000000080A0A001F000000311008000000010101000101001F18004A0F000014140000F0000000F000000054519802780000000C000A14190600011F2A0C00F00000004CF6130FF000000000000000143E030000000000300B000000000000AA090000400300001B412315610021000808000220000000080B0B001F00000031920800000001010100010100201800C60F000016160000FF000000FF00000056519C02800000000C000A161A0600011F2A0C00FF000000CCF6130FFF00000000000000E86E030000000000400B000000000000AA090000400300001D442515660022000A0A000224000000080C0C001F00000031920800000001010100010100221800B3100000171700000E0100000E0100005751A802870000000C000A171C0600011F2A0C000E0100000CF7130F0E01000000000000000000000A000000554C01007CFE000180FE000184FE000188FE0001A0FE0001A4FE0001091C0003C91A0003091B0003FFFFFF0000280000A8498840000004B08002000000000401C0C0C0C0C0C0C0C0C00000008000000080000000FFFFFF0100280000A8498840000004B08002000000000401C0C0C0C0C0C0C0C0D00000008000000090000000FFFFFF0200280000A8498840000004B08002000000000401C0C0C0C0C0C0C0C0D00000008000000090000000FFFFFF0300280000A8498840000004B08002000000000401C0C0C0C0C0C0C0C0C0000000800000008000000000000000400000004F4C0100504C0100514C0100524C0100534C0100544C0100564C0100574C0100584C01005E4C0100604C010000FE000104FE000108FE00010CFE000110FE000114FE000118FE00011CFE000120FE000124FE000128FE00012CFE000130FE000134FE000138FE00013CFE000140FE000144FE000148FE00014CFE000150FE000154FE000158FE000178FE00018CFE000190FE000198FE00019CFE0001A8FE0001B4FE0001B8FE0001BCFE000184FF000188FF00018CFF000190FF000194FF000198FF00019CFF0001A0FF0001A4FF0001A8FF0001ACFF0001ECFF0001F0FF0001F4FF0001F8FF0001021D0003031D00038C1C0003A10400051804000555020005FFFFFF001AA720000500000000002A000000060600040000F425E50F0000000000000000000000000000000000000000060003000000030400000305000000009C000408480448040000080000000800171000000F300000220A4C56500501000F0FDE00204004001042000000000000040000000000000038383043080810696600001B2A8C480000000000C0E03886000000000300000000142F2F22222B2B000000000000000000000000000000001C038E03AA028E03E300AA02E3007100E3007100550171001C0355011C038E03CF0030FFFF0C00F30080019000088001050000000E1000403FFF3F0F2B0000002200000010000000120000000000000016000000000000001800000024400100254001002640010024480100254801002648010024400500254005002640050024480500254805002648050024400900254009002640090024480900254809002648090024400D0025400D0026400D0024480D0025480D0026480D00FFFFFF00100000006754130220314576100000001023754664573201100000001023754664573201100000006754130220314576010000001023754664573201010000006754130220314576010000006754130220314576010000001023754664573201000000000050000303000000000010050000020100000000007D00E40C0008700010270000CC55010024F4000050C3000010270000000000006B0000008000000000000000000000000000000000000000000000006C00040205030E002106400000000000FF0005070C000E0000000000000001030E002106400000000000FF0001070C000E0002020000000004030E001006440000000000FF0004070C000E0000030000000002030E001006440000000000FF0002070C000E0000010000000014000202000000000000000000001000000000002000020114001A00000000000800000004000000000000000000000000000000F4000304010B0000D0074E0C00004E0C10270000FF000500FF000001FF00000060CC050070C600001873010070C60000B0CC0000CCF100000ED40000A4000000E8DF0500203005000000000000000000020000000000F00070110100D084000000000000333381017D1A09963014C902600060004788730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55AA5700F10E00000B0064860100000000000000000058001C000000504349520210407300001800000000035700000003800000474F5020414D44205245563A20782E782E782E782E780078787878007878787800000000B3AC0000A02301002698876FBF7D624A5B78E666660E000701444AA5B25C68A698516B03176CE2731798B82923534B48C262944841CE2952D60E02E7338E6CDB555DABBB5555B6D5BB3B54982AB002802AA88CB14D32ECEDBC1C2A5AA156A33FBFBFFCE6705A6DDEF7DB77DDEF7F6EFBE2F77EEF7C3EFDEFB3DFBDFE09FBBBB592471B6E7BB32BFF5FA115FFFFD3EE4A2B41A963F859F18AC3B48F5F1A53DF9CA2A86514B395C18535F7F8368AFDFCBF385EB8987CBF78B8A8FFFA00E4FFE2EF85C628A978D5757E8C7735C54BAFFFF703E2CFA3AA3C3DE8BE750677BF3FE237AA39E8B174FA0CF9F9F5013106DA2BEFF0E0328AC962EA94C3B18C0B0E5861D5F7FD30F51474B4D4451053116DA2BEFF2C3E43CF529B1FCFDEE3CEFEFEEB15F790FCFDF27CDFEFEEEBD053DF95F9FBA1FF1EE4D5F761E7FF6F67FF8F7EB6E94F7ACFCFD9FFE3DE8B6EAF266E152CF57DEAD5F7ADE77BB857DD2EE55C3ABEE415D3F1DCEF7B4571F0BFEBDFFE35F76AFBB6FCF6F884FC3ED5FA57E3F4A299C987C971FA249BED7C0A65D745A0BE5FD1528309F8B43C3DB2B429E2D0D15BAA4CB611684BAB74C62E8EC3F234969BB5682F8B4572D457AD4582D458AD4592F85711737168AE5A8AF5A8B05A8B15A8B25ED7791737168AE5A8AF5A8B05A8B15A8B25F75BF8B9B8B4572D457AD4582D458AD47FF79BD37BB1737168AE5A8AF5A8B05A8B15A8B25EDB7D1737168AE5A8AF5A8B05A8B15A8B25F8EDEC5CDC5A2B96A2BD6A2C16A2C56A2037AD9BBA5B3774B66EE96CDDD2D9BBA5B36E3DF8A44B54AD15CB515E550AD7B3375176390DFFE54572D457AD4582D458AD4592F76B68B60A956322A2D15CB51296A2BCABC5ACEF16B397E045B37BEF45D3E2D15CB515EB5160B5162B5164BFCA585BB8BF1956DB612D46A961BEA961BEA961BEA961BEA961BEA961BE29C95F9F214BB88B1C6F11638DE22C71BC458E3788B1C6F11638D2BFD01FFF5A3791799CA7EB1C1BF58E0DFAC706FD63837EB1C1BF58E0EF16CDDE2D9BBC5B3778B66EF16CDDE2D9BD65CC5CDC5A2B96A2BD6A2C16A2C56A3CD1965D6A374B1C6DD2C71BE82C6CBE82C6CBCC630D750E8BBFD149AFF3293D7A49361E63CF5DE4A2EB8C1FD183C0AE93A9EAAA702BDC54E7AA702C35B49ADA4C0B0A1A9A6A9C12EEAE5BAB96C12E6D2CD83CBB96772CA68FF1771C28980CFFA593EF7D8ECFCAEFB6BD2E77AFFF389C0AE47E0C9ECBE3D0E9F2360D70BDD27A27FC7E26F653638729EDAE41FD5E37C9F3FB4D95A6EDD789E927C4C5EE7BBFD9A2CA29A3F98AFB3556573EE3BF1AF3396549BDDFFE7DFCB7AF39191F6B7395F4364F627551DF66E28B4741F658712472F59EEA7C3CCFF0D7D1C5D5EC0BFECCAFC5BFA7FF17ED3FC5F95F9DFFD78353DE6E5D4F79AC32B7125FD9A2C661F70C07BB927598A0CF3A3735932BE8D9FEEE0DFDF993C6E7B2485252E80ADC48F372E36F3A4E13A292E8F996FE4FDBF07A7D363F5597ADACBCE076F9CAAD77E5D4FF957CFEAB9ECEFA72D4997F1BA8477FDDE6B9385E0FEDC96B3E1C436CAF8BF6F79C1B87BD776F7B8E90E85EEF7966798EA05C52F3EF3D4EC24F57B2EAFCBBC37F57A7E94D77F57DDEA7C1D1F631BD6F737329FA66CAF32DE92A1B95EC7D8EE168BD177FAEF3D8FD1D6CFAFF2319ED5E1D51F6D839FDB87FAF377CC63F7DA1EF2D753E347CD64FCDD53A939E8CADB5F5719E64BCEFB5CFCF7A361211BD261BCBEEA2677BCEDFDF4E9F31AFF810F566713BA95E8D84D4F6797FAD95D65D76BEF95846F45E6E33A0CE76DE206DFD6A9B7FA7825597F57DD52BEE855F6E15F730AFB9757D94AFBE360556F7D5F76CAFB84AFBF4D5F7E3ABEDEABEE9D5F742AFB9F57DB857DCCABEDA2BEE4D5F652BEF8CAB99FC257DDC2BEED95F7095F7E9ABEFC757DBD57DE895F79B57D9CAFB6CAFB9857DCBABECA57DA7F9B9C8D0AC511D4774565CAEC0A30A76144D12EF2B2654F958C2B3D14BFC95F1BCEAF35CEAF8EE757C7F3ABF56B577654E859F375FEC15DE5515C29CF05894B63FB00B1A057173EB5BD1AB55DFF4FFF90CFFFCFFFFDD3FF1D4CC01B4D0B3CA1598013DA159B0E2BC707BCC80C34A1F73E55487968183A82A90AD2053A856F748562C3A5F161C978E0F3324A3D613C8D3A00C0D3936BC693ECC93E8E1F1E54E060F181E71E5365392F4717A5DA15920E7BA02B3414680A3C37DA80F346568399D1FF961E74AA62BB90DAC42DBFC5F60E7324F78926F3A00DE7FAD11FDF88CE3C1FDC85C9855106A3481DF62008FE9DA6EE69EA29BB9A4A9A3D034348F70A9F9D68F499FA5A4D034779FD1E850CFE868E2DBB9D1E81A533C6993A4A552CB3347534751A40CE4C4D4CF4DF857C6CDF4D7F8F9EA3D052135B9DCCCEB47351514D50AE9F414744854521FDDB4EC2929A973E7D25368E2EFF4400C85876D4B46D3B0A3A8A956E67EA298FA6A2A6A58BA266B279A9FE6EA0EA767BF0AE676D4D507D180F34B51479FD0776D2A4FCF92BE5E98F5A9914BF49DCD1B4A640F8A33E5E99A7739FA7A7A4D1E856AE02D53D368EA541DCCA2A2A3A9A9686D1E8E909A08E9F93A6434717B4483834A2D1071F2B7F7FC064EC7A6A540371C9A14B4BCDAF3B4D5150853800B9840F1E370DCE990A80DAA2E5FB10E2CA3683C0B474421D9B47A4A4A2A3BF1C4407CC88D2AE3F2483C794750D3354D4CD283B9CFABB60DBE7F40D15BBA0A4EF28CAEBF4752853D3D32DC1F17BFE7F3F519FEE68CFA38BD5EEE98331CCE5FEC73F51A30E2469407E7C3AA2FC9062DBFFB6FF16FD413F279FA4A542A28F996E6D1D2D1F36E22DBF5FA3EE3474DA5D1B4CA52F774FA2123F409D1054CE331B358EC7F57D6369B33253A6B9C9E43461CB21DC350194FD03045C95F42852683F42867F32DAABA9C4CCE396F18DAA1FAB50FCBEC50DD4D1CF022D8BA3E3741D52BE31DA068D0694E0F85363CD8E4E46383E87BEFE25FC3977697C34073EBC70725EF9D37238BE444885D6C014D3BEC8E885CA040F74FA8B8C51D05961C2EDDC191C5726244748CEA6163AC258B2F7EF4963C20B1DFFD91221D0707C8E412C6E8E4997D61A244D64F8402A7B91DBFD010144F9B0B5E085A8667DC531C98639CCD5B9C96DD4C4C3963A12ACC73D871C742559697A73EDA38CE59EE370E04319976965684E4B259B391C1EA89A71AD5C0699EA88288C8E982C8E45969C7D00B48C8E70289913C1F1C45B48714FB40D66032B85E5DF1057A0504C90BD1CF66F3602646A80EF4E1707226B56801BF483A741912EF46CB3AD4E391C2FDA385470BF707A89AC3970F1C8EB019FE1FD77F98F9BE0108646FAF49B27BCA6B33E3A34176D4E8B98DA4C88BDB81D6999C8D2B5937619E97EC3B13334755DE3D2072CB6E0EF3399AE21C972CE4AECC81F5BA6FB43E54E6F7079674256099BDBB601C64EE6F13FB03167F68ED291FD2141D4DF5CCD030E83AB6BAD160DF43D0869D5EB72D37B13F12725E192EDDC1AC6A318EDD256E9D873E1C6D39B43D9BA4BE1BBFDA212EC16D97C85C247EEA6001591017F602C450075307239A6AD07A3E64064D808D696EE26B0611700585EADE0E8A3D5B0683E97696DD5E220C6B99AB0392B6893F32186392BA2F571838949D868EB6E8EE6FCD1EC22EB3D7270722350D4A47C60E6BABEED8F1E486EBC31B32EB8C9785D7FA8514F26D7175B56292AABC8339F9D7599434A179D91CE92F74C2EB8D8F00A734CD8CF37F74F2DE399BEDBA83D985B667A83986F1EF78C18D44A3C366FB662E0BD75EC2185DE59BC6265648D6F20CD06464E7D67D01957CA16F910E89C85CB38C2EBB34D5C175CEDAE493EDA6D4E3D261F14ED265F783F713C1D63D50688E25EFF13930CC95189E355C7C9ADD4C3CFE1C161CC5EA5D0F36B9980E53D080DAED2DD93959A13A130BED7BC3CBBF0BB2CDF8DDA385F4BDE1CAD9BB83E5357A4EC8CE3474977130E92F90CCEE7BB52131A5B4391CD81303897EBC4980CEBCCEC0CEC5435775316AE2C07B30E467E11C97D2E66AF5DC170D0D469D9BB469C4B3B0D743AFDDBB83D4EE3EA1EDEDD3A4B7D17B86D0E4B92ED2E169F04742958BD68DF845D6D2B21EF6D590104EEF9EE713EA18B74DEFB4DCF86B765EF2833BB26AA1ADA8319DEE2C4EF870D24CF707A35DCDE3ECC20531F98392C2FA9BF104F624D7CD2B60311603522EB3E61C9F5BDF975BC41203AFF1C23AC190819530C66F410E4E92BE4FC007770C4B769E50192FBBDA024735E385AF1492A7D2EF09D0002B9F2D086D87278AB535FE31D30DD85AE97E974DF82874C01C46FFB0711B1438A40D44EB5600EF5E90E46A599C956357E52A046E4D7FECA64E739D93B59D9718FAD6B3A0A59625277005AA291EE4A7E9758A0A90704086D10C6DFAC8B9E4FB50E15BBC03113BE7DD090CE3DE00A93892C24A5688362736A7E3F582501F297ABF5811ED3572381E9A496D0E4A316A458E9DAA0C206DE7F52913529B7D2A1E50E8BFB722BAC20647521D4A6CDFB326387905372E3F5609D2DC175C90DA27EAF431796653B60E13AA5EC2A591895A43909AB74FBD095D98988589E8F9E3B4975D606A3EC8D145F092E18DED0BAD18DC15EC0825F56389F24419721D071A9F5B8521FC5D18404CD47C9D683F1C9BA915854C5894182395F915F366365A5C0094B6C1F895DA57A6B6F664828E93B72EBB287D6BE1CF9E701B3A7B1C210B6F4CDC35FD529D69E36B0501F5D983D37DD61398CF961CA905C8D205E9DEE3C07A6639F3433E5EB9F0EB8E98720EABB522A21BF1AA3C510A66FD91C71EED365A550980868BA7949AB94D29028A8395629FC63A56452F089ABE902D12489C2588DC18DF708350D089D37D01A8938EA1E066AE47A51D933D189D2E3C9D2FC911427CD81E484B861E2AF0BEE0B0AF184469EA2847A3811E318F1270C61B00E94F4D79ED5E396FBF3F0D5E251E9E7A849C087961D6CBEA235E1A8CFB435FE6DB407C546A9C02CCBD75B269F62B7386C83AA0ED9C53B93362C8A200C40AA3628ECDB9DD25C464A15934312DC27FCC098D42A7AB8F5B56783AB257DFD4A1D3DA4A90C874F831A90C393540E7CD2606F8D6C440BA120741CD35A70271C4CD072410B6C096CFC13721701F1E3BC4E2992136ED58A62AF5F095FAF4058BFC36F46EE4627CC45E7726F08BCEE8FE82318CA31865EBA47E2F944CF2D0C2E7B74FE46E02A1ADB1F850883C8519C950508308CD02A27E5E2C9CA6ED2BAD34D859E806CFA5C62D660B9465ADB912A2CB330DABFCD4DAB53F3ABB51C44FEDD17AE6567BC13D8E09859B6AFD96B888B4B5392CEB5AA16158CCF19DA99DA0A7EFB4B3E6B0BE7227B597227F413F2D26B813C95DBA4B8C725C576970CE9BC2E87782B4EE090BB723950A800227870E323A6E999975BB521617D484D0F4FEED3E191239EF3A4B708F963C8C9D81110BBB4F41A48B709D7864AEFC62775700335A4AACB246FC41A26A8525284BA9A40F6A29A123E5891043CA9D07C013A081BD6DE590E7A58E9B5C7CADF591D5762268F39E30DAD47143BCCE6FB73FA005FD9FC8B8E8F54A210D4A63A43FECBBA7512CDDB7E0D43F540FC28EBD1D7EC2F1C952833AA91ED67CE128BCE8B44FB4684E3AB3538DC5646D0E7DDE34016BE0392E51EE1EEBD324C119706A35A3B4B51C2179BA6ADCC83D72E2319D54725960401B2BBB811DB282BC0F4AF057816EAA881F627ECDD957FEDEE46F47D1E2A53F6EE078E89DF5E77149DF495E730996D196DC99953607F7FB063F9BB00303D077628DECB66CEADB73E38315DA062A24F80D2CA809B0EDC6A3EC4D1A9A6E74197101DB3DFEE4560CD5FCC0D4101133B9BF5264799AB2B80084B503CE537A8AC258FA24EE780418D265C50D6CD47D4AC202060346998660147950C5B3757056BBB1BBD54C92AF8040EA0E7A601B37EAE1907BCF4316E7295ACC3B0E8968A7013B47D79953D0E3CC82E4D022C703A0C4B635FF4C744B174DF6BA5FA434F982313DB93E5047FDD907F0CACC11A8DF2C4978C81F61ADBD7124A2F5394126E9DEE8C9CDA975BB7230D3DC499759B151B83E88813C7CF035691447F778B1447768F712E18D38FE94313F66E87211EEE093A569B8BCCEBB005B4CC431871ADBB8D31ECC515FD01153CCAC3743AF4EC98660E83DC0353F239353BEF3DC5547F1E9068B68C5EAF50A1024C08BC3DBDDF6E9517BB806787141EA60D47B92A1F6A0271FD908947D010229A2EBA74F9B2193902EC9FE017585DFFDC2E951CC5693308103AA1057AB8BEB43AAECC4E41C4E571391B515F7C2D0F6744D3A95AE6C4D6284888F21A828D41812EE8A5CE1695D19F93FC1A899ABD7497267A0EB47A6DCC07B244239E538F6D71B03E53672D349D39A978043E2D710FFDD41CB9E5D72E32B6C4F6065F6D1540D9CFB8AB904DD25C13607CE1942F52D45D1B0023441DBCE02C2B3EC12019BA9932EB6908417400E75EE9C3FA9A74F2D6E2CC7CD0B324180984317AF097BEAB17A5DA8A3F017E75E510ABAE0E896EE1FE34E8964737DD0137A33A043541305D7658F0735D39FC58EB552D118CADB043041CD5C0DC29D644378A2981EF7B6DC2BDEEABD3523984C9DEDA21F42E941E3DE829F29ABD388A1ED3B3B49DA75C015F757FD1E7CA386D53DD1F1E3D2BD3B05143C0024D938A9030978DC36DECC40083A51EE8F7BF98A0B6D22BF06AA950EADB0EEE426AF62EC30E301AB3381C0A6FC12F57EC109C184F259821805A81843323578A3DDD7DB713BA9D97177FFF9898C0A5F4032D3E9C34D5E4D36A439ECFD38F3B9FA510F079C1428943F410169BA938FC68697EC6DC427C1CD7F32146004CB047F9840030E990E6150286B7D1077B05CD39F1AE207CE8D70DC197DB554ABCD9C50FDB509FDE2BD1AE22035EFDCB8BD491EAC9BAE1412975BD9079489A9985EBBE12103C4DBA3AA14691D45BD3A0CAF883C089B5058C3C7D66BC5FA6475439AB42979C92945C2E60AD27963F22F788E6D7B881C135B7EC22B5E8E7A0F6A7B96D45F79E703C40C150FF20AA2F0C4A77C72F5DBD44DF0589939F09F828A8E883839202214FAEF6C4C0E83E156D93302BDF5981CA3AFC0E52A3CB06AEA6157D67449E86D0741CC0254B402857225F1DA40FC4329BDB0F683B17F2A47DBFE44289E093D11332A74103F53A600788BBEFF620CE3DB89066A9B28157ABFCA2907866F4F1C6A3FB09C9F02F0504D422821AA71036A282EA7FC18B9BF5459ED64D45C224CBC5B7241DE7704C993928D0D77A9FE5CC4590368410145BF274D81219ECEB6467665FCEE993851F155C81B6B61649D45767357BF5EFC922B52F7C129EFABC5BE9290709EB8999E4813C5562C1B3D7CA3811E128E04658F4FBCC51455658203B7A7998FE6BD68756C413F67DAC51DE26C913370C0E94104E35E74399A74609876FC420846222812F6ED945CEAC5DF1C25DB693BDD150D8DA4EE9977E0BAB3607903DC0E71B726A56EEE0EA08937ACE01E625BCABFE220256DC9ED00AE4EA80F16312DDF708B07DE254C4AC6C5366D4B3271C54C362E5B9D547E04CBEB2755760246AE6737B516B356166E92B01806EE66AE5CC0DB1ADBC12157E39DC1ABB621875738C756D91F1CE916665A38C62E87809E464DCBF9FC6197D62AE906CE22427EDEFD43A3B8A08B01D87B2C143CCB6D6A24F1C3392CAB39E0299042BFD2EE0E49A0708D57C9122092A290249D8A410509E0C41115F9855166BE75A11548802F7B260436FC89FC472F430D89C8E4E846D1DC1FD2A33363099A6AE41999267FE217E542F879D21140E173983DD81D75B5A6A459D75C625EF45C4FB218935B7EF901394C04D585DF9EAE64E471FD429BE97595A47E0756D7D3536916B46DA26B1E9190AF2896A30413584832B3B8635FEC54A6C2764AD353F0851606A3FBF9240EB53C6212E80768E927C0433A6AC1E2358D7C10B57F58D7C20F5067E301D83F59AFEA143AF4F402F86F944D38D47B5D12722117D2BE385995B84E8C27CB27A6C4866753B61820C2C6B22487D585F238408F29BEA7C20F47F76009B3878E0E7337E515095B3C7DCACCA1CFBC3018579274C2D5D30F78BAE66F1BC71F7810C5BA73E583CCC1F38EA098BDE24CC70C859CF25B99E0C76242280F7E1D713C96D473112C071487D61AD5205A8C45AED01D5574FACE4E7A6F1486A947A0EFBAEFC9E83DA84003475326476836A29C9DD279160B72CEE93CD11E78DA9FDB9B37FBE10BDB99F2F5FE90D0E8AA648E83FA468FB1189E5C0E2EBC6698A7E575C286CDC990823450748DC17AEDE043F53732C4F98366FD6F149A160CBAEAC251BEB178622176F7951951BF033278E513724C4DACD9C991ADF72A17CEECD4766480D076107ABF7E7722EBA0B77DC3393A5FB860D204185DD042404EC82A876D8A3B4197B9843A8F71661301C8C4FDCD9909005A9DB63A26EE2B575F7A8C254D5C83D58F48FABA8C2999AD5C9FDD891AFD84982C4C5CCBFDD7A46099394C028CD6E27FD514375D2138BD466D3D78C1E0361AF5C4C26E93C03353932983930900A5E2A359FDA40D4C78C03181E2C713D7EB9227AFD3224F5A9900F503C58D1F3FA59C68ED5B39727AFD72C1E9F845DB35FE69ACC1AFC1BB64FB9EC8901EACCBAEF7BF84627EF015B27F4B92480FF1ACC91D7A7FE20E290838C28FFB317FF4D47F5C9914E4457725234D6641842FFC4B32661E0D816EF7204268C8F5CA68BCFFC42A685323837366AC8210CB06237C120D31A3864B2E0860F0DAF982435D963D4E1AE946D5DCDFAC4B543009F9A30AFC13A4116AC638C9317D7B650E68BAE3B46730B67496D4D6DD31F1A744B035B750CD0E406D6B97013FBB01FA1C817B303B95C310BE25819009B25E01E61A8F4C280B145409EC7F9287B1E4A87B169A914332EB4AC25FAE21F3F903BD64B3007A2A5AC4B63AC7852A082951A82C982C7197B3620FB0D0FDA0414B480E056F4F82D1D47FFC2430849F3748E719BA07075D2352ABBA757782BC3B4505F6066C457820EF1C165B6F8E244B435AE1D8078C42E15EA4C181C9354008E81AB4788923F332F694C2E3580E01B4228196D0AD926037E1265997A0A5FCE92FCE84113586FF324BF9F0BFF0D4BFA06237F9B0A3DB0EDF44C83C540D1A98C845331E2734182482C900C739834AD79638ED11AFE95ACD0828D86FE9C2FE8FE3130BE38DED501DAFAA744162E1976A3B2D635174B9887B7C3CB2E917D3EA5ECF85ECD5BBC0E682264917D4F1098E183FAC42567BD0C1E40306E1FA89721F7E113133BAC0FBE605F753F19257CEB42962C94BD5A1884B1B00B1980B132D275FC8078F1E443C7A7241E216082FAE0285B4A98FFB8BA0E95E127392A3AEFEF235275DC9782A57E788C9F7EB1E5804393C47BDFB93D8CB129A2C13DD6045B5EDA9F186C03A421B03606E5C0238D2DA3C6F6A7E0EFAC411740450C5E246B1681A6209C3C99A166E810D2C2DE01BB8C3BF9DFB43D621FCE84A5437700E096581C339378DF7C7F3F3CC2C32A91BB4983B1562F119474C372F12950500EF90E3BC47A62F5D96FB4BD74EF2CD7FDC6B1F70CB896D693BE0B0B1E1B306164087A8BF547DB6B585583346B9EA396F0C4129CB13F08B01F02B18F9846A5F6A87CB0F0809891DDCE6009D7F0FDD159427DFFA841801BD6B64FA5C10263F523C22AB766F12270C56FCE4977FE53568BB9B23A2A66464767320DBF3A061AAF3182FD01E644DD84B4A4AF9CA2129E77E7F6AF12ECD7C2FBA20B789F6B318D3BC186C1E30BA3A0D4310571707493867B8C4929DBA408FCE310A9F5E921F49F3A080E3CD4686423F94188F8EA2509A2845EF0CE8E674BB8474E56B577F57C41A5FB5CD8DB087F1FE1300C426702C590C26D12DFC78253FAC1986F8E04134D7CC06398E5CF0313D5A7D2E585633C00E7ACEB5C60A5B26A0F5F18389991BFC6862665826BF2C44BF675AE39E1ADC1A3DD24704A7B6B32084887200ACDB3B1E160814AFC605FEF62D3A918AB2EC081BA639BFC47B53606AB8241E2DB5EBC8C606D8E24BEF06671A3C5E3357F332FCC17026D7679494D728EAFE6E256DCA0C8177D0E65ECE77E84A3DEF17443F8C34A3094B08F6717929303E34BAEC92F3985C43E18B36204F62D40926FD8373FE6C56E7C260191B53085B009A36559B25216C3EF3C8DD98F2DE72FE5778C06B6849A9E32BA20B800B79535C01961019A75B25146FEF8C91855472DC2052718BD58C3164B280D2078048D4D075449B188AB2020209602F94516E3335B36B4CD0A1167EA4E2E8692B39E736757AC9989BF2C74119B7F667977A4D557029166F3D307B0145F370D68B343DFD5A6CC0630452430A0EDF0E554810D0B5C402358B6B8A6A5A49880C4341F006DDFB18F3A2599CDC971525930D0F84315A3D7E6C19DD11C0FAD0BAE249842B288EA65DD2340050957783A5C2BA1818E972ABF59BEEA3400DC5550948676169CD51335DAC28E7612CF7B27AA56EF9E04E09D30D1D18C4FFD8091971832BA21B7A59E833B09D41CB5C4D5C811FE6F43D757CB815050FA41E33FDA0A802E05401FC83C683E50F147F607879EF878D0A21E29FF6878EE8545151B5150F5E0783F001E10B90F1A4C8078D2E5812A89FED0F1779B0F1C5DA87887781E2F3001E3E6B90F1F3E403C7D19657BB2B6C0B7F1C45A4D628DD425D8B38ADE5033A83DB954301FC55827741C039F7A049E4C099652797BEF924CE5161C2AF37D66691658354E5884E7D42131DABCB26C59A18CDEEC7AE238BAD7C528FCB509D379566806D8A6B0EDBD9A1DC1CFB624024993701C9FFE5AF1DEBC0F6859A8C0050166C626B2840400847ED8822C254455016484162024144E5859581A2415B08D60CC5EDF63AD5D0D7E36C052B2A39599C9304AA82577E450F4DF5E4C42288DC4F00E4B78F1162D45B312ABC57B23DE2BE12E0BD2222BA11AEC613D231CEEDAC90D68C84BD8295A5C6B33230624C6E186BC7426017C0D720D49084C5829AA49ACC0260613256CE56138294A32C44321B04E623579F7339667F5008E9C099036E2DDE1B372B587E02FAC5EF2B0151F405804F78AC5E8C718DF6E8337302CC166C8F3E73EB262E9F5DAE42C80C2A63FAC0732EBB9535735FD671C20B77D39619B2A9E724061C743EADE56F7ACCF98856C13531E56CEB33F0A2DB0756133B3D362831F4D99F225D750334B6DC8DE275F9623FA69F4720A4FDA6F688592879910D56688B10539161AE76F91043BDD55FC83344DE04CE2669E748AF9BA9AB7878E795B9E667E284F1DF61839E3A01D0764A682C6A3057AE18AF1DD3B15374372CA498BF59581821BA7B9CC820630106EC497144AFA80753AA07544D0015F1E43980556F00FE881638BCF95525AF5905E58A719E9AE3D83EF93EF78959C3C00648F8FBF4C05FA5FC0C8E9C" +RX6600M_64K = "55AA59E9CE2A00000000000000000000000000000000000068030000000049424D5724A60000000000000000000000042037363132393535323000000000000000000000000000003C0300000000000031302F32392F32312030323A3431000033000000E9C12B00E9CB2B00000010020A0000000102400000C3FF73000000006E0064006400000064009808100E100E7C9905000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000160034030FFC40010000000008000800088041CDF32842CDA31080E80D7EE378CEFFFF000000000000207EC5FF010000003003200040F33F87B26A3E473EC7003000003300EE735305800F082104FF3C00C0220202C2C60165D57C0E00E0810A001E9008F03EA76824002100210021000100C0024C02910102001880014006061E11075280180C0E120000000EF00200000008008001060008401810F000000020000000040000001C040000C8000000300000000000F8084000000000000004000000001C32000840D83800000000000000000000000000000000E42709401018006400000000C09807000040FA01A000240000008010900118000008008081470100F0A01F11300000000002FCCF818D8300043131332D44353334303130302D583030004E4156493233005043495F45585052455353004744445236000D0A4E415649323320584C2041302038474220353030652F3530306D20302E39562F312E3335562020202020202020202020202020202020202020202020202020202020202020202020202020200D0A000D0A200D0A0028432920313938382D323032302C20416476616E636564204D6963726F20446576696365732C20496E632E0041544F4D42494F53424B2D414D44205645523032302E3030332E3030302E3033302E30313639373400484330322E726F6D2020202000323337323830342000353932313134202000202020202020202000414D445F4E41564932335F4435333430315F584C5F41305F47444452365F3847425C636F6E6669672E68000000902C00020341544F4D00C0D32BE6028D033A02062C000000000210360E680370A9C0030000C80A000000000300504349520210FF7300001800000000035900031400000000414D442041544F4D42494F5300688BF7DF00000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000004A000201000000009808AC1F2C0874200C046804C009B40A0000A00C6406A820AC0C302100001C21040D0000240D380DA407A41F440D00000000D006940D000000000000301F0000000000005C00020100000000000000000000000000000000000000000000000006000000C8000000000034210000B80B000000007D0000010000000000FFFF000000000000000000000000000000000000000000000000000000000000000000FC010504100038004C0060019C014800307500000C003F053E000000409C00000C003F053E000000FFFFFF000C003F057721000000000000FFFFFF000C003F043E0000000000000000000000483F00003F004400001600000004011F00000004022C00000004033E00000004081B500000040925600000040A35900000041021B0000004112FF0000004182A40010004786900003F004400001600000004011F00000004022C00000004033E00000004081B500000040925600000040A35900000041021B0000004112FF0000004182A40010004F0D200003F004400001D00000004012600000004023000000004033E00000004082240000004092C600000040A37700000041027900000041132C0000004182E00010004683C01003F004400001D00000004012600000004023000000004033E00000004082240000004092C600000040A37700000041027900000041132C0000004182E0001000400000000000102030203000100070500000102030203000100070500000102030203000100070500000102030203000100070500000102030203000100070500083000009B0000000730000046250000083100009B0000000731000046250000083200009B0000000732000046250000083300009B0000000733000046250000560000000500000046000000300000005000000030000000FFFFFFFF000000006C000201B55D000000000100B55D000008080200B55D000010100300B55D000018140400B55D00001A180500B55D00001C1C0600915D000008089000955D000008089100995D0000080892009D5D000008089300A15D000008089400A55D000008089500A95D000008089700D40004040000000024F400001027881300000000000000005E014E0C01010000550000001027FF730003050100050500C891050080EA822B0000000000000000000000005400010000000000000000000000000010113D104D1C0200004500000000000000000000000000000000AA0A22220200AAFA03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000880001048A0E0400133148001E2200006C000000080000001432510021210000730000000200000013335A00202200007A000000000200000C3163001E21000081000000000400000104910002040200FF0104930002040400FF0104940002040500FF0104920002040300FF14060F0E0000FF14060F0E0000FF14060F0E0000FF14060F0E0000FF6C0003041E00031450C30000E425000000540400000000004C0000002003A302E204200300000000000000000000000000000000000000000000000000000000DC0500008403840346058403000000003E400000000C00006160010000000080808D5B000000000000000000280104090101E000000101000101E0000102010000010000020301000001000003040100000100000405000000010000050600000000000006070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFF0000000201030000C0303201000040000000200000002000000000800100FF0005000500FF00FFFFFF00FFFF000001081F0001081F000110210000001F00FF00000010000000640000000000000000000000E204E204E204E204A00FA00FA00FA00F0100000010101010101010101010101010101010204E000000000000000000000000000000000000000000000000000000000000000000000000000000000000F4000305010B0000F4014E0C00004E0C10270000C891050020A1070004000000005307003075000000770100B0810000B0CC0000CCF1000048A3000000000000203005000000000048E80100D43000001200000000000000281D0100A009010000000000333381017D1A09963014C9026400640028841C00281D010088130000020000000200000002000000020000000100000001000000018000007D1F1E9605800000FFFF0F007100FF2F4D0000A50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000EC0102060000000000000000000000000000000024505350413225C01B000000000000000000000040040000000003000000000001000000003000000000050000000000030000000030000000300500000000000400000000B2000000000000000000000500000000010000000A0300000000000600000000B0000000F40900000000000700000040040000C00B0300000000000900000008000000880C0000000000001100000000C200000020060000000000120000000050000000A40700000000001400000000300000001003000000000021000000C0000000000B030000000000230000000050000000E40A00000000002D00000000A0000000400300000000002E00000000C000000060050000000000330000000060000000E00300000000003500000010000000900C000000000000360000000040000000F407000000000037000000001000000034080000000000380000004004000000050300000000003D0000000020000000C4090000000000400000000030000000A40A0000000000420000000010000000E40900000000004900000000C0000000400400000000004A00000000C2000000E20600000000004C0000000010000000D40A000000000071000000008001000044080000000000010000000000000003010000000C000000000000000000000C000201000000000000000058000206030A02000802040264C0000065C0000066C0000067C00000E8C00000A0210000036B01001027000007040200000000009F3000000000000000000000000000000000000000000000000000000000000000000000200002020000000000000000080000000400000000000000000000000000000014000202000000000000000000001000000000000C000202080000000000000050000303000000000020060000C20000000000006400E40C0808700010270000A08601001008010084B20000E4250000000000002BF108000000000000000000000000000000000000000000000000009C110205FD007102750BB10CAD100000000000000408080400200000FF00000000002D0000700804720000000000000000000008000000040000040000000A08000040000000200000FF00000000004700007008047200000000000000000081080000000400000400C0000A08000040004B345A4146333235424D5F504152544E45525F4249544C414E440000200000FF00000000003800007008047201000000000000000006080000000400000400C0000A080000400048353643424D32344D49520000200000FF0000000000390000700804720200000000000000001F080000000400000D00C0000A08000040004D5436314B3531324D333243001600000050500100525001005E5001006050010072500100007E0101047E0101087E0101107E0101147E0101207E0101447E0101487E01014C7E0101507E0101547E0101587E0101787E0101807E0101887E0101A87E0101BC7E0101FFFFFF0005001B0000000707000013000C001400080017000600030000000304000003059C00F40246044844171060000000000038382A430505106B7700001B2A8807000000000000D138800000009000000001000000004AA54A05FFFFFF0105001B00000006060100130000000000080017000600030000000304000003059C00B40246044844171080000000000038382A430303106B6600001B2A8809000000000000E128860000059000000501000000004A258703FFFFFF0205000000000008080000130000000000080117000600850000008104000081059C0CF402460448841710600002030000383824430808106C7700001B2A88070049490020C2D02C800000029000000201004000004AA54A0500000000A86100010905090DDB011E0F030503050202050305080203010A0A0A0A02040A0A1E031E48E80100030C0A0202040A101501020904040100000000000000000050C300010A050F17B6033C1E040805080202080406080203010A0A0A0A02040A0A3C053C90D00300050C0A0202040A111B010209040701000000000000000000CC5501010F051B297F066935070E080E04040E0608100203020B0A0A0A02040A0A690969FCAC0600090C0A0202040A172801020A040C01000000000000000000A086010110051E2D6C07783C080F090F0404100608100203020C0A0A0A02040A0A780A7820A107000A0C0A0202040A182B01020A040D0100000000000000000048E80101140626394709964B0A130C130505140709140203030D0D0A0A02040D0D960D96688909000D0C0A0202050D1D3301020A0410010000000000000000004013020115072D44180AB45A0C170E17060618080A180203030E0F0A0A02040F0FB40FB4B0710B000F0C0A0202060F1F3C01020B041301000000000000000000785D020116072D443E17B45A0C170E17060618080A180203030E0F0A0A02040F0FB40FB4B0710B000F0C0A0202060F1F3C01020B04130100000000000000000020BF020118072D443E17B45A0C170F17060618080A180203030E0F0A0A02041212B412B4F8590D00120C0A02020712214301020B041601000000000000000000B024030118072D443E17B45A0C170F170606180A0C1802030310140A0A02041414B414B440420F00140C0A02020814234C01020C041901000000000000000000588603011A0844443E17B487122415170909220C0E2402030311170A0A02041717B417B4882A1100170C0A02020917265401020C041C0100000000000000000020A003011A0848443E17B48E132416240909240D0F2402040311180B0B02041818B418B49D1A1200180D0B02020918285902020D041E0100000000000000000058B803011A084A7018122593142617260A0A260D1028020403111A0B0B02041A1A251A25F59412001A0D0B02020A1A295B02020D041F010101010000000000007427000209050305C0000B0B020202020202040303080203010A020A0A020304080D020E44C5000004050A020204040D0E010208040206000000000000000000204E0002090505087C011414020303030202040303080203010A030A0A0203040818031BA086010004050A020204040D0F0102080404060000000000000000004C4F00020905060982011515030403040202040404080203010A030A0A0203040819031C7C8C010004050A020204040D10010209040406000000000000000000E45700020B050609AC011717030403040202040404080203010A0C0C0C020304081B041F74B7010004050A02020408101401020B040506000000000000000000C8AF00020E050C1257032D2D050706070202080505080203010A0C0C0C0203040836053EE86E030005050A02020408131701020C040906000000000000000000084C01020F06162250065555090D0B0D03030E07070C0203020A0D0D0D020304086609740E7E060009090A02020408152401020E041106000000000000000000A08601020F0619286C0764640A0F0C0F03030F07070C0203020A0D0D0D02030408780A8720A107000A0A0A02020408152701020E04130600000000000000000048E801021407203247097D7D0D140F140404140808100203020A0D0D0D0203050A960DAA688909000D0D0A0202050A1A32010210041906000000000000000000F04902021607263C220B96960F1712170505170A0A140203020B0F0F0F0203060CB40FCBB0710B000F0F0A0202060C1C38010211041D0600000000000000000020BF020218072C465C0DAFAF121B151B05051C0A0A140203020C1212120203070ED212ECA0BB0D0012120A0202070E1E410102120422060000000000000000006CDC02021A072F4BEB0DBCBC131D171D06061D0C0C180203030E130A0A0203080FE113FE1C4E0E001D130A0202080F2144010212042306000000000000000000400D03021C073250D80EC8C8141E181E06061E0C0C180203030E140A0A02030810F0140E40420F001E140A020208102346010213042606000001000000000000D430000309050406EE001408040301020202040304080203020A140A0A02030A1214020F20A107000A0A040101010A0B0D01020304030F000000000000000000204E0003090505097C01200A040401040202040304080203020A140A0A02030A1220021820A107000A0A040101010A0B0F01020304030F000000000000000000A86100030A05070BDB01280E040401040202040304080203020A14040402030A1228031E20A107000A0A040101010A0C1101020204040F00000000000000000050C300030B050D15B603501A050802080202080405080203020A14040402030A1250053C20A107000A0A040101010A0F1701020204060F000000000000000000F82401030D05131F91057826080C060C02030C0506080203020A14040402030A1278085A20A107000A0A040101010A111F01020204090F000000000000000000CC5501030F0516247F068C2C090E080E02040E0607080203020A14040402030A128C096920A107000A0A040101010A1323010202040A0F000000000000000000A0860103100519286C07A0320A100A0F0204100607080203020B14040402030A12A00A7820A107000A0A040101010A1426010202040B0F00000000000000000048E80103120520334709C8400D140E1303051407080C0203020C14040402030A12C80D9620A107000A0A040101010A162E010202040E0F000000000000000000F04902031505263D220BF04C0F18121703061808090C0203030C14040402030A12F00FB420A107000A0A040101010A1A3601020204100F00000000000000000020BF020317052C475C0D1858121C161B04071C090A100203030D14040402030A121812D220A107000A0A040101010A1C3E01020204130F0101000000000000006CDC020318072F4CEB0D2C5E131E161D04081E0A0B100203030E14040402030A122C13E120A107000A0A040101010A1D4401020204140F010100000000000000400D030318073250D80E40641420181E0408200A0B100203030E14040402030A124014F020A107000A0A040101010A1D4701020204150F010100000000000000000000000500000055500100A07E0101A47E01017C7E01012C7E0101FFFFFF00001E00000000000000010001E1498C4030830100FFFFFF01001E00000000000000010001E9498C4050850100FFFFFF02001E00000000000000010001E9498C4050850100FFFFFF03001E00000000000000010001E1498C4030830100FFFFFF04001E00000000000001000100E1498C4030830100FFFFFF05001E00000000000001000100E9498C4050850100FFFFFF06001E00000000000001000100E9498C4050850100FFFFFF07001E00000000000001000100E1498C4030830100FFFFFF08001E00000000000000010001E1498C6030830100FFFFFF09001E00000000000000010001E9498C6050850100FFFFFF0A001E00000000000000010001E9498C6050850100FFFFFF0B001E00000000000000010001E1498C6030830100000000007E0000004F50010051500100535001005450010056500100575001005850010073500100765001005D500100645001006B500100785001000C7E0101187E01011C7E0101247E0101287E0101307E0101347E0101387E01013C7E0101407E01015C7E0101607E0101647E0101687E01016C7E0101707E0101747E0101847E01018C7E0101907E0101947E0101987E01019C7E0101AC7E0101B07E0101B47E0101B87E0101C07E0101C47E0101C87E0101CC7E0101D07E0101D47E0101D87E0101DC7E0101E07E0101E47E0101E87E0101EC7E0101F07E0101F47E0101F87E0101FC7E0101007F0101047F0101087F01010C7F0101107F0101147F0101187F01011C7F0101207F0101247F0101287F01012C7F0101307F0101347F0101387F01013C7F0101407F0101447F0101487F01014C7F0101507F0101547F0101587F01015C7F0101607F0101647F0101687F01016C7F0101707F0101747F0101787F01017C7F0101807F0101847F0101887F01018C7F0101907F0101947F0101987F01019C7F0101A07F0101A47F0101A87F0101AC7F0101B07F0101B47F0101B87F0101BC7F0101C07F0101C47F0101C87F0101CC7F0101D07F0101D47F0101D87F0101DC7F0101E07F0101E47F0101E87F0101EC7F0101F07F0101F47F0101F87F0101FC7F0101021D0003031D00038C1C0003A10400051804000555020005FFFFFF001A27200000002A0000040000F425E50F0000000000000000000000000000130040001800000017000000170000001700042000F0000000000000080000000800133080002E0A4C521C0FDE00204004700000F001011086000000000000000000000000000000000000000000000000000000000000000000C004000000000000030000000000000000141A1B1B1B1C1C00000000000000000800C00200080000000000001800045000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001C038E03AA028E03E300AA02E3007100E3007100550171001C0355011C038E03CF0030FFFF0C00F30080019000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000088001050000000E1000403FFF3F0F000000002B000000220000001000000010000000000000001400000000000000EC00000000000101000000000400000001532476103254768800000012385076A9040000000000000100000010325476103254768800000010325876940A0000000000000400000010325674123054768800000010325476980A0000000000000100000010326475103264758800000010325476980A0000000000000400000010325674123054768800000010325476980A0000000000000100000010326475103264758800000010325476980A0000000000000400000001532476103254768800000012385076A9040000000000000100000010325476103254768800000010325876940A00000000000000000074000402050312001A0660000000000094005A00FF0005070C000E00000008000000010312001A0660000000000094005A00FF0001070C000E0000020800000004030E001006440000000000FF0004070C000E0000030800000002030E001006440000000000FF0002070C000E000001080000000800010100000000C8000201D6098002A000E0012D00100060000A0002000000000008080600123CA00F2003000158021C00280080000100040000000000000000006A3C6419000440010003260018008800030006000000000000000600553C302A00050802C003280060007000010003000000000000000000E93C302A0005980100042A0030007000010003000000000000000000EA3C8F2F7805D0011A04270058009000030004000000000000000200EB3C483F40063002B00432004000C000010003000000000000000000EC3C3400020100000000002A002A00002A2A2A00002A002A2A15002A2A2A15151515153F153F15153F3F3F15153F153F3F3F153F3F3F7400010110010612110116121301066A1401166A1601065517011655190106EA1A0116EA650116E966010AE921010A1222010A6A23010A5524010AEA450116EB46010AEB750116EC76010AECD2011670D4010A70D8011671D9010A71E2011672E4010A72E8011673E9010A73F5011674F6010A74140002010200080080000002000400084000FFFFA6091200022203C3090000DE4400008300140000001C000000000000760000000000000000000000000001000000010000000D000000A00A0000000500006B03000008070000E204000095050000E204000095050000B0040000C1040000C10400002A030000C1040000000000000000000000000000F4010000A201000061000000F40100003C020000B20200003C020000B2020000A2010000E7010000E70100002C010000E701000000000000000000000000000081000000100000001E0000000101010101010101010101010101010100000000000000000000000000000000A00A0000A00A0000000000000000000000000000A00A0000B6030000B603000014000000100E0000100E00006E0000006E000000010000000100000001000000010000000100000064000000640000006400000064000000640000006400000064000000640000006400000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F4010000F4010000000000000000000000000000F4010000A2020000A202000006000000190000007206000019000000320000000000000000000000000000000000000000000000190000000A000000190000000A000000190000000A000000190000000A000000190000000A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000060000005F005F005F005F0098089808D606D606100E100ECA08CA08000000000000000000000000000000000000000000000000000000000000000006000000FFFD7FA3E33704006400000000000000000000000000000064000000000000000000000000000000640012000000000064006E006400730073007300730000000000000000000000010000000A000000000000000000000000000000B80B480DD80E6810C01200000C380000FF3F0000640064000000540B540B640000000000B80BB80B681068108000000137003700B80BB80BB80BB80B05000500000002000000803F000000003815093EF8C2E4BD2E1C283FA101000000000200BE9F8A3F48E17A3EF758FA3A560EED3EEF55AB3EA201000000010400713D9A3FC3F5283E00521B3B0AD7033FC347943E61000000000002000000803F00000000B9A5D53ABE9FDA3E5C8F823EF401000000000200F6289C3F08AC1C3EA3401F3B4260053FEE5A823E3C02000000000200D34D823F83C04A3EFD82DD3A52B8DE3E8D5D823EB202000000000200F6289C3F08AC1C3EA3401F3B4260053FEE5A823E3C02000000000200D34D823F83C04A3EFD82DD3A52B8DE3E8D5D823EB202000000000200F90F993F9A99993E890C2B3B713D0A3F1B64823EA201000000000200EC51983F7368113E17F11D3B2FDD043F8D5D823EE701000000000200EC51983F7368113E17F11D3B2FDD043F8D5D823EE701000000000200D9CE373FAC1C3A3F0000000044698F3EC139133F2C01000000000200EC51983F7368113E17F11D3B2FDD043F8D5D823EE7010000F401A00A00000000000000000000000000000000000000000000000000000000B20295050000000000000000000000003C02E204000000000000000000000000A201000500000000000000000000000061001E02A4026B03A201B004000000000000000000000000E701C104000000000000000000000000E701C1040000000000000000000000002C012A03000000000000000000000000E701C104000000000000000000000000F401080700000000000000000000000000000000BE30813F1DE64B3D9F7678BDBE30813F1DE64B3D9F7678BDBE30813F1DE64B3D9F7678BD541D623FD578E93DA392BABD1FD7263F6DE7FB3DBEF62C3EA00A0000000500006100000008070000E204000095050000E204000095050000B0040000C1040000C10400002A030000C104000000030303080700004C01FA01F00A800C8C0A480D480D480D8813181518151815BC022003BC02010001025D00010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000EC107D100E109F0F300FC10E520EE30D740D050D960C270CF60AC4099308620700000000030201000000000000000000000000000003010451006B023700460090019001900190019001900190019001900190011E009808100E100E00006400F40100000100010200000000000000000000030000000000000000000000000000000000000000000000000008AC9CBD4260053F377102BF0000000000000000000000008195C33D58E2813D705F87BD0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000960096000101000000000000960096001900190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001540B2000000039B420407958683EF0A720417AA5CC3E00000000540B540BA00BA00B4C0064006400000000000000000000000000000000000000B507000081080000900900003E040000B3040000DA07000000000000000000000000000000001A0101F5AD80190868010105481CDC030B000000C0D0E0F0A50101AA1900000074010105FFFFFFFF320101AF0000008E01000000170000001700000092572B050000010065006E00740001006F00690064000100670061006900010020006100740001004D0065006D0001007200790000006E0000000000246D4CAF0001008E040000001700000017000000E2012B05000054005F004D0045004D0031002000010001006E000100430001006E00010072006F006C0020000100610001006E0000006E0000000000DE6C5AAF0002008C010000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E0666506651665266536655665666570E1FA314008C0E0800B220E8201C0BC07514A20200665F665E665D665B665A66596658071FCBE83A1DE80C1B32D28D3E3C03895516E8C30CE8860CE81C1A7519E8D10DE8B002E809180BC0740CE83900E83B43E87D0CE89043E8A900B480E8011B8AC766C1E0108AE3B02066A324A6E8B002E8900CE82E1D665F665E665D665B665A66596658071FCB2E8B1E0800833EE52B007504891EE32B1E2E8E1EDB2B9CFA66C706080165F000F0C7064000062C891E4200C706B401062C891EB601C7067C000674891E7E00C7060C011978891E0E01C706A804386E891EAA042E8E1EE32B8BC3A33A6EA34A6EA35C6E9D1FC3504D4944062C6A5E000000A000B000B800C000000000BB0000E8E30B66C1E810A36E03BB0B00E8D60B8D3E3C0366894518C300E85C1C80FCA07505E83D00EB1180FC4F7505E87531EB07E8CA36EB02B401E8631CCFE83A1CE82000EB02B401E8551CCBE82C1C80FC4F7505E84F31EB07E8A436EB02B401E83D1CCB1E0666506651665266536655665666570E1F3C0475292EA11A0066C1E0102EA118008D3637028A3C80EF30B3028BEC895E0C89460466C1E810894600E92B013C057518E8B219E885192EA108008BEC895610894600895E04E90F013C067532E80A0B66D1E08BEC894614BB0200E8140B884618E8FE0A6689460CBB0900E8040B33C0668946002E8B166E03895610E9D9003C0B75240ADB7511B98000BB02008BEC895E0C894E14E9C0008AC7E8D300E838010F84AC00E9B1003C17751DE8910D7406E88309E88712E81307E8780A33C98BC88BEC894E14E990003C1875450AFF7510E8180D8BEC89460CE84F07894E10EB78E85C0D756BBB004CE8B81A660BC0746023C9745CE8300DBE004CE8A20A7451BE004CB040E8F10AE8E10C8BEC89460CEB473C82751A80FB0575150AFF750AE8260C8BEC894614EB30E8030C741BEB293C8E751580FF01740880FF02750B80C10280C102E84D0CEB108BECC6461902EB0F8BECC6461901EB0732E48BEC886619665F665E665D665B665A66596658071FC3518AC8B80100D3E059C3E8D31AC30C01C3005051B0B6E643B033E642B005E642E4618AE00C03E6618AC4B9C800E8C716E6615958C3001E560E1F803E0200807605C6060200808A0E0200C1E109FCC60621000033F632E4AC02E0E2FBF6D4FEC4882621005E1FC300570BC0750FE81E087425E82400F6C30474F3EB118BC8E8E51423C17412E81100F6C304740A5F8D5C28E891050BF6C35FC350E8D307E837140AC97411E8C2113C00751FE8CB018AEBF6C50474158D9CA600E877190AC0740AE88804740533C9E85105E8AC0158C351B95F00E8D01559C351E81A007514E869013C01750DB92000E82700750580C901EB0233C959C353E83919663D00FFFFFF750E83C304E82B19663DFFFFFF0075005BC3505232D2E81A1902D002D466C1E81002D002D483C304E2EC0AD25A58C3E83E078D9CA401E8E900A9DD0F7452E83801F6C308740C8D5C28E8DD007504E86200C3B502E8FC00E81F01F6C30474218D5C28E82201E86D037405B503E8E400E84104E8FA0080FD007538B502E8E600C38D9CC001B80055E85C0DB502E8D600C3A90200741DB504E8B900E8DC008D5C28E8E400E82A16E8BC008D5C28E8D800E80104C3B501E89B00B502E8A800E8B900F6C30475108D9CC001B80055E8170DB502E81700C3E8AF00B5028D5C28E857007502B501E80400E8C903C38D9CAC02B102E8E317B502E86C00C38D9CA401E82100E87500F6C30474128D5C28E87800E85D00E859078D5C28E8BE15B80200E80EFFC3B94800E87E14C35381C30501E8021824063C025BC3E810003D0104750A5383C318E8ED175BA801C35383C312E8E2175BC38D9CAC0232C9E87B17C38D9CAC02E8CF17C38D9CAC02B101E86917C38D9CAC02B101E87717C366508D9CA001E8B1178AD86658C3555751B90800BD0000BB08002BD9D1E38D584EE89617E8E301E2EE8D5C4BE88B17B903008BD0BF37318555067417668B058D9A4C02E8D4168B45048D9A5002E8E21683C50883C708E2DF8D7C5EB9040033ED8BDFE855170BC07409E85D01E8970083C51C83C712E2E95532EDE86FFF8D5C28E84EFF3D0104751083C314E82C172470C0E8048AE8E854FF5D8D9CA600E81A170AC074468D9CA800E80F173C02753B66C1E8103C0472338D9CA80032E403D88BFBB904008BDFE8F1160BC07415E8F900A98000750DE82E0083C51C555883F870730883C712490BC975DA595F5DC3200358023C000100000400033C0000088002E0013C002000518D1DE8AE168D9AC001E81F168D5D02E8A1168D9AC201E889008D5D05E894168D9AC601E87C008D5D08E887168BC88ADC66C1C8108AD080E20F8AF0C0EE048AECC0ED068AFCC0EF0480E70380E40FC0E4040AD480E23F80E4C0C0EC020AF48BC38D9ACC01E8C4158D9ACA018BC1E8BB158D9ACE0133C08AC6E8B0158D9AD00133C08AC2E8A5158D5D0CE827168D9AD201E80F008D5D0FE81A168D9AD601E88B1559C38BC88AD466C1C8108AF080E60F8AE8C0ED048BC1E8721583C3028BC2E86A15C38D5D11E8EB158AE0BA0600C0E807740380CA808AC4C0E80324033C02750380CA403C0375128AC4D0E8A801740380E2FDA802740380E2FB8BC28D9AD801E82915C33C01743D8BD00FB6C6243F043C8D9A5002E814150FB6C283C01FC1E0038D9A4C02E804150FB6DEC1EB06C1E3022EF7A797322EF7B795328D9A4E02E8EA1483C508C310000A000400030005000400100009008D9CA600E857150AC0741F8D9CA80051B103E81800590BDB7410E8411532C0663D00030C0075030BDBC333DBC3665052E82B153C02753980FC03723466C1E81032E43C04762A03C38BD083C3043BDA731FE80A158AE080E4E0C0EC05241F38CC740832E4FEC003D8EBE30BDB5A6658C333DB5A6658C3518D9CA600E8E0140AC0741B8D9CA800B112E818007410E8CE1466C1E81080E4077504B001EB0232C00AC059C3665052E8B5143C70752B66C1E81032E483C30503C38BD03BDA731AE89D1438C8740D66C1E81032E483C00303D8EBE80BDB5A6658C333DB5A6658C35766518D9CC0018BFB8BDFE83D000BC9740B668BD1E8C90B83C71CEBEC66595FC3505351B90700538BDAE8531483F8005B740966C1E3108BDAE8CC10595B58C35153B90700E8D9105B59C366505383C306E82C145B8BC866C1E1105383C302E81E148BC85B6658C38D9CA00132C9E8B313C357B92000E8B4105FC35351E8190032E48AC5C1E002598D9C780103D8E8EF135BC3B102E80700C3B101E80100C3538D9C9801E895135BC333C9E80D11753C66BA22F0FFFF6633C0E83C0333C0E81A02741CA9DD0F74F6E81BFAF6C3047505F6C31874E9E81F0050E8A0FA58EBDFE80B007508E820008BC1E890FAC36650E81F038BC86658C3665266BAFFFFFFFF33D0E8F402665AC3505352E8E0FF750933C0E8C7018BC8EB1083F902740BE87B0680FB01750383E1FD5A5B58C3566633C066BA00F0FFFFE89E0566C1E610E8A8010BC07403E80500E87E055EC35051E86C015032C0E89002E8CC00E87F008D9C0803E81C1338E8740A8AC5E87312B002E8750258E8790066518D9CA401E80113668BC88BD38D9CD002E8F512663BC17516538D9CAA01E8E8128BC88D9CD602E8DF123BC15B7405B002E83C026659E871FEE8FF016625FF0F0000595866BAFFFFFFFF33D0E8110533D2C35366508D9CC802E8AD128AC4E8081266585BC35366508D9CC802E89A128AE0E8F51166585BC38D9CC802E88A1232C0E8E511C366525232ED83C102B80600F7E16633C98BC85A51668BC28ACF66F7E166C1E0035966F7F1668BC8665AC353E8480166C1E210E836018D9C9801E848128AE88D9CAC02E83F128AC8E88E0B8AEB5BC3575353E8D6FFE814015BE8A00B746180FD00752953E819FE668BD0E854FA8ACD51E825FE8AFD59E888FF5BE808126625FFFF0000663BC8733432DBEB3380FD027511E82C018D1FE8EC113D7440761E32DBEB1D80FD03751553E8C100B303E8080B5BE8D1113BC2760432DBEB0380CB015B5FC3538BF0E8F00C23F074098BC6E8740B8BF30BDB5BC351E831FEE80F0059C35150E8D30C8BC858E8020059C35753E8040D0BC074098B1D83C7023BC375F78B1D83FBFF740B83C70285D974F28BC3EB0233C00BC05B5FC333C0E8C3FF7413E8A0FF8BDEB90001E8CE0D83C608E8380CEBE8BBF046B85AA5E8C210C35366508D9C0803E83F118AE866585BC35366508D9CC802E82F118AC866585BC35366508D9CC902E81F118AC866585BC3538D5C14E812118BD05BC35366508D5C16E805118BD066585BC35366508D9C1803E8F510668BD066585BC35366508D9C1C03E8E410668BD066585BC35366508D5C10E8D4108BC866585BC3538D5C08E8C7105BC35366508D5C0CE8BC108BD066585BC38D5C04E8B010C3518D5C0432C9E84A1059C38D9C1803668BC2E8FA0FC353518AE8E8DCFF24FD0AE8E8DCFF595BC353665051668BC8BB0000E81C0A23C20BC1E81E0A5966585BC3BB0000E80A0A6625DF0F0000C380FA14C3E8AC0CE83A0CE8180C7506E8950BE81C0CC3B001C355B301E8020E5DC3665366BB04000000E8050033C0665BC35566C1E310BB0000E8E50D5DC3E83A0DBB0A00E8C509C3BB0A00E8B509668BC8E8270DBB0A00662BC1E8AF09C3E829000BC97422E8A6FCE81D076651668BC8BB0700E88D09662500FE0000660BC1E88A0966590C01C332C0C3E849020BC97506E855FCE84F0251E84EFC66C1E61033C0E810FE7409E8E70275F633C8EBF25823C866C1EE10C3A8107563E8DC0666C1E61033C0E8F6FD7435E8D3FD50518BC8E8D70185C15958740D50E8CFFE24033C035875E0EB0C50E8C2FEA8015874D5E8CCFC50E8830758B500E8B7FEEBC6E8A901E89BFDE81301B100E83D0766C1EE10E88006A820753BA8407503E80F08E867060AC07500E88201E874FDE86FFEE8CFFE7514E847020ADB740DE881FCE83907B500E86EFEEB08E82007B501E864FEE8CE00C3E83F0AC3E89BFDE89F0633C9E884016633D26633C0E86FFEE87307E85200B1FFE8770033C0E842FD740AE81FFD50E8130758EBF132C9E8A601E8060A0BC0C3E860FDE864066633C066BA00F0FFFFE8150151B101E86E0159E8460733C0E80AFD740AE8E7FC50E8CC0658EBF1E8F206C3B80200E8D6FC7421B80200E8F7F4F6C304750AE8BC0983E0FDE8BF09C366B802000000E8F1FDE83DF6C3E825087413BB0200E80B088AE1E80F08E8CC060C01C30C01C351E80B08740C8ACCBB0200E8EF078AC48AE159C3B001E80806C3B000E80206C3E8740B7518E87C00E86EFC80F901750E51E83006B501E874FD59E8DDFFC3E8D3FF51E83D06B500E863FD59C3B80200E847FC7403E86106C3E8C6043BC17C358BC1E80605E819078BC851E83605E8FA0459E80C073BC872028BC18D5C04E8540D32C9E8410AFEC138C172F78D5C04E8CB0D0BC0C333C0C3BB004833C08AC2C1E00203D8C353BB0300E852076625FF0F00005BC35366506651668BC8BB0300E83C076623C2660BC1E83C07665966585BC36650BB0500E8250725FF0F8BC86658C3E8AC0823C86650BB0500E810072500F00BC1E811076658C3665053BB0600E8FC06A8107509BBF046E8510D3D5AA55B6658C3665053BB0600E8E20680F101C0E10424EF0AC1E8DE065B6658C3536650BBF846E8270D8AC1E8820C66585BC353BBF846E8170D83E0015BC3536650BB0700E8AA06F6C4016658B0067402B0085BC353BB0700E8960680E4FE80FD06740380CC01E891065BC36650BB0600E87E062440C0E8068AD86658C3C3506651E809FB83EC048BEC6633C06689460033D28DBCC0018D1DE8B50C0BC0743D8BDFB000E82D01742F8BDFE86EF8668B46003BC87F10722066C1C81066C1C9103BC87F0EEB1266C1C81066C1C9103BC872068BD766894E0083C71CEBBA0BD2740A8D9CA401E80AF8E988006633C0668946008DBC4C0233D28D1DE8540C0BC074586650E875F43C01665874053D400673436652668BD08D5D04E8350CE85D008BCA665A742F568D9CA4018BF1E8E2F75EB000E89F00741D8D1DE8150C8BD98B470666C1E0108B4702663B460072068BD16689460083C708EB9F0BD27416568D9CA4018BF2E8AAF75E538D9CBA0133C0E8570B5B83C4040BD2665958C3575351E85E073B5502751866C1CA108BDA66C1CA103B5D0675090AC0740E3A451B740983C71CE2DE33D2EB028BD70BD2595B5FC3516652E82A073A651A75090AC0740E3A451B740983C71CE2ED33FFEB000BFF665A59C3C366516652535366C1CE108BDE66C1CE10E86A0B5B668BD0E82EF73BCA720A663BCA7205E821F9750232C05B665A6659C35657E8A7FF8BF7E803F75F5EC36650536651525756518BDEB90800E8A207598D5C088BC166C1E0108AC2E8800AF6C20375248BC1247FBF0E3E803DFF0F840F013A05750D668B450166B900050200E9960083C705EBE3F6C20174258BC1E870010F84EB0033C98ACC8AD00AC075128D5C086625FFFF000066C1E008E82F0AEBB28AE18ACA80E10CC0E90280E2F0C0EA048AEA80FC70722A80FC75732580EC708AC432E450E830028BD85838D8720533C0E99C00E82802E8940A0BC00F849000EB1B32C0E8E6FE0F8485008D5C168B05E8F3098B450666C1E0108B450250E8910366C1E1108BC8588D1CE8C10966508D5C04668BC1E8B609665866C1E8106681E1FFFF000066F7E166508D5C06E83E0A8BC8665866D3E066C1E810408D5C14E8A409BF6E3E83F9017411BF763E81F901017408BF7E3E83F90275158D5C0C668B05E86A098D5C10668B4504E860090BDB5E5F5A66595B6658C3004001900101400190010280029001038002900104400190010540019001068002900107D0025E010D400190010E800290010F80025E011080025E01118002E001128002E00113800290012320049001322004B002332004C002628002E001FF050A050505000000050B060505000000081008080800000056E8070780E4013B04740983C604E2F733C0EB058B44020BC05EC3575350E8EA0683E90AE8E00083F8057603B80500D1E003C8FBA583C602E2FAB8FFFFAB585B5FC36653516652E84100E8770066C1E810660FB7D866B80000000366D3E86633D266F7F3E83A02665A59665BC3536650BB0700E8A8020AC066585BC3536650BB0700E89902A80166585BC35366508D5C06E8E9088AC866585BC351B90800E8640559C3538D5C08E8D30824035BC38D5C09E8C908C35153B1018AE88D5C08E860085B59C3538D1CE8B3085BC3538D5C04E8AA085BC3538D5C16E8A1085BC353665233C0E8C2FC665A751BE834007416E815004083F805730DE8E20748E80F00668BC2E8D8075BC3BB004EE87008C3BB084EC1E00303D8C3BB004EB93200E8D004C351E8E2FF0BC074128BC8BB084EE84C08663BC2740783C308E2F30BDB59C3C3C3008AE8BB20DF3C01740232DBE81928E8F1F666B8800600086633DB8AD98ACDE8B600C366516652665766B88005000866BB004C00006633C98BCEE89B00665F665A6659C366B8800700046633DB8BDEE88600C366B8800800046633DB8BDEE87700C366B8800900046633DB8BDEE86800C3C366B8800A00046633DB8BDEE85800E8FFEFF6C3027403B000C3B001C366B8800D0000E84100C366B8800F00046633DB8AD9E8320066B8800F00046633DB56E8F1028B5C205EE81E00C366B800000000E8140066B8800C0004E80B00C366B800010000E80100C383EC1C8BEC6689460066895E0466894E086689560C66897E10668976148CD066C1E0108BC4BB4200E8DB0283C41CC3518D5C14E82E0766C1E010E8020059C383EC108BEC663DF0FFFF03760666B8F0FFFF03668BC86633C0668946046689460066894608B00188460966B8000000D0660BC16689460C8CD066C1E0108BC4BB3500E8820283C410C33DC07F7604B8C07FC383C03F24C0C353516650B9687480FB03740BB9A08C80FB007503B978696633D28BD16658595BC36652B30066F7C20000FFFF751380FD13740E80FD147409B30380F9037402B302665AC3E8860666C1E8100BC0741C5383C306E877065B3DE001720F5383C318E86A065BA88075030C01C332C0C35383C34CE826065BC35383C34CE878055BC356E8B1018B4438837C20005EC3BB005083F8027432BB005483F808742ABB00583D80007422BB005C3D0002741ABB00603D00047412BB00643D0008740ABB006883F840740233DBC357E85600740A8B5D02E808000BDB5FC333DB5FC352510BDB741B56E8360103DE5E33C98A4F01803FFF74083A07740603D9EBF033DB0BDB595AC35651E8150132C98D7C083B5D04740C83C710FEC13A4C0672F133FF0BFF595EC35651E8F50032C98D7C083B1D740C83C710FEC13A4C0672F233FF0BFF595EC35651E8D60032C98D7C083B450C740C83C710FEC13A4C0672F133FF0BFF595EC35750E8DBFF8AC38B5D02E86EFF585FC35332C9B301E8E8FF74038A4F020AC95BC35332EDB302E8D7FF74038A6F020AED5BC350578BDAE868FF750433DBEB0DB0148B5D08E834FF74F28B5F025F58C366505351578D1C6625FFFF0000E86D048BD8E83B0023C37431E875FF0BFF742A6650668B45048D5C0CE851046633C08B058D5C04E846046658E885FFE893FF8D5C086633C08BC1E833045F595B6658C356E818008B44045EC356E80F008944045EC3C3BF5C038B3D8B7D0CC3BE5C038B348B7430C31E0E1FBF5C038B3D8B7D0E83C7041FC3BE5C038B348B7410C3BF5C038B3D8B7D2683C704C3BE5C038B348B741AC3BE5C038B348B743AC3BF5C038B3D8B7D0A83C704B90700C3E80100CB83EC068BEC8946006633C0C646040089460206528AD38BC5E8B1165A0783C406C3E880FF66F7451001000000C350E873FFB8010009451058C3E868FF884510C3665383EC488BEC66895E0066894E048CD066C1E0108BC4BB0000E8A3FF83C448665BE84901E88301E80E00BB0400E88EFD66C1E810E8C1FFC383EC0C8BEC8CD066C1E0108BC4BB1600E874FF66BB0000000083C40CE80CFF668BC366C1E80866894528668BC366C1E0186689452CC3E8F2FE668B5D08668B4D0CC356E825FF668B44085EC3B005C3B001C3B005C3B008C35366506633C0E8E40283C304E2F866585BC332C0C3FCE8740366C1CB10E8CD0266C1CB106681C304000400E2E9C3FC66ADE8B90283C304E2F6C3FCE84F0366AB83C304E2F6C36650B86400E810006658C36650B850C3D1E1E803006658C352F7E18BCAE8400166538BD966C1E3108BD866B80CAC0500B20066EFB20466ED33C96603D8730A66ED66A90000008075F666ED663BC3730C66C1E8103BC172048BC8EBED665B5AC36652E8FB0066B80CAC0500B20066EFB20466ED6633D266BB6400000066F7F3665AC3665053BB0700E85DFC2500023500020BC05B6658C36651B301E86800E823FE6633C98B4C0866C1E00A0BC97503B9000C662BC1E80CFE66894404894C086659C3561E0E1FE8FBFD668B440466C1E00A1F5EC3C3BE5C038B348B741E83C604B91C00C356E8C7FD8A6C335EC356E8BEFD8A442480FD007405886C330C028844245EC332C0C3665383EC048BEC8CD066C1E0108BC466895E00BB0600E8CAFD8BEC668B460083C404665BC383EC0C8BEC8CD066C1E0108BC4BB1600E8ABFD8BEC668B5E00668B4E00668B560883C40CC366B8C8910500C3BF5C038B3D8B7D12C366508CC83D00C0750CBAC303EC8AF00AF67402EB0E532E8B1E1400B220E8D6008AF45B32D266ED6658C32E8B1E1400C30BC9751A4B7416433BDA77098BC88BC22BD2F7F391F7F38BDA8BD12BC9C33BCA721A75103BD8770C2BC38BD82BC92BD2B80100C32BC92BDB9387CAC355562BF68BEE03DB13C97211453BCA72F577043BD876EFF813F64D7820D1D9D1DB2BC31BD1F572F003F64D780CD1E9D1DB03C313D173F1EBDF03C313D18BD88BCA8BC633D25E5DC39350920BC07402F7E2910BC07404F7E303C858F7E303D1C3526650B4808AC766C1E0108AE38AC224FCBAF80C66EF66585AC352BAFC0C66ED5AC352BAFC0C66EF5AC3E831FF9CFAE8CEFFE8E5FF9DC3E824FF9CFAE8C1FFE8E0FF9DC35266536650E806006658665B5AC366C1E31066C1EB0EE8D7FEE8BF01C3665066C1E31066C1EB10E828FE6603D86681CB000000806658C3526653E80400665B5AC36650E8AAFEE8D4FFE88F016658C35251665366508ACB80E3FC80E103C0E103E88DFEE8B7FFE8590166D3C8585066D3C0E867016658665B595AC3665051C0E103E8530066D3C88AC566D3C0E8A8FF596658C3665051C0E103E83B0066D3C8598AE86658C3E85F00C3E844FE66C1E31066C1EB0EE80B01C3E835FE66C1E302E80001C3526653E80400665B5AC3E820FEE84AFFE8EC00C35166528AEB8ACB80E103C0E10380E3FCE8D9FF80F9007414668BD083C304E8CBFF83EB046692660FADD08ADD665A59C3526653E89CFF665B5AC3526653E8A1FF665B5AC36650555266508BEC8B560C895608E8C4FDB20066ED6689460A66585A5DC36650555266508BEC8B560C895608E8A6FD2EA1AD03B218EFB20066ED6689460A66585A5DC3665055528BECE889FDB200668B460A66EF8B560889560C668B4604668946085A5D66586658C3665052E866FDB21866ED2EA3AD035A6658C366526653E853FD6681CB00000080E81B00665B665AC366526653E83DFD6681CB00000080E81E00665B665AC36681FBFF00000077048AD3EB09668BC3B20066EFB20466EDC36681FBFF00000077048AD3EB0A6693B20066EF6693B20466EFC3C3C3558BEC53518BF0268B5C14268B4C16E828008D66FC595B5DC353518BF0268B5C2433C9E81400595BC3558BEC53518BF0268B5C08268B4C0AEBD5525657C80600008BF0894EFE8CD08EC0268B7C268A450132E4B920002BC8B8FFFF8BD0E306D1EAD1D8E2FA8A4D0332ED8BFAE306D1E0D1D7E2FAF7D0F7D72621441C26217C1E8CD08EC0268B7C268A4D0132EDB820002BC18BC866C746FAFFFFFFFFE308D16EFCD15EFAE2F88A4D0232ED8BC38B56FEE306D1EAD1D8E2FA2346FA2356FC8A4D0332EDE306D1E0D1D2E2FA2609441C2609541EC95F5E5AC3558BEC53515257508BD8268B7F268A450132E4B920002BC8B8FFFF8BD0E306D1EAD1D8E2FA8A4D02884EF6C646F7008BFA8B4EF6E306D1E0D1D7E2FA2609471C26097F1E8D66F85F5AE9F1FE535152578BD88CD08EC0268B77268A4C0132EDB820002BC18BC8BEFFFF8BD6E306D1EAD1DEE2FA268B7F268A4D0232ED8BC6E306D1E0D1D2E2FAF7D0F7D22621471C2621571E5FE9CC0553568BD88CD28EC2268B7726803C007473803C0175588A440132E426837F1E00754C263B471C75468A046BF0038A8458A7260147268CD08EC0268B7726803C0974178A0432E46BF0038BC3FF9456A7268B77268A0432E4EBD18B44012629472626FF4726268B471C268B771EEB1A268B77268A0432E46BF0038A8458A726014726E980FF33C033F68BD65E5BC3535152568BD88D7735268B54022689571426C7471600008BF0268B472033D226014714261154168CD18EC126F6472F060F850A0526807F34007522268B7716260B7714751026D1670826D1570A26D1670826D1570A8BC3E87911E9E104268A473432E4058000992689471C2689571E8BC3E8FFFEE9C70453568BD88D7735268A440232E4C1E002268B37268B3403F0268B4708268B5F0A26890426895C025E5BC35352568BD88CD08D77358EC0268A540280FA407321268B770232F6C1E202268B740403F2268B5708268B470A26891426894402E97F0080FA417512268B4708268B570A2689471026895712EB6880FA407512268B4708268B570A2689470C2689570EEB5180FA42750A268B470826894722EB4280FA43750A268A470826884733EB3380FA467512268B5708268B470A268957182689471AEB1C80FA47750A268B470826894724EB0D80FA487508268B4708268947205E5A5BC353568BD88D7735268A440232E42689471426C7471600005E5BC353568BD8268B7702268B74028A0432E42689471426C747160000268B5F0226FF47025E5BC353568BD8268B7702268B74028B342689771426C747160000268B5F0226834702025E5BC353568BD8268B7702268B74028B048B74022689471426897716268B5F0226834702045E5BC35352568BD8E870FF8BF3268B4718268B571AD1EAD1D8D1EAD1D826014714261154168BC3E82B10E94AFF538BD8268B5F0226FF47025BC3538BD8268B5F0226834702025BC353568BD8E86BFF8BF3268B572033C026015714261144168CD28EC2268B472E32C080E4063D0002750732E433D25E5BC3268B472E32C080E4063D000474EB26807F340075088BC3E82F0F5E5BC3268A473432E42689471C26C7471E00008BC3E8EAFC5E5BC3538BD8E8E2FE268B4714C1E002268B1F268B1F03D8268B07268B57025BC3364E2B4E414E904E474E5F4E794E844E8A4E5351568BD8E8B0FE8CD28EC226837F1600752226837F1440731B268B7702268B5F14C1E302268B440403D8268B07268B5702E97B05268B7714268B471683C6C083D0FF757183FE08776C03F62EFFA4C84D268B4710268B5712E95405268B470C268B570EE94905268B4722EB4B268A4F3332EDBB010033D2E306D1E3D1D2E2FA8BC3E92B05268A4F3332EDB8010033D2E306D1E0D1D2E2FAF7D0F7D2E91105268B4718268B571AE90605268B4724EB08268B4720EB0233C033D2E9F30453568BD8E8F4FD8BF3268B4718268B571AD1EAD1D8D1EAD1D826014714261154168BC3E8160E5E5BC333C033D2C353568BD8E8EBFD268B7714260377228B048B54025E5BC3538BD826C6472B04E8ABFD268B4714268B57165BC3538BD826C6472B01E8BBFDEBE9538BD826C6472B00E8D2FDEBDC538BD883C335268A5F01C0EB0380E30732FF03DBFF97E6A65BC35352568BD8268A472C32E48BF003F08BC3FF9402A7268947082689570A268A472D32E48BF003F08BC3FF941AA72689470426895706E917FD5351568BD8268A4F2B32ED8BF18A8C4AA7E30A26D16F0626D15F04E2F6268A4F2B32ED8BF1C1E6028B8C2AA78BB42CA726214F0426217706268A4F2A32ED8BF18A8C52A7E30A26D1670426D15706E2F6268B4F08268B770A26894F1426897716E9CA0353515256578BD88CD08EC0268A472B32E48BF0C1E602268A472A8BF88A8D52A732ED8B842AA78B942CA7E306D1E0D1D2E2FAF7D0F7D22621471426215716268A472A32E48BF08A8C52A732EDE30A26D16F0A26D15F08E2F6268A472B8BF0C1E6028B842AA78B942CA7262147082621570A268A472A32E48BF08A8C52A732EDE30A26D1670826D1570AE2F6268B4714268B5716260947082609570A5F5E5A595BC35351568BD8268A4F2B32ED8BF18A8C4AA7E30A26D16F0626D15F04E2F6268A4F2B32ED8BF1C1E6028B8C2AA78BB42CA726214F0426217706268A4F2A32ED8BF18A8C52A7E30A26D16F0A26D15F08E2F6268A4F2B32ED8BF1C1E6028B8C2AA78BB42CA726214F082621770AE9BB025152578BD88CD08EC026807F2B00741A268A472C32E48BF003F08BC3FF9402A7268947082689570AEB10268A472C32E48BF003F08BC3FF940EA7268A472D32E48BF003F08BC3FF941AA726894704268957068CD28EC226807F2B00744A268A472B32E48BF0C1E602268A472A8BF88A8D52A732ED8B842AA78B942CA7E306D1E0D1D2E2FAF7D0F7D2262147082621570A8BC3E8F9FD268B4704268B5706260947082609570AEB10268B5704268B4706268957082689470A268A472C32E48BF003F08BC3FF94F6A65F5A59C353515256578BD8268A472A32E48BF08A8C52A732EDE30A26D1670426D15706E2F68CD08EC0268A472B32E48BF0C1E602268A472A8BF88A8D52A732ED8B842AA78B942CA7E306D1E0D1D2E2FAF7D0F7D22609470426095706268B4704268B5706262147082621570AE956FE558BEC5152508BD8268A472C32E48BF003F08BC3FF9402A7268947082689570A268A472B8846FA8BC3E8DAFC2689470426895706268A4F2D32ED8BF103F18BC3FF941AA726894714268957168BC3E844FF8A46FA32E48BF08A8C4AA7E30A26D16F1626D15F14E2F6268A572B32F68BF2C1E6028B842AA78B942CA72621471426215716268A472A32E48BF08A8C52A732EDE30A26D1671426D15716E2F6268B4714268B5716260947082609570A268A472C32E48BF003F08BC3FF94F6A68D66FC5A595DC3518BD8E856FC268A4F2B32ED8BF18A8C4AA7E30A26D16F0626D15F04E2F68BC3E8AEFE268A4F2C32ED8BF103F18BC3FF94F6A659C38BD8E822FC8BC3E855FC268B4704268B7706260947082609770A268A472C32E48BF003F08BC3FF94F6A6C353568BD8E8F5FB8BC3E828FC268B4704268B7706263147082631770A268A472C32E48BF003F08BC3FF94F6A65E5BC35351568BD8E8C5FB8BC3E8F8FB268B4F04E30A26D1670826D1570AE2F68BC3E846FC268A4F2C32ED8BF103F18BC3FF94F6A65E595BC35351568BD8E88FFB8BC3E8C2FB268B4F04E30A26D16F0A26D15F08E2F6EBC8528BD8E872FB8BC3E8A5FB8BF3268B4704268B5706260147082611540A8BC3E8F1FB268A472C32E48BF003F08BC3FF94F6A65AC3528BD8E83EFB8BC3E871FB8BF3268B4704268B5706262947082619540AEBCA51528BF0E81EFB8BC6E855FC268B4408268B540A268B5C04268B4C06E8B1F22689440C2689540E5A59C3528BD0E8F5FA8BC2E82CFC8BC2E85C085AC35152578BF0E8E1FA8BC6E818FC268B4408268B540A268B5C04268B4C06E8F0F12689440C2689540E8CD78EC7268B4408268B540A268B5C04268B4C06E8D1F126895C1026894C12E910FD528BD0E899FA8BC2E8D0FB8BC2E8D3075AC351528BD8E886FA8BC3E8BDFB8CD18EC1268B4708268B570A263B5706750E263B4704750826C64730015A59C38BD0268B470A263B47067208750A263B5704730433C0EB03B80200268847305A59C35152578BD8268A472C32E48BF003F08BC3FF9402A7268947082689570A8CD08EC0268A472B32E48BF0C1E6028BF88A8D4AA732ED8B842AA78B942CA7E306D1E0D1D2E2FAF7D0F7D2262147082621570AE94DFC558BEC51525783EC068BD8268A472B32E48BF0C1E6028BF88A8D4AA732ED8B842AA78946F48B842CA78946F6E308D166F4D156F6E2F88B7EF48B46F68946F8268A472C32E48BF003F08BC3FF9402A7268947082689570A8BC3E847F926894704268957068BC7F7D08B56F6F7D2262347082623570A268947142689571626217F088B46F62621470A8CD18D77358EC126803C197312268B4F04E30A26D1670826D1570AE2F6EB10268B4F04E30A26D16F0A26D15F08E2F626217F088B46F82621470A268B5714268B4716260957082609470A268A472C32E48BF003F08BC3FF94F6A68D66FA5FE9A3FC528BD8E8FDF88BC3E834FA268B4708268B570A268557067506268547047405B80300EB03B80100268847305AC3535152568BD8268A4F2D32ED8BF103F1FF941AA72689470426895706268A472B32E48BF08A8C4AA7E30A26D16F0626D15F04E2F6268A472B8BF0C1E6028B842AA78B942CA72621470426215706E9BDF9528BD8E8AAFF268B4704268B5706268947182689571A5AC351528BD8E891FF8CD08EC0268B7702268B7402813C5A5A745B268B7702268B7402803C6375E1268B770226FF44028BC3E821F8268947082689570A8BC3E8FAF726894714268957168CD18EC1268B5704268B4706263B470A75AD263B570875A7268B7702268B04260347148BDE268947025A59C3268B5F0226834702025A59C352578BD88CD08EC0268B7702268B74028A540184D2750826C747220000EB2F80FAFF7514268B7702268B3F268B34262B750226897722EB16268B07E8E3028BF08AC232E403C003F08B0426894722268B5F0226834702025F5AC38BD82680672FF9268B7702268B74028A440126884734268B5F022683470203C38BD8268B7702268B74028B740126897720EBE3538BD8268A472C32E424032680672FF9C1E0092609472EE94FF58BD8268B7702268B74028A440132E42689470426C7470600008BC3E84004268B5F022683470202C38BD8268B7702268B74028A440132E42689470426C7470600008BC3E80804EBD653568BD8268B7702268B74028A440132E42689470426C7470600008BC3E80604E986F453568BD8268B7702268B74028A440132E42689470426C7470600008BC3E8F003E963F453568BD8268B77028BDE268B5F028B5F0183C30326015C025E5BC351528BD8268B77022683440202268B07E8A0018BC8268B77288A440132E403C08BF103F0833C00744F268B77288A540132F68BC3E8B00126884731268B7702268B348B4404C1E80832E4247FC1E80232E4241F2680672F07C1E00B2609472E268B37268B472EC1E80BC1E00226010426C647320126894F285A59C3538BD826C64732825BC3578BD88CD68EC626807F2C05740A268A472C263A4730751E268B7702268B7402268B7F02268B3D8B740103F7268B5F02268977025FC3268B5F0226834702035FC35356578BD88CD68EC626807F3001740A268A4730263A472C7520268B7702268B7402268B7F02268B3D8B740103F7268B5F02268977025F5E5BC3268B5F022683470203EBF15356578BD88CD08EC026807F300174E5EBC3558BEC535152578BD8268B7702268B74028A0426C6473D008CD18EC126807F3D087320268B7702268A573D32F6268B7C0203FA8BF303F28A152688543526FE473DEBD5268B7F028AD032F68BF2C1E6028A9477A726015502268B7F288A550180E2072688572D268B7F288A5501C0EA0380E2072688572B268B7F288A5501C0EA062688572A8A9476A72688572C8D66F85F5A595B5DC353568BD8268B77028BC68B744803F08BD88B741E8BC603C383C0045E5BC353568BD8268B77028BC68B744803F08BD88B7420EBE08AC2C31607558BEC5351565783EC408BF88946B8E8D3FF8BD88946E0268B45028B5F2E03D8895EDE8346DE048BC7E89BFF8946E032F68D46B8E8C4FF8AD032F603D28B5EE003DA833F000F8465018846E98066E7F9C646EC00C746D8000066C746D000000000C746F60000C646EA01EB063C010F858C008A56E98AC232E403C08B5EE003D8833F007475268B45028B1F03D88B470432E48BC883C10932ED80E1FE83F9007407D1E933C050E2FD8BC48BD88946BA85C0744183C008268947048AC232E403C08B5EE003D8268B450203078B5EBA2689078B5EBA268B0783C006268947028B5EBA8B76F6268977068B5EBA895EF6C646EA00EB0AC646EA87EB04C646EA838A46EA32E483F8100F8F99008866EA8A46EA32E485C00F8F83008CD08B5EBA8EC0268B5F02895EE08B5EBA268B5F02803F5B754AC646EA108B5EBA268B5F06895EF68B5EBA268B1F8B470432E48BC883C10932ED80E1FE03E18B5EF6895EBA85DB74AC268B1F8B4704C1E80832E4247FC1E802C1E0028B5EB8262907EB918D46B8E8AFFD8AD08D46B8E82E008ADA32FFC1E3028D46B8FF9774A7E972FF837EF6000F85C9FE807EEA1074058A46EAEB0232C08D66F85F5EE90BFEC353518BD8268A4F0432EDE883E8595BC353518BD8268A4F0432EDE87EE8595BC3538BD8268A470432E4E6805BC353518BD8268A4F0432EDE860E3595BC3538BD866506652665166268B470866268B571066268B4F0466F7F1662689470C66268957106659665A66585BC3538BD866506652665166268B470866268B4F0466F7E1662689470C66268957106659665A66585BC353578BD8268B5F14E80CEB8BD066C1E810925F5BC353578BD8268B4714268B57168BD887D366C1E3108BDAC1E302E80AEBEBD85352578BD8268B4708268B570A9266C1E0108BC2268B5F14E82EEA5F5A5BC353528BD8268B471C268B571E9266C1E0108BC2268B5F268B5F01E80DEA5A5BC35352568BF0268B5C268B5F01E896EA8BD066C1E810922689441C2689541E5E5A5BC3535152578BD8268B4708268B570A9266C1E0108BC28BC8268B4714268B57168BD887D366C1E3108BDAC1E3028BC1E8EAE95F5A595BC38BD066C1E81092C31E0666506651665266536655665666570E1F0AC07506E80701E9A8003C017506E83701E99E003C027506E85F02E994003C03750BE891038BEC895E0CE985003C047502EB7D3C05750AE80E048BEC895610EB713C067510E88C038BEC895E0C894E14895610EB5D3C077502EB553C08750AE8D0038BEC887E0DEB493C097502EB413C0A7510E865048BEC894E14897E008C461CEB2F3C0B7502EB273C10750AE85A048BEC895E0CEB1B3C117502EB133C15750FE880048BEC895E0CEB07B84F01EB0232C08BEC894618665F665E665D665B665A66596658071FC3501E2E8E1EE32B8CD8A33A6EA34A6EA35C6E2E8E1EDB2BA3AA041F58CB0030312E303000564553410003800300000100000000000000000303149102000021020000885E0000B93F008BDF83C704FC6633C0F366AB8BFBBE8E5EB92200F3A4061F8C4F088C47108C4F188C4F1C8C4F2083C722897F0E0E1FE8BCDFB04F32E4C3565157B910006633C0F366AB5F598CDB061FC705BB00C6450207C745044000C745064000C6451B06C6451801C6451A01C6451E0166C7453E0084D7178EDBE897D8660BC00F84E100061F668945288CC866C1E010B847626689450C81F9FF810F84C20080E503B201538EDBBE804CE868DD5B061F0F84B10053E8E4DF66894512C7451608108D5C0CE88EE86689451F668945368D5C10E880E8668945236689453A8D5C08E872E80AC075035BEB7BE8AFDF66C1E8103D5802740D3D5E017408770AC6451708EB04C645170E2EA1DD2B8945088D5C04E841E8668BC866C1E910D3E0894510894532B008D2E08845198D5C14E825E85B3D0003772F538BD8B8000333D2F7F35BFEC888451D8845358845348EDB5753BE804CE827D85B5F0BC97505061F8325FE32E4EB02B403B04F8EDB5EC30081FBFF8174680AFF75048BC3EB21F6C70875618BCBB201BE204CE88ADC7455E8EBDE0AC0750E66C1E81080E7800AC7E8D502EB3AE84CDA7406E8DBD8E8D1D3BE204CE89ED7742DE8A500E82C0066BB204C004CE8AEDEBE004CE8B1DE66C1E8108BD832C0F6C7807402B040E8CED7B04F32E4EB04B04FB403C3560633C08EC0BE204CE8A9DE8BC866C1E8108BD0C1E90326890E4A04C1EA04FECA268816840426C60651040026C60650040026C60649046226C7064C0400A026C60685041026C7060C0119788CC826A30E01075EC3010F000A000000000040050FFF000102030405060708090A0B0C0D0E0F01000F00FCBAC403B80001EFB90400B401BEEA60AC86E0EF8AE0FEC4E2F6B80003EFB90900BACE0332E4BEEE608AC4EEAC42EE4AFEC4E2F5BADA03ECB91400BAC00332E4BEF7608AC4EEACEEFEC4E2F7C3BE004CE8CBDD66C1E8108BD8E89BDD7505E887DD7504B84F00C3B84F03C3E889DD7453BE004C80FB00741880FB017505E8C7DDEB1D80FB0275108BC1E87ADDD3E88BC8E861D8742EEB0880FB037524E81EDDE864DD8BD8D3E35033C0BA00033BDA7705BAFFFFEB04F7F38BD0588BC8B84F00C332C0C3B04FB403C3000ADB75078AEFE82AD9EB05E80FD98AF8B04F32E4C300E815DD742CBE004CE834DD66C1E810F6C440751D0AFF7505E81E00EB0F80FF017514BB1300E8BFE5D1E88BD0B04F32E4C3B04FB403C332C0C35253665081E2FF02D1E28BC24066C1E0108BC2BB1300E8F0E4BB1200E8EAE466585B5AC39C1E0666506651665266536655665666570E1FE808E6E88AFFE827E68BEC895610894618665F665E665D665B665A66596658071F9DCB0E07BF1CA62E8B0E1AA6B84F00C3000ADB7506B704B310EB2680FB017514E801D8752153B1010AFF7402B100E81BD75BEB0D80FB02750DE82ED88AF8C0E702B04F32E4C3B84F03C30080E3010ADB7505BB0201EB0EE8CAD7750E33C053E85BCB5B7405B04F32E4C3B04FB403C380FC1D7203CD42C31E0666506651665266536655665666570E1F500FB6C4D1E08BF0582EFF943463665F665E665D665B665A66596658071FC30E1F500FB6C4D1E08BF0582EFF943463C36E63849C9D9CBA9C6164BD93EF936294E594739677968099519AD49A3B9BD69C389DE19F7A6628A4226322632263226322632263F8A4FB884F8A2E8E1EDB2B80268704F3508AE080E47FE8C4013BD3750258C3BACC03ECB2B4B13026F6470901740CB2D4B120A8017549B509EB06A8017441B50B53E870410AFF5B750B80268804F0082E8804EB2B58800E87040880261004CF080E100489166304C70685040800C606840418C7060C0106708C0E0E01CD42C380261004CF080E10045853500E1F33C98AC8E8A3D67406E832D5E828D0B208BE404CE8B7D8BE404CE8EDD3585B0F846EFF2E8E1EDB2B0E07E819290E1F5366BB404C004CE8F2DABE004CB020E822D45B2E8E1EDB2B0E07E8222EE82400E82E3DE8D101E88E01E8B43E0E1FBE004CB010E8FED3E8FD2DE8F52DC38BECC6461B00C3E82F00E80A00E8BD00E85200E8620AC35383C30A8B166304B91900B81130EF32C0268A27EF43FEC0E2F75B80C20632C0EEC353BAC40383C305B90400B001268A273C01750380CC20EF43FEC0E2F05B268A4709BAC203EEB2C4B80003EFC38BF383C623BADA0326F64709017502B2BAF606890408741D83C610ECB90400B410B2C080FC1174078AC4EE268A04EE46FEC4E2EFC353EC8BDEB9140032E4B2C08AC4EEFEC4268A07EE43E2F48AC4EE32C0EE1E06E84F2D0BED74091F1EB91000F3A446A4071F5BC35383C337B9090032C0BACE03268A27EF43FEC0E2F75BC3BB38680E078BD30AE47D01C3E80100C333F680FC037F3C4AF6068904107520A08804240F3C027E243C0874203C06741C3C077418B040F6E403D881C30004C3B040D0ECF6E403D881C38004C3B040F6E403D8C380FC077511F606890410750581C30001C381C30005C3BFBA65BEDB6533C9E84E00C304800005800006C0000D80020EC0020F800310C00311400512800513C005624001538A1E490480FB077F1032FFD1E32E8B87F765A26504882666045BC32C3028302D3029302A302E301E3F29302E382575072E035D0133F6C383C7033BFE75EDC31E062E8E1EDB2BF6068704807549833E4C04007442A04904B900202E8B3EE12B26F6473301750E26F6470901751B2E8B3EDF2BEB143C067E0B2E8B3EDD2B268A6737B5406633C0EB0666B8200720078EC76633FFF366AB802687047F071FC32E8E1EDB2B80FB107503E97A0180FB207503E99B0180FB32751FBACC030AC07509EC0C02B2C2EEE9F300FEC80F85F300EC24FDB2C2EEE9E40080FB3575283C807508800E890440E9D300F6068904400F84D0000AC00F84CA00FEC87506E89807E9BA00E9BD0080FB307403E9960050E8303E8AEFBACC03EC8AC8580AC07530B708F6C10175120AED750BA0100424303C307510B702E98B00A0100424303C307502B702802689046F800E890480EB363C017541802689046FB709F6C1017515B70BA0100424303C30741BB7090AED7415B705EB11A0100424303C307508B70B0AED7402B7038A1E880480E3F00ADF881E8804EB293C02752B802689046F800E890410EBB480FB31751B0AC0740B3C01750C800E890408EB0580268904F78BECC6461A12C380FB3375163C00740B3C0175EC80268904FDEBE5800E890402EBDE80FB3475173C00750780268704FEEBCE3C017507800E870401EBC3C380FB367526BB20FF3C017409BB00DF3C007402EBADE80200EBA89CFABAC403B001EE42EC22C70AC3EE9DC3C3B303B700BACC03ECA8017502B701A088048AE0B104D2EC250F0F8BC88BEC895E0E894E16C6461A12C39CFAC7061400E88D8C0E16009DC3281808000809030002632D2728902BA0BF1F00C70607000000009C8E8F141F96B9A3FF0001020304050607101112131415161708000F000000000000100E00FF501808001001030002635F4F50825581BF1F00C70607000000009C8E8F281F96B9A3FF0001020304050607101112131415161708000F000000000000100E00FF281808004009030002632D2728902B80BF1F00C10000000000009C8E8F140096B9A2FF00131517020406071011121314151617010003000000000000300F00FF501808004001010006635F4F50825480BF1F00C10000000000009C8E8F280096B9C2FF00171717171717171717171717171717010001000000000000000D00FF50180E001000030003A65F4F50825581BF1F004D0B0C0000000083855D280D63BAA3FF000808080808080810181818181818180E000F080000000000100A00FF501D1000A0010F000AE35F4F508254800B3E0040000000000000EA8CDF5000E704E3FF000102030405060708090A0B0C0D0E0F01000F00100000000040050FFF0005111C080B14280E182D322024383F0005080B0E1114181C2024282D32383F070C10151A181615131C252F38332E2A2527292A2C2319102325272A2C2B2A29292D32373B3937343233343535312C272F303233343433333235383A3D3C3A39383839393A373431030507090B0B0A09080D11151917151311111213140F0B071010121314131313121416181A191817161717171815141115151617171717171618191A1B1B1A19191919191A181716020304050706060505070A0C0E0D0C0B090A0A0B0B09060409090A0B0B0B0B0B0A0C0D0E0F0F0E0D0D0D0D0D0E0C0B0A0C0C0C0D0D0D0D0D0C0D0E0F0F0F0F0E0E0E0E0E0E0E0D0C00000000000000002818080020090F0006632D2728902B80BF1F00C00000000000009C8E8F140096B9E3FF0001020304050607101112131415161701000F00000000000000050FFF5018080040010F0006635F4F50825480BF1F00C00000000000009C8E8F280096B9E3FF0001020304050607101112131415161701000F00000000000000050FFF0005111C080B252802071B200F14282C0C11252A141E32360F13272C1B203439060B1F2413182C30090D2126151A2E3313172B301F24383D0E182D322024383F0005111C080B14180005111C080B14180E182D322024383F0E182D322024383F0005111C080B14180005111C080B14180E182D322024383F0E182D322024383F50180E0080010F0006A25F4F50825480BF1F004000000000000083855D280F63BAE3FF000800001818000000080000001800000B0005000000000000000505FF50180E0080010F0006A35F4F50825480BF1F004000000000000083855D280F63BAE3FF000102030405140738393A3B3C3D3E3F01000F00000000000000050FFF28180E000809030002A32D2728902BA0BF1F004D0B0C0000000083855D141F63BAA3FF000102030405140738393A3B3C3D3E3F08000F000000000000100E00FF50180E001001030002A35F4F50825581BF1F004D0B0C0000000083855D281F63BAA3FF000102030405140738393A3B3C3D3E3F08000F000000000000100E00FF281810000808030002672D2728902BA0BF1F004F0D0E000000009C8E8F141F96B9A3FF000102030405140738393A3B3C3D3E3F0C000F080000000000100E00FF501810001000030002675F4F50825581BF1F004F0D0E000000009C8E8F281F96B9A3FF000102030405140738393A3B3C3D3E3F0C000F080000000000100E00FF501810001000030002665F4F50825581BF1F004F0D0E000000009C8E8F280F96B9A3FF000808080808080810181818181818180E000F080000000000100A00FF501D1000A0010F0006E35F4F508254800B3E0040000000000000EA8CDF2800E704C3FF003F3F3F3F3F3F3F3F3F3F3F3F3F3F3F01000F000000000000000501FF501D1000A0010F0006E35F4F508254800B3E0040000000000000EA8CDF2800E704E3FF000102030405140738393A3B3C3D3E3F01000F00000000000000050FFF2818080020010F000E635F4F50825480BF1F00410000000000009C8E8F284096B9A3FF000102030405060708090A0B0C0D0E0F41000F00000000000040050FFF386800C0000000000000000000000000586E00C00000000000000000000000001A00F6A500C000000000000000000000000000000000000000001EE810002E8E1EDB2BE8FE36E8D536E8761A1FC32E8E1EDB2BC706870460F9C6068A040BA089040C112417A28904B401E8731EA0890480261004CF800E100420C7066304D4030E1FE8CDC5F7C102007503E866C5B80300E857F4C31E2E8E1EDB2B0E07EB001FC3F6068904087401C353BAC803ECB0FFB2C6EEB2C8803E6304B4752FBEBA6FB9400033DB9CFA8AC3EE8BFBC1EF0383E70303FE2E8A0542EE2E8A4504EE2E8A4508EEFEC34AE2DF9DE98500268A472BF6068904067431B9F800BFB8693C087411B94000BF386B3C3874073C3F7403BF786B33DB9CFA8AC3EE2E8A0142EEEEEEFEC34AE2F19DEB493C0874253C38742E3C3F742AB9080033DB51E8E7D3B90800F7C31000740383C718E8670059E2EAEB20E8D0D3B9100033DBE85700EB13B9400033DB51E828008AC3E8B72FFEC359E2F25BC3002A002A002A002A153F153F153F153F002A003F002A003F002A003F8BFBC1EF0283E70F2E8AB5AA6F8BFBD1EF83E70F2E8AADAA6F8BFB83E70F2E8A8DAA6FC39C8AC3FAEE2E8A0542EE472E8A05EE472E8A05EE47FEC34AE2E79DC300000000000000007E81A581BD99817E7CFED6BAC6FE7C00C6EEFEFE7C38100010387CFE7C381000103810EEEE103800387CFEFE6C10380000183C7E3C180000FFE7C381C3E7FFFF00183C66663C1800FFE7C39999C3E7FF1E0E1E3678CCCC787EC3C37E187E18181E1A1E181870F0603E3E3636F6661E0CDB3C66E7663CDB0080C0F0F8F0C0800002061E3E1E060200183C7E187E3C180066666666660066007FDB7B3B1B1B1B003C66386C6C38CC7800000000FEFEFE00183C7E187E3C187E183C7E1818181800181818187E3C180000181CFE1C180000003070FE703000000000C0C0C0FE0000002466FF662400000010387C7CFE000000FE7C7C381000000000000000000000183C3C18180018006C6C6C00000000006C6CFE6CFE6C6C00187EC07C06FC180000C60C183060C600386C3876CCCC760018183000000000001830606060301800603018181830600000EE7CFE7CEE00000018187E181800000000000018183000000000FE000000000000000000383800060C183060C080007CC6CEDEF6E67C001878181818187E007CC60C183066FE007CC6063C06C67C000C1C3C6CFE0C0C00FEC0FC0606C67C007CC6C0FCC6C67C00FEC6060C181818007CC6C67CC6C67C007CC6C67E06C67C00001C1C00001C1C0000181800001818300C18306030180C000000FE0000FE00006030180C183060007CC6060C180018007CC6C6DEDCC07E00386CC6C6FEC6C600FC66667C6666FC003C66C0C0C0663C00F86C6666666CF800FEC2C0F8C0C2FE00FE62607C6060F0007CC6C0C0DEC67C00C6C6C6FEC6C6C6003C18181818183C003C181818D8D87000C6CCD8F0D8CCC600F06060606062FE00C6EEFED6D6C6C600C6E6E6F6DECEC6007CC6C6C6C6C67C00FC66667C6060F0007CC6C6C6C6D67C06FCC6C6FCD8CCC6007CC6C07C06C67C007E5A181818183C00C6C6C6C6C6C67C00C6C6C6C66C381000C6C6D6D6FEEEC600C66C3838386CC6006666663C18183C00FE860C183062FE007C60606060607C00C06030180C0602007C0C0C0C0C0C7C0010386CC60000000000000000000000FF30301800000000000000780C7CCC7E00E0607C666666FC0000007CC6C0C67C001C0C7CCCCCCC7E0000007CC6FEC07C001C3630FC30307800000076CEC67E067CE0607C666666E6001800381818183C000C001C0C0C0CCC78E060666C786CE6001818181818181C0000006CFED6D6C6000000DC666666660000007CC6C6C67C000000DC66667C60F0000076CCCC7C0C1E0000DC666060F00000007CC07C067C003030FC3030361C000000CCCCCCCC76000000C6C66C3810000000C6C6D6FE6C000000C66C386CC6000000C6C6CE76067C0000FC983064FC000E18187018180E0018181800181818007018180E1818700076DC000000000000001038386C6CFE003C66C0663C18CC7800C600C6C6CE76000E007CC6FEC07C007CC6780C7CCC7E00C600780C7CCC7E00E000780C7CCC7E003838780C7CCC7E0000007CC07C186C387CC67CC6FEC07C00C6007CC6FEC07C00E0007CC6FEC07C006600381818183C007CC6381818183C000000381818183C00C6386CC6FEC6C6003838007CC6FEC6000E00FEC0F8C0FE0000006C9A7ED86E007ED8D8FED8D8DE007CC6007CC6C67C0000C6007CC6C67C0000E0007CC6C67C007CC600C6C6CE760000E000C6C6CE760018003C1818183C00C6386CC6C66C3800C600C6C6C6C67C0000187ED8D87E1800386C60F066F66C00C3663C7E183C18003E63380E633E001C003E613C867C001C0E00780C7CCC7E001C00381818183C00000E007CC6C67C00000E00CCCCDC760000FC00BC6666E600FE00C6E6F6CEC6003E003E6067633D003E0076CEC67E067C1800183060663C000000007C606000000000007C0C0C0000C0CCD8307C360C3EC0CCD8306C3C7E0C180018183C3C180000366CD86C36000000D86C366CD80000228822882288228855AA55AA55AA55AADD77DD77DD77DD77181818181818181818181818F81818181818F818F818181836363636F636363600000000FE3636360000F818F81818183636F606F636363636363636363636360000FE06F63636363636F606FE00000036363636FE0000001818F818F800000000000000F8181818181818181F00000018181818FF00000000000000FF181818181818181F18181800000000FF00000018181818FF18181818181F181F1818183636363637363636363637303F00000000003F30373636363636F700FF0000000000FF00F736363636363730373636360000FF00FF0000003636F700F73636361818FF00FF00000036363636FF0000000000FF00FF18181800000000FF363636363636363F00000018181F181F00000000001F181F181818000000003F36363636363636FF3636361818FF18FF18181818181818F8000000000000001F181818FFFFFFFFFFFFFFFF00000000FFFFFFFFF0F0F0F0F0F0F0F00F0F0F0F0F0F0F0FFFFFFFFF00000000000066DCD8DC66000078CCF8CCC6CC0000FE62606060E00000FE6C6C6C6C6C00FEC6603060C6FE00007ED8CCCCD8700000666666667CC0000076DC1818183800FE386CC66C38FE00386CC6FEC66C3800386CC6C66C6CEE003E603866C6CC780000007EDBDB7E0000067CDEF6E67CC0003860C0F8C06038007CC6C6C6C6C6C60000FE00FE00FE000018187E1818007E0030180C1830007E000C1830180C007E000C1E1818181818181818181818783000000018007E0018000076DC0076DC00007CC6C67C00000000000000181800000000000000180000001F181818F8381800D86C6C6C0000000070D830F80000000000007C7C7C7C000000000000000000001D000000002466FF66240000000000000010000000000000000000000000000000000000007E81A58181BD9981817E0000000000007CFEFED6FEFEBAC6FE7C000000000000006CEEFEFEFEFE7C38100000000000000010387CFE7C3810000000000000000000103838106CEE6C103800000000000010387C7CFEFEFE6C1038000000000000000000183C3C3C18000000000000FFFFFFFFFFE7C3C3C3E7FFFFFFFFFFFF00000000183C6666663C180000000000FFFFFFFFE7C3999999C3E7FFFFFFFFFF00001E0E1E3678CCCCCCCC780000000000003C6666663C187E1818180000000000001E1A1E1818181878F8700000000000003E363E363676F6660E1E0C000000000018DB7E3C66663C7EDB180000000000000080E0F0FCFEFCF0E08000000000000000020E3E7EFE7E3E0E02000000000000183C7E181818187E3C18000000000000666666666666660066660000000000007FDBDBDBDB7B1B1B1B1B0000000000007CC6C6607CF6DE7C0CC6C67C00000000000000000000FEFEFEFE000000000000183C7E1818187E3C187E000000000000183C7E18181818181818000000000000181818181818187E3C180000000000000000000C0EFF0E0C00000000000000000000003070FE7030000000000000000000000000C0C0C0FE00000000000000000000002466FF6624000000000000000000103838387C7CFEFE0000000000000000FEFE7C7C7C3838100000000000000000000000000000000000000000000000183C3C3C3C1818001818000000000036363636140000000000000000000000006C6C6CFE6C6CFE6C6C6C00000000000018187CC6C0783C06C67C18180000000000000062660C183066C6000000000000386C3830767ECCCCCC7600000000000C0C0C18000000000000000000000000000C18303030303030180C00000000000030180C0C0C0C0C0C18300000000000000000006C38FE386C000000000000000000000018187E18180000000000000000000000000000000C0C0C1800000000000000000000FE0000000000000000000000000000000000001818000000000000000002060C183060C0800000000000007CC6C6CEDEF6E6C6C67C0000000000001878181818181818187E0000000000007CC6C6060C183060C6FE0000000000007CC606063C060606C67C0000000000000C1C3C6CCCCCFE0C0C1E000000000000FEC0C0C0FC060606C67C0000000000007CC6C0C0FCC6C6C6C67C000000000000FEC6060C1830303030300000000000007CC6C6C67CC6C6C6C67C0000000000007CC6C6C6C67E0606C67C0000000000000000000C0C00000C0C000000000000000000000C0C00000C0C0C180000000000000C183060C06030180C00000000000000000000FE00FE000000000000000000006030180C060C1830600000000000007CC6C60C1818180018180000000000007CC6C6C6DEDEDEDCC07E000000000000386CC6C6C6FEC6C6C6C6000000000000FC6666667C66666666FC0000000000003C66C2C0C0C0C0C2663C000000000000F86C6666666666666CF8000000000000FE6660647C64606066FE000000000000FE6660647C64606060F00000000000007CC6C6C0C0C0CEC6C67C000000000000C6C6C6C6FEC6C6C6C6C60000000000003C18181818181818183C0000000000003C181818181818D8D870000000000000C6C6CCD8F0F0D8CCC6C6000000000000F06060606060606266FE000000000000C6C6EEEEFED6D6D6C6C6000000000000C6C6E6E6F6DECECEC6C60000000000007CC6C6C6C6C6C6C6C67C000000000000FC666666667C606060F00000000000007CC6C6C6C6C6C6D6D67C060000000000FC6666667C786C6666E60000000000007CC6C0C0701C0606C67C0000000000007E5A181818181818183C000000000000C6C6C6C6C6C6C6C6C67C000000000000C6C6C6C6C6C6C66C3810000000000000C6C6C6D6D6D6FEEEC6C6000000000000C6C6C66C38386CC6C6C600000000000066666666663C1818183C000000000000FEC6860C183060C2C6FE0000000000007C60606060606060607C000000000000000080C06030180C06020000000000007C0C0C0C0C0C0C0C0C7C000000000010386CC6000000000000000000000000000000000000000000000000FF0000001818180C00000000000000000000000000000000780C7CCCCCDC76000000000000E060607C6666666666FC0000000000000000007CC6C0C0C0C67C0000000000001C0C0C7CCCCCCCCCCC7E0000000000000000007CC6C6FEC0C67C0000000000001C363030FC303030307800000000000000000076CEC6C6CE7606C67C00000000E060607C6666666666E60000000000001818003818181818183C0000000000000C0C001C0C0C0C0C0CCCCC7800000000E0606066666C786C66E60000000000001818181818181818181C0000000000000000006CFED6D6C6C6C6000000000000000000DC6666666666660000000000000000007CC6C6C6C6C67C000000000000000000DC666666667C6060F00000000000000076CCCCCCCC7C0C0C1E00000000000000DC6660606060F00000000000000000007CC6C07C06C67C000000000000303030FC30303030361C000000000000000000CCCCCCCCCCCC76000000000000000000C6C6C6C66C3810000000000000000000C6C6D6D6D6FE6C000000000000000000C6C66C386CC6C6000000000000000000C6C6C6C6CE7606C67C00000000000000FE860C183062FE0000000000000E18181870181818180E00000000000018181818001818181818000000000000701818180E181818187000000000000076DC00000000000000000000000000000000001038386C6CFE000000000000003C66C0C0C0C6663C180CCC3800000000C60000C6C6C6C6C6CE7600000000000C1830007CC6C6FEC0C67C00000000003078CC00780C7CCCCCDC76000000000000CC0000780C7CCCCCDC76000000000060301800780C7CCCCCDC760000000000386C3800780C7CCCCCDC7600000000000000007CC6C0C0C67C180C6C380000003078CC007CC6C6FEC0C67C000000000000CC00007CC6C6FEC0C67C000000000030180C007CC6C6FEC0C67C0000000000006600003818181818183C0000000000183C66003818181818183C0000000000000000003818181818183C0000000000C600386CC6C6C6FEC6C6C600000000386C3800386CC6C6FEC6C6C6000000000C183000FE60607C606060FE000000000000000066DB1B7FD8D8DF760000000000007ED8D8D8D8FED8D8D8DE00000000003078CC007CC6C6C6C6C67C000000000000C600007CC6C6C6C6C67C000000000030180C007CC6C6C6C6C67C00000000003078CC00C6C6C6C6C6CE76000000000060301800C6C6C6C6C6CE76000000000018003C181818181818183C0000000000C6007CC6C6C6C6C6C6C67C0000000000C600C6C6C6C6C6C6C6C67C00000000000018187CC6C0C0C67C18180000000000386C6060F060606066F66C0000000000666666663C187E183C18180000000000003E6363301C0663633E001C00000000000000003E63380E633E001C000000000C183000780C7CCCCCDC7600000000000C1830003818181818183C00000000000C1830007CC6C6C6C6C67C000000000018306000CCCCCCCCCCDC7600000000000076DC00BC6666666666E6000000000076DC00C6C6E6F6DECEC6C60000000000211E001E3360606763331D0000000000423C003B6666663E06663C00000000000030300030303060C6C67C00000000000000000000007E6060600000000000000000000000007E060606000000000000606062666C183060DC360C183E000000606062666C18366EDE367E06060000000018180018183C3C3C3C18000000000000000000366CD86C360000000000000000000000D86C366CD800000000000011441144114411441144114411441144AA55AA55AA55AA55AA55AA55AA55AA55DD77DD77DD77DD77DD77DD77DD77DD771818181818181818181818181818181818181818181818F818181818181818181818181818F818F8181818181818181836363636363636F6363636363636363600000000000000FE36363636363636360000000000F818F818181818181818183636363636F606F63636363636363636363636363636363636363636363636360000000000FE06F636363636363636363636363636F606FE000000000000000036363636363636FE00000000000000001818181818F818F8000000000000000000000000000000F81818181818181818181818181818181F000000000000000018181818181818FF000000000000000000000000000000FF1818181818181818181818181818181F181818181818181800000000000000FF000000000000000018181818181818FF181818181818181818181818181F181F181818181818181836363636363636373636363636363636363636363637303F000000000000000000000000003F303736363636363636363636363636F700FF00000000000000000000000000FF00F73636363636363636363636363637303736363636363636360000000000FF00FF00000000000000003636363636F700F736363636363636361818181818FF00FF000000000000000036363636363636FF00000000000000000000000000FF00FF181818181818181800000000000000FF3636363636363636363636363636363F000000000000000018181818181F181F000000000000000000000000001F181F1818181818181818000000000000003F363636363636363636363636363636FF36363636363636361818181818FF18FF181818181818181818181818181818F80000000000000000000000000000001F1818181818181818FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFFF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F00F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0FFFFFFFFFFFFFFFFF00000000000000000000000076DCD8D8D8D8DC7600000000000078CCCCD8FCC6C6C6C6CC000000000000FE666260606060606060000000000000000000FE6C6C6C6C6C6C000000000000FEC6623018183062C6FE0000000000000000007ED8CCCCCCD870000000000000000066666666667C60C080000000000000000076DC1818181818000000000000FE38386CC6C66C3838FE00000000000000386CC6C6FEC6C66C38000000000000386CC6C6C6C66C6C6CEE0000000000003E60603C66C6C6C6CC780000000000000000007EDBDBDB7E000000000000000002067CCEDEF6F67C60C0000000000000001C3060607C6060301C0000000000007CC6C6C6C6C6C6C6C6C60000000000000000FE0000FE0000FE00000000000000000018187E181800007E00000000000030180C060C183000007E0000000000000C18306030180C00007E00000000000000000C1E1A18181818181818181818181818181818181818587830000000000000001818007E001818000000000000000000000076DC0076DC0000000000000078CCCC7800000000000000000000000000000000001818000000000000000000000000000000180000000000000000001F1818181818D8D87838180000000000D86C6C6C6C6C0000000000000000000070D8183060F8000000000000000000000000007E7E7E7E7E7E0000000000000000000000000000000000000000001D00000000002466FF66240000000000002F0000000103060C183060C080000000003000007EC3C3C3DBDBC3C3C37E000000004D0000C3C3E7FFFFDBDBC3C3C300000000540000FF99181818181818183C00000000560000C3C3C3C3C3C3C3663C1800000000570000C3C3C3C3DBDBDBDBFF6600000000580000C3C3C3663C3C66C3C3C300000000590000C3C3C3C3663C1818183C000000005C00000080C06030180C060301000000006D000000000066FFDBDBDBC3C300000000760000000000C3C3C3C3663C1800000000770000000000C3C3DBDBDBFF660000000000001978C30BDB7403E946015732C0B94000F3AA5FBBE6A526891D268C4D022E8E1EDB2B57BE4904B91E0083C704FC8A042688054647E2F75FA08404FEC026884522A185042689452357E8D91B5F26885D2526887D268A1E4904E8FC05740580FB137F1D32FFD1E32E8A87BEA532E40AC0740140268945272E8A87BFA526884529A185048A168404FEC2F6E2B2003DC8007E15B2013D5E017E0EB2023D90017E07B2033DE0017E002688552A8A2687048AC42401C0E00480E402D0E40AE080F410A08904240A0AE0E89408B2C0B030EE42EC2408C0E0020AC40C012688452DB00326884531E877081E0632D2C41EA8048CC18CCE3BCE743426C577048CD90BF1740380CA1026C5770C8CD90BF1740380CA0826C577088CD90BF1740380CA0426C577108CD90BF1740380CA01071F26885532B003BAC403E8E4048AE0C0EC028AD48AF2D0EE81E204042503030BC22689452B8BECC6461A1BC30AC0740FFEC87436FEC874568BECC6461A00C3B82000F6C101740383C060F6C102740383C03AF6C104740305030383C03FC1E8068BEC89460EC6461A1CC38BFB83C720F6C1017403E88500F6C1027403E8EF00F6C1047403E8F8018BECC6461A1CC3F6C1017406E81615E82501F6C1027403E88501F6C1047403E81202EBDC8AC4EE42ECAAFEC44AE2F5C3E86B04BDC003B9150032E4EC87EA8AC4EE42ECAAFEC44A87EAE2F0EC87EAB020EEC3268A0588044746E2F7C3268A25EF47FEC0E2F7C3B2C08AC4EE268A0547FEC4EEE2F4C39CFA51578BC72BC32689078BF7E80A04EC2688440126895441B2CEEC26884402B2CCEC26884409B2CAEC26884404B2C4EC26880483C705B90400B401E870FF268A04EE8BFE83C70A268B544132E4B91900E85BFF268A4401EEE85FFFB2CE32E4B90900E849FF268A4402EE5F5983C7609DC39CFA8BC72BC32689470251572E8E1EDB2BA010042430AABE4904B91E00F3A4BE8404B107F3A4BEA804B104F3A4BE1400B104F3A4BE7400B104F3A4BE7C00B104F3A4BE0C01B104F3A45F5983C73A9DC39CFA268B3F03FB8BF7518BFE83C705BAC403B80001EFB001B90400E807FF47B2C2268A4409EEB2C4B80003EF268A04EE268B5441B81100EFB11932C0E8E6FE268A4401EE80C206268A4404EEECB11532E4E8DBFEB020EEB10932C0B2CEE8C5FE268A4402EE599DC39CFA268B7F0203FB06512E8E1EDB2B268A0580261004CF0806100447BE4904B91E00E88EFEBE8404B107E886FEBEA804B104E87EFEBE1400B104E876FEBE7400B104E86EFEBE7C00B104E866FEBE0C01B104E85EFE59079DC39CFA8BC72BC326894704E8A605B2C0B034EE42EC268805BAC803EC26884501504A4AEC2688450283C703B900034232C0EE4242ECAAE2FC584AEE9DC39CFA268B770403F3E86C05B2C0B034EE268A04EEBAC603268A4402EE56424283C60332C0EE42B90003268A04EE46E2F95EFECA268A4401EE9DC3BACC03EC24FE0AC4B2C2EEC3E8A812B2C086C4EEEB00EB0086C4EEEB00EB00B020EEC3508AE080E480802687047F08268704247F8AE03C077E1E26F647330175173C2374133C32740F3C33740BB40326F64709017502B40788264904BAB403800E87040226F64709017407B2D480268704FD89166304C7064E040000C606620400B90800BF5004FC061E0733C0F3AB07268A0732E4A34A04268A4701A28404268A4702A38504268B4703A34C04268B471486C4A3600458C35232E433D2B00DCD17F6C429750832E433D2B00ACD175AC3061E602E8E1EDB2B2E830EE52B00756E803E0005017467C6060005018A1E620432FFD1E38BFB8B8550048A3E62045032F68A0E840432ED41518B0E4A0432D2E8A6FFF6C429753D515289955004B408E8B0D40AC07502B02033D232E4CD17F6C429751F5AFEC259E2DEFEC659E2CAE877FF32C0A200055889855004E8AF03611F07CF5A5959B0FFEBEA522AF538C67D0232C02E8E06DD2B5AC3A08504F6E38AF78BD88BCABAC403B8020FEFB402E86D008BD132ED8AE68AC6F3AA03FD8ACA4B75F732E4E85700C3538A1E62040BC075040ADB7403E835035B8BF82BD181C201018AC332E4C38AC632F62BEA0ADB742A2AC3F62685048BC852B401E82000B2C4B8020FEF5A1E061F8BC18BCAF3A403F503FD4875F51FE881FFC38AD8EBF8BACE03B005EE42EC24FC0AC4EE4AC39CFAEE42EB00EC4A9DC31E2E8E1EDB2B8B16630480C2061FC31E2E8E1EDB2B8B1663041FC350BACC03ECB2D4A8017502B2B458C3E8EEFF80C206C31E2E8E1EDB2B803E4904037413803E490402740C803E4904017405803E4904001FC3502E8E06DF2BA0100424303C3074052E8E06E12B580BC07403E8E10103064E0481C201012BD132ED8B2E4A048BF88BF08BC5F6E3D1E0D1E50ADB75028ADE38F37E028ADEC3FC8AD88BC1E8B3FF03F03A164A047406061FE81B00C3061FE84000C3FC8AD88AC18AE6E895FFF7DD2BF0061FE80100C3538ACE2ACB32ED32F62BEA2BEAE30E8AE18BCAF3A503F503FDFECC75F458B0208ACAF3AB03FDFECB75F6C3538AC62AC37406F6E28BC8F3A5588BC8F6E291B020F3ABC38BD8E82E002E8E1EE12B8AC322DFD3E3A8807403301CC3F6D78A3422F70ADE881CC38AFCE80C002E8E1EE12B8A04D2E822C7C3B028F6E2F6C201740305D81F8BF1C1EE0280FF057F0C03F0F6D180E103D0E1B703C3D1EE03F0F6D180E107B701C3FC8AD82E8E06E12B8BC1E8860075028ADE03F0E82400C3FC8AD82E8E06E12B8AC18AE6E86D0075028ADEF7DD81C6F00081C7F0002BF0E80100C38ACE2ACBE32C518ACA5657D1E9F3A513C9F3A48ACA2BF12BF981C6002081C70020D1E9F3A513C9F3A45F5E03F503FD59E2D48AC78AE78ACA57D1E9F3AB13C9F3AA8ACA2BF981C70020D1E9F3AB13C9F3AA5F03FDFECB75DEC3E8B00081C201012BD138F3760232DB51B102D2E6D2E35932EDBD5000803E4904067404D0E2D1E0061F8BF88BF0B050F6E30ADBC352538ADF32FFD1E38B875004E80E0050D1EBA14C04F7E35B03C35B5AC3528BD0A04A04F6E432F603C2D1E05AC38BE883E50F2E8AA696918BE8C1ED0483E50F2E8A869691C300030C0F30333C3FC0C3CCCFF0F3FCFF268B0486E0BA0080F6C4C074020AD6D0EEC1E00275F288560045C38B1663048AC586E0EF8AE1FEC0EFC3528BD08AC4F6264A04C1E00232F603C25AC3538ADF32FFD1E38B875004D1EBE802005BC3528BD032F6528AC4F6268504F7264A045A03C20ADB740803064C04FECB75F85AC3528BD0A04A04F6E432F603C2D1E003064E04D1F88BC8B40EE891FF5AC38BC2F7264A048BF9C1EF0303F80AFF740A8AC732E4F7264C0403F880E107B480D2ECC3E8F0FCECC3E8F8FFB2C0B020EEC3E8EFFFB2C032C0EEC3C43EA80426C47D048CC50BEFC353B00EE895FC8AE0B00FE88EFC8BD8B80EAAEFB80F55EFB00EE87FFC3CAA740DB00FE876FC3C55740432C05BC3B00E8AE7EFFEC08AE3EFB0010AC05BC38AEE8A368504061F0ADB74258AC52AC3F6E68BC85232F6C1E2028BC2D1E22BD5518BC8F3A52BFA2BF259E2F45AE80500C38ADDEBF88AC6F6E38BC88AC78AE032F6C1E2028BDAD1E28BF12BD58BCBF3AB2BFA4E75F7C32E8E1EDB2B890E60048AC52460A8207406B9001EE99400F6068704010F858B00803E4904077406E819FC7401C3F6068704087403CD42C38A3685048AD6FECA38CD7E1F0AC974648AE98ACEFEC9EB5C38D174588ACE80FD037C048AE9D0EDFEC9EB4980F9037E448AE12AE50AE4750A38CA7438FECA38CA74328AC50AC138F07E0A80FC027E1280FD027FC880FC037DBF8AD6FECA38D174138ACEFEC9FEC98AE92AEC80FE087F04FEC1FEC5B40AE805FEC32E8E1EDB2BA2620432E48BF0F7264C04A34E048BC8E87AFB7407803E4904077702D1F9B40CE8DCFDD1E68B845004E827FEC32E8E1EDB2B8A264904E8F8AA7407C70650040000C3E848FB740B80FC0774067708E877FCC3E89FFBC380FC137C03741FC3E84EFA8AD88BC1E885FA8B2E4A0452F6268504F7E58BF703F05AE890FAC3E830FA8AD88BC1E867FAC1E703528B2E4A04C1E503F7268504F7E58BF703F05AE855FEC32E8E1EDB2B8A264904E8E1FA740C80FC077407774080FC037F04E853FBC3E81EFCC3E8EAF98AD88AC18AE6FEC4E81DFA2B3E4A04C1E70352F7268504F7264A04C1E0038BF72BF05A8B2E4A04C1E503F7DDE800FEC380FC137C0374C6C3E8AFF98AD88AC18AE6FEC4E8E2F9528B2E4A04F7268504F7E52BFD8BF72BF05AF7DDE8E9F9C32E8E1EDB2B2E8E06E12BE85DFA7411803E4904077C182E8E06DF2B7403E9B100E849FC8BD8268B078BEC89461AC3A15004E8B7FC8BF0BB08002BE38BEC803E490406751FB90400268A0488460045268A8400208846004583C650E2EBB80002B280EB17D1E6B90400E856FC81C60020E84FFC81EEB01FE2F0EBE22BEBC43E0C0132F680FB0E750926837DFE107502B680161F8BF58BCBF6C6807401475657F3A65F5E7423FEC003FBF6C680740147FECA75E2FECC740F2E8E1EDB2BC43E7C00B080B280EBCB32C08BE503E38BEC89461AC3803E4904137C057502EB5AC32E8E06DD2BE818FC8BF08B1E85042BE38BECB80508B90500BACE03803E49040F721AF6068704607513B40AF7C601007502B405B007EFB80518B90501EF518BCB268A04F6D08846004503364A04E2F158EFB8000132D2E944FF2E8E06DD2BA150048BD032F6528AC432E4F7264A04F72685045A03C28BF08B1E85042BE38BEC538B3E4A044FC1E703C1E603B9080032E4268A04D0E40AC0740380CC0146E2F18866004503F74B75E35BB8000132D2E9ECFE33EDEB03BD01002E8E1EDB2B2E8E06E12B8A264904E8C4F8740F2E8E06DF2B80FC0774057C3DE90B028AE38BF08ADF32FFD1E38B8750048BD0A04A04F6E432F603C28BF8D1E70ADB740BD0EBA14C0403F8FECB75FA8BC60BED7503F3ABC3AA47E2FCC3502E8E06E12BA15004E8EEFA8BF8A050048A264A048BE858A8807408247FC5367C00EB04C5360C018AFC32E4C1E00303F080FF067469D1E78AF3B05580E303F6E38AD88AF88BC550B204F6C6807527ACE855FA23C3ABACE84EFA23C3268985FE1F83C74EFECA75E783EE0881EF3E0158E87200E2D2C3ACE82EFA23C3263105ACE825FA23C3263185002083C750FECA75E583EE0881EF3E0158E84900E2A9C38BC550B204F6C380741FAC263005AC263085002083C750FECA7FEF83EE0881EF3F0158E82000E2DAC3ACAAAC268885FF1F83C74FFECA7FF183EE0881EF3F0158E80300E2BDC3FEC038E0720632C081C7F000C38A168504F6E28BF032F68BEA660FB6065104F6E2660FB7164A0483FA647503BA680083FA5A7503BA600081FAAF007503BAB00052F7E2660FB7F8660FB60650046603F866C1CF1003FA66C1C70381CF00FF66C1C7105A8AE24AC1E203A0500450C5060C0103F02E8E06DD2B585751509CFC8BCDE83B00B508AC8AE08AC7D0E473028AC32688056647FECD75EF6603FAE2E29D58595F83C708FEC038E0720E5132C08BCD4903FA83C708E2F9592BF5E2BCC3065233C08EC066C1C7108BC766C1C710E867A6741638E074120FB6D0E88BC98AE066C1C7108BF866C1C7105A07C380FC137C0B0F8418FF80FC620F8411FFE80100C380FC11750680E38080CB0F50538ADF32FFD1E38B9750045B2E8E06DD2BE807F98BF88B2E85045832F6528B364A04568A268504C5360C0180FC0E7509837CFE107503B41046F6E403F0B603F6C380756DB2C4B8020FEF5A5850524A8AE0575151578BCD32C0AA03FAE2FB5F47FEC438D4760A32E48BCD4903FA47E2FB59E2E0595FBAC403B0028AE3EF5B4B5A51578BCDAC268A25AA03FBE2F75F472BF5423BD3760A33D28BCD4903FB47E2FB59E2DDBACE03B80300EFB2C4B8020FEFC3B2CEB80318EFEBBC2E8E1EDB2B803E6304B4740BBACC03ECA8017504CD42C3C3E8D1F80AFF751C8AE380E41F80266604E008266604E82D008A1E660480E320B105D2EBE891F57419A0660424DF80E30174020C20A2660424100C020AD8E85300C3E87EF8C3535080E30F8AFBD0E381E310070ADFE860F5740EB4008AC3E82CF30BED740326881D8AC3E80B000BED740426885D10585BC38AE01E2E8E1EDB2B803E4904331F7F088AC4B411E8FEF2C3E830F8C39CFAE89F05B2C0B90300B4018AC4EE8AC3EE0BED74044726881DFEC480C302E2EBB020EE9DC32E8E1EDB2B8A264904E8F2F4740A80FC0777067403E8C0F5C380FC137C03744CC38AD8E8BBF7BACE03B008EF2E8E1EDD2BF6C380751CB2C4B802FFEF8A25C605008AE3EF8A25B4FF8825EFB2CEB808FFEFC3B80318EFB2C4B0028AE3EF8A25B4FF8825EFB2CEB80300EFEBE1502E8E06DD2BA14A04C1E003F7E203C18BF858268805C32E8E1EDB2B8A264904E86FF4740F80FC07770B7408E85FF58BEC89461AC380FC137C037429C3E835F78ACC2E8E1EDD2BBACE0332FFB80403EFD0E78A1D22D9740380CF01FECC7DF08BEC887E1AC3A14A04C1E003F7E203C18BF82E8E1EDD2B8A058BEC88461AC32E8E1EDB2B8A3E62048ACFD0E132EDBE500403F18B148A0E49043C0D764A2E8E06E12BE8EEF375338A1E4A048AF88AC6F6E38ACA03C8D1E1030E4E048BF98AC7AAD1E941FEC238DA735989148B166304B00E8AE5EF8AE1FEC0EFC32E8E06DF2B80F90774C3E98300750432D2EB0F3C0A751D3A3684047503E99300FEC68914E892F3750580F9077F058BC2E84CF6C33C077504E81192C33C0875830AD274DEFECAEBDA32FF32D22BCB3A3684047406FEC603CBEB9589148B166304B00E8AE5EF8AE1FEC0EF2BFB2BFB268A6502508B3E4E048BF3D1E603F7A08404F6E38BC8061FF3A58BCB58B020F3ABC3B90100B40AE8BCC6FEC23A164A0474038914C332FF32D289143A368404742EFEC6EBED89142E8E06E12BE8FCF2740E2E8E06DF2B80F907740432FFEB108BC2E8ADF58BF9D1E74F8B1E4A04EB9133C98A3684048A164A04FECAB001E970F72E8E1EDB2BF6068704087403CD42C3E876F68BECC6461B00C32E8E1EDB2B8ADF32FFD1E389975004D0EB381E620475058BC2E85CF5C32E8E1EDB2B8ADF32FFD1E38B9750048B0E60048BEC894E16895612C32E8E1EDB2BA0870424800A0649048A264A04F606870408752DE85DF27407803E4904077F21518ACC8A2E8404BE2F9D81FE389D74102E3B0C740583C603EBF024802E0A4402598A3E62048BEC89461A895E0E894E16895612C3841823842B33842A322E8E1EDB2B3C047F1B5052BACC03ECA8015A587408803E6304D47408C3803E6304B475F83C1C73F432E48BE8D1E52EFFA66B9DA39DC39DD59D0A9E549D549D549D2F9E479E4B9E549D549D549D549D549D549D799E549D7D9E989E549DD79E549DE59E009F079F139F379F803E4904137415E8BFF48BC386E0E870EF0BED74078AC732FF268801E893F4C3E8A6F48AC7E844FC0BED74F026887D11EBEA803E49041374E306E88CF41F0BED74078BF2B91100F3A48BF2E8DE0132E4B91000B2C09CFA8AC4EEACEE9DFEC4E2F4ACE807FCEBB580FB0177B0E843F4B2C0B030E8FBF024F780266504DF80FB0074070C08800E650420EEEB90E89D018AC3B2C0E8DBF08BC8EEB020EE8BEC884E0FE978FFB311EBE48BFAE805F4B9100032E4B2C08AC49CFAEE42EC268805FEC44AEE9D47E2EE9CFAB011EE42EC9D268805E8E3F3EB5AE8CE00C38BFA518BC3268A35268A6D01268A4D02E8CA0083C7034359E2E8C30ADB751AE8B6F3B2C0B030E86EF0247FF6C70174020C80B430E870EEEB1DFECB751CE898F3B2C0B030E850F0EEA8807503C0E702B4348AC7E851EEE87FF3C38AC3E8D1008BEC894E16887613C38BFA518BC3E8C30026883526886D0126884D0283C7034359E2E8C3BAC6038AC3EEC3BAC603EC32E48BEC89460EC3E83FF3B2C0B034E8F7EF8AF8EEB030E8EFEFB301A8807505B300C0EF028BEC895E0EEB9C518BC3E87300E82A008AC3E852004359E2EEC3F6068904027403E816008AC3E83900C3F6068904027403E806008AC3E82E00C350B01EF6E650B03BF6E550B00BF6E15903C15903C1B164F6F180FC327C02FEC08AF08AE88AC858C352E839005A538BDABAC8039CFAEE428AC7EE8AC5EE8AC1EE9D5BC3E81F00539CFABAC703EE4242EC8AF8EB00EB00EC8AE8EB00EB00EC8AC88AF79D5BC35051E881F2B9FF7FECA8087502E2F95958C32E8E1EDB2B8BF8240F81E7F000C1EF0383FF067F182EFFA5FB9F03A018A0B6A012A13C0374083C047F03E8CF01C3BAC403B0038AE3EFC33C0477F2505350528B166304B007E8EBEE8AE0D0EC80E401A840740380CC02B012E8D8EE40A38504B009E8CFEEA8807404D12E8504A185048BD03DF401731BBAE0013DC2017313BA90013D7C01730BBA5E013D4A017303BAC800891685045A58C6068404FFE85D015B588ADF32FF0AC0740EB30E3C017408B3083C027402B3108ACB8AE981E9010280F9087E0481E90101871E850453E85BF25BE88502C30AC07411FEC8741AFEC87438FEC8743CFEC87440C3892E7C008C067E00C38CC88EC0FECA0ADB740732FF2E8A97F6A0890E850488168404892E0C018C060E01C3000D182AE80903B90E00EBD6BD0670B90800EBCABD1978B91000EBC20AC075BD8B0E85048A16840480FF07773580FF0177100AFF7506C42E7C00EB26C42E0C01EB2080FF02750B8CCD8EC52E8B2EF888EB100E078ADF32FF80EB02D1E32E8BAF69A18BC58BEC894E1689561289460A8C461EC319780670067406781978198853068A264904E8C7C3268A4702268A6733F6C4017422BD19783C107D0EE871028C060E013C0E7D07BD06708C0E0E01892E0C01E81001EB2DB4843C107D13B4813C0E740D7F15B4023C08740580CC80EB0A26F6470501740380E47F86C432DBE80600E8C901075BC38BF88BF2BACE03ECB80500EFB80604EFB2C4ECB80204EFB80407EF8BD68BC750247F0AC074220E0733D2B900013C047507B710BD1978EB10FEC87507B70EE8E801EB05B708BD0670E82D0058A8807400BAC403B80203EFB80402EFB2CCECA801B8060E7502B40AB2CE50EC58EFB004EE32C0EEB80510EFC3FC0BC97501C380FF0E750A26837EFE10750380CD801E061F2E8E06DD2B8BFAC1E705B0405380E303F6E35BF6C3047402042086E08BD003FA8BF5E32832C086FB32FF51F6C5807401468BCBF3A483FB207407B920002BCBF3AA59F6C580740146FEC975DE1FC38A264904500657C43EA80426C47D0C8CC50BEF74308BEF83C707268A053CFF742438E0740347EBF28BFD268A05FEC8A28404268B4501A3850426C47D03893E0C018C060E015F0758C326F64733017401C3C41EA80426C45F088CC00BC3741FBF0B00268A013CFF7415473806490475F2268A0732E4870685048BD8E80100C38B1663048A268504FECC80E41F9CFA80FAD4740BB014EE42EC24E00AC4EE4AB009EE42EC24E00AC4EE9D8AC88BC38A168504F6F2803E8404FF7507A28404FE0E8404F6E2F6C1807402D1E0488AE0B0128B166304EFA04A048A268404FEC4F6E4D1E0050001A34C04C38A264904500657C43EA80426C47D088CC50BEF744A8BEF83C70B268A053CFF743E38E0740347EBF28BFD268A3D268A5D01268B4D02268B5504268A450A3CFF7402FEC850A08404FEC0F6268504A3850458A2840426C47D068BEF32C0E8DBFD5F0758C3505351522EA1E52B0BC074092E8B2EF8880E07EB08B83011B702E8C7BE5A595B58C33C047C01C3E3FD532E8E1EDB2B86FB32FFD1E38BFB81C750048B355B8915568BF05153268A4600453C0D7F1F750432D2EB403C0A74323C077505E87D89EB623C0875080AD2745AFECAEB27F7C602007405268A5E0045B90100B409E864BEFEC23A164A04720C32D289153A3684047306FEC68915EB2B50E8ADEA7508B80008E840BEEB09803E49040774F132E4863E6204538AFC55E825005D5B863E6204588B155B59E2155AF7C60100750289158B053A3E62047503E834EDC3E964FF33C98A3684048A164A04FECAB80106E8F3BDC32E8E1EDB2B0AC0750DE818008BEC895E0EC6461A1AC33C017509E841008BECC6461A1AC3A08A04BFF6A52E3A057204BBFFFFC332E4D1E003F82E8B5D040ADB74050AFF7504C386FBC3A0100424303C307406F6C30175EFC3F6C30174E9C3BFF6A52E8A0D32C083C7042E3B1D741286FB2E3B1D740B83C702FEC038C87EEBB0FFA28A04C3BB0800F606890402740EBB0700BACC03ECA8017418BB0800BAB403E8DFEC7421B701E88CE980FAD4741786FBC3BAD403E8CAEC740CB702E877E980FAB4740286FBC30F080F080F080F08030103010101000801010101010101010F010F080F0400020F0201010F01FF01FFE00F00000000070208FF0E00003F00100108000000000100020201000404010005020500060106050600080108000702070607CA003400900096000000000000005F57C390506652E8F4FFFFFF81EF100000002E8B0766BAF80CEF66BAFC0CED32C0668BF8665A58C366B84F03665266565357500AFF7544E8C8FFFFFF6633F666BB5300E848000000C1E810F6C440752B6681E2FF0266D1E2668BC26640C1E010668BC266BB1300E83000000066BB1200E827000000665832E46650585F5B665E665AC39066B84F03C39066B84F03C3906652E811000000ED665AC3906652E805000000EF665AC390668BD75033C0668BC36603C6C1E002EF6683C20458C3FE4EF14EF14EF14EDC4EDC4EDC4EDC4E1E4B954BBF4B004DFF4CFF4C454DAA4DDA4D974EC04EC04E374D2A4D2A4D2A4D2A4D2A4D454DAA4DDA4D974EC54E0B4FC04EC04EFFFFFFFFFFFF0000FFFF0000FFFF0000FF000000FF000000FF000000FF000000000008100008101800081018224901224902305D03105D03474A03FB49032349043C49044C490422490300000000CF500002CF500102CF500202CF500302CF500402CF500502C9520002C9520102C9520202C9520302C9520402C9520502FE520002FE520102FE520202FE520302FE520402FE5205023955000239550102395502023955030239550402395505023955000239550102395502023955030239550402395505020054000200540102005402020054030200540402005405023C5400023C5401023C5402023C5403023C5404023C540502AD530002AD530102AD530202AD530302AD530402AD530502E1530002E1530102E1530202E1530302E1530402E153050291570000C4570100C4570200B15700009F560002985400029854010298540202985403029854040298540502B75600020C5905000C5901000C5900000C5902004D5900004D59020092590000225600022256010222560202225603022256040222560502DE570000065800008758000002590000E6540002E6540102E6540202E6540302E6540402E6540502015900010159000105520002055201020552020205520302055204020552050226580000025900000259000002590000375700002953000229530102295302022953030229530402295305025853000258530102585302025853030258530402585305028E5300028E5301028E5302028E5303028E5304028E530502495800006C5800002A5401022A5402028654010286540202A600020116AA000000000000000000007AAA00000000000000000000000000B000000000000000000000000000000000C6AA00000000000000000000000000000000000000000000000000000000000000000000000020B000000000000000000000000000000000000036B00000EEAA000000000000000000000000000000000000000000000000DEAB000000000000000000000000000000000000000000000000000000006300020100003700004BE5000449370056004001050000616001003CE501008044370051142D0540010000003E054020A107004513005B520D030041E30D3E054100000000446200330541010000001B05410B01020600410D251F0E030D655300025B004B000101000037000042210063001F006301390063033F00630245005A5A5B030540000005042D1940001505400201020C00400200000D005B020000E30D5B020000E70D5B020000E80D5B00280002010000370000010500006CA80100020000010002010100010500006DA8010002000201005BF00003030C00370000660403044628003E054600000000493C000301000003010101030102025216030146001B0546080202000002020101020202023D6502004947002C2202463DE502004952002CA202463D650202496D0001050000E5680100010101000002050098A3050003014003090540000000FC3E0540000000D0499000010563C000000040552002439400540063C0010164C00056004003214002010265C040010166C00103314002010267C0400101E8C0033D65020144E7003DE5020144E70051025600404AE5A0218044EF0051142D0540010000003E0540A086010045CB005B4AE5A0218049E7005B2104010104003700003D25000044F5004A05A336020000004420040305003200000003004097360305460031000004020440030341053E024140484C00330240410303410633024140435000330241403E054100000000447000330500010000003E050000000000442004511443220003034005030241402D05400000000003024640040100000401010104010202040103030401040404010505040106062D0541400000003E05410020000045B7002705410020000003054600310000040205410102963641030500801A0600030041973603004096363E024140442004330500010000003E050000000000442004511443CE003D6500014432014A05B636000001004932014A05B636000002004432010105000061600100010501000000000801050000616001003CE5010080492A014A05B6360000020049D1010105B836000002100305410A000000330541010000003E054100000000448F01510A3C05B83600000200494C0103054104000000510A330541010000003E054100000000448F013C05AA36ADDEADDE4971010105B836000000000D05B636000002000705B636FFFFFEFF0D05013800010000510A010597360000000001059636000000000105A3360000000007050138FFFEFFFF660B0304000400020204001505000A1A05041602050000000000020201002C05010000020055000202890204020503000005D0523502050000000000020201002C05010000000055000202890204020503002000D0523566163E0D420000449B02560041030C4100002D05410F000000090541F0FF00002D0A41420305460080000056084204040000002D0546040000002D0542040000003E024241455602020500008000002C020000020201002C050100000200028902040209020402050300000040020A03415235521602010200140500181A050208030241002D0541000002002D01410001027B3641030140042D01400201027C3640010568360000000301057036FFFF0183030241002D0541000004002D01410001027D3641030140042D01400201027E3640010569360000000401057136FFFF0084030241002D0541000005002D01410001027F3641030140042D014002010280364001056A360000000501057236FFFF0085030241002D0541000006002D0141000102813641030140042D014002010282364001056B360000000601057336FFFF008603054600310000030241002D0541000000002D014100030140042D0140020402004104020140040504000000000405050000000004050600200000010594360000008001059536002000000102563641010257364001056136FFFF00800105973600000000010596360000000003004196360105B136000000005C05B63600FFFFFFC60009000705B636FFFFFDFF03054150C300004A05A336020000004920045114330541010000003E0541000000004902045B0020000201000437000052336616030C4104004C0D410200441D000DA54C00025B15000301040037000003054610270000020200465B005B01020104043700004221006307290063034E006302600063048400630C1201630D1B015A5A435A01010D455D9F0F66060124475D3000014C475D31000324463B003E254600445A015420475D5B0DE5475D015C05435DFFFFFFFD110003005B0DE5475D014A25445D024472000DA5435D010DA5435D025C05435DEFFFFFFD010000005B4A055300000200004494000D254B5D013D8D00000044110103054680F0FA0256004103194100210541FF0F0000270246403E05410000000044C4002D0D400100018A455D4056004103194100210240412702464001A54A5D0C010A4A5D4015054004560041032941002D0D410100210240411B054008010A485D400DE5485DC007254B5DFE4A654B5D014909015B0105435D000003005B0DE5475D014A05435D010000004441014A05435D10000000494E014A05445D010000004936015C05435DFFFFFEFF000002005B5C05435DEFFFFDFD010000005B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55AA1C00F10E00000B0064860100000000000000000034001C000000504349520210FF7300001800000000031C000000030000006F37000000B0000029108DBDFBDA32EED7FD9E4999264924790A8B14122E085098B4B3045984C3FF0F25B1410016104084661169523265B3C78C596DE5E725E55716D39652F16282452332489242F088082D4816B232DA845248567F7F7FF999244979CE7BEFBCF79C6B5DD87765DD6BBAEEB55DDA7765DDD7DF7E047DB1F75AFBEE73E6C3D6CBED6FFEFC54761AC9236DC7118F49FFF7D0C1FE256244997F592D8DBD4A4BCA47A740A1A00FF068D9D29C1E1F6C308FBF9D773F4C57DBBD7FF403BEFFF81A6BEB812FFA7D795D98905C91C0E6FB61EEC3E8048932B798FCBE597659C5DC8D87BB22AF0D7971862DFCAD87BB2C3B3BCC7E633B1D16E2FF5F61EECB0F8249E2FBC5F78BEF17DE2FBC5F78BEF17DE2FBC5F78BEF17DE2FBC5F7FBFD8D21FC338F6643F89D627CC46585AB21AB1A65276C2D86D7B7FCED6306DBB95F6C166173D6DA05AB746D71816DC221FC938F6955FBC86EE70816FB9C6B725D68F29939DC6E208A59A396234A875230E7BBD513667F12AB65155FB37AFC00B321F476363FCEC231C589418E18D8EC8B603BFF202C7CF6473B3722AEE10FABD1265AAAB344FA008AD614EEE3BC551362D9E0E3F858665A6776FF90855B0ED883D640D0057544F997D982CAAB68BDDF2932601399DCA05B4ED7949B559A31A8FB8C8D4919ADAD365CDD7BB5BE332B19BB1B3EC4E3A2E4F32628FE2FD5BA27AEC4E255CEDD1F69EDD3EDF6A314B3D5D1C66659675442461F9FE3462193AB8C694E9630CDFC66AE84B4732CA4DFCF180BF187FAEC12464C8E9A6DC69EF3186998DFC23268201CB336F723F4986C1B9BB6719D344109526F1C8372B3B361E53A4B85705ADDD47CEB29359CBD64E6B39FACE9B8D2AE23A6F47B86C19B71FB359C88C3FEA7029E4B32DF6EA2FC1459610D89E08EFB93A48CCB5C1DCDF80DF9233DF00972EA12C67065656BA050B76E1E3DEF611F7EDB9F7C3498BC4E331BABE6627573B89D5F17126EBCD97042487377584BEA1EABDE648CB513CAF38CDF4FED46D98CA0C7556C5961971F46C39E6B4A77B8C658279D99A643A8CA9C6EEB141E597BBC10D496B0E854C6439F88D59C7E3B58441728F496FAFC023768B7D7ED556C0E1494A634C394449B00CCF56FF72EE376FA83603EC3F37DB61F670E1CF9F8D7FC79A6622FDACA4D6B2F46ACE649ACE1A92B895605F21CF577221F96BB37CDF99EF9A606E0699915112C96509BF21BF064359C5C86B27721ACE66532790C94EE24FA13F89126FC27218A510910C69173AAB3DE4112FA0E879871F441EAD6440E510C142CF15901C744FA26FC310C688710F18E470CD1CE1A11C61904DA0CEA448C59AEB99B014E6EEE9FC7D15BBD1221ECD5804354436DF424BACE027391A3504C43768D71E44033D964E44336FA4434AFE44343007219682390CDC19C868610E434B0C6ED31113546C7686283C69735550E6C72B901F27CA0EA8143D9967143DD977143E0997143E4A171436B4AE284D0A0D0004DF2C4C41184F53B5BB042680A1018EC7957724842A4136CB385116E0E445B5154F4A19A223A388200FA7807C7807C7807C7807C7807C7807C7807C7E961887E2032B8E010B502216A0A216A0A216A1C885A872216A1C885A872216A1C885A872216A111094B36FCF21F4256614E2D6596A76F466BC021810DDDD4DB87C5BC145E751B06B6F2DA8A943C4E9F426A4A59D0122771643FEC578D578C578B578AC48B1E5C69B107D08295FD2462CD7DD0E432EA33510C7A2BADCB4F7B50B4FC5284F107C3A87D883CE50FF1015A0220FC8A88204525791579257935794C97F2D5D69FC15A7F1A20FDFC41FC2390D328CD157CC8A68A6BEE055F58A6914F314F614B70F7F7AC52CD629A273BE3F1C7D19C08B9A014754E362EB107DADC636894D2238D5834399620076AB676B48CAF97B1395A651044649BCF1DC6EB52460CD2A8ED92EB40ACFADC4105A718E054F873C15568D8354B8857E893827CF0134025D70025C7F15CB2B0AC5E80A7514C0CC7C6E31BF17AE2E3834C6E6FB53846156917BCC8D6A45C9A6458613A6D054C7A516B3C61E622D75AEB173604AEB125759392FD54699DC9D34C0525E7C011E28E74AA61D38034CE24E85D72A77B60E2E5BEBD5D5674E3E82BAA4667BBB56C2A54D567CE3E7EBAA4647776A96A1B014A6DC69677AF3B2B8928386FC325CE0F56992AC1F2BD400EC28876801E54D1002EC285D800B2E2A7D3276091218D9E40499F4DDBD4859642765656218B3364F55E60A8FD3EB6B4925F7413A8AB27469C87C9A895741B7D6A8962FBF9546BCBEF78A25CBEF6EA2603D9C7EC144C97DFCAA1997DEDD45097DEF145187BDFD6EA24D452A34356A1AA343F528AB4686B5427752D0D8287F1DE1052720B539C8621F4E431CFE0C5F0631E6C18C7A10631E96137F591CF9B3CC8E3E39D27B9FA548D37F59A84AA37F59C64CB019941524D44897578EC13E96FCE3FA871F4E729EC90DD833BA9D03D239496ABAEF0B881B5C005910FAA38C6E356F64328087D49CA78A740D19D03C122903A7523737F229A95A6768695F8291CCB01701A6A1699401BC02B3080DC2CE08F1D238FA5394F51C6E74C7292D50DCE52BF73A538FED1C6765CEE671F56729E09D0342481AA0DCC2B1773394D5AD32B4BB9852399798E7739FB11B201C0D94C2B0AA7A4E378E49CA4B56C86CA33846CEA57EF15245354481A93A07B45DE2A5C6F00365638DE006CBBB9DE37E51B20046CEAC688D81C5FE84E534CE373E19CA4B56C86CA138F6C719D2AFDCDA9CA7B4740D59CE06C6AE3736A433B8E3736A73250E7739EB11B20DC0D9C329A7E49778240F644C9855EE8D83647F40E329ABF7803FB54708D8D8BBC53B8DE29D6A27FAFC283ECADAE7789D28D9062365A63453CBB2534FC37036371326156C86CAB38FE61C6726BF7303FAD8E816A740F40BB9D2B8DCE956D70D94A732B4B9DCE72C46C8670363729A77298DD903264FE698C036714E33875FBC01FDE81D034E740F30BBC51B8DE28D699F938DE28CE64F6E7786851B21846CF37362767B2534EDDC6E7D41326156C86C6A71FC127753180FEF30E81A53A078A5DCE85C6E742B4CEDB8DCE848E53189F9A6344FFC2D9C488D98FCD7F85B3CAFFC2D9E5446CD3E67FC2D9E83FE16CF402366BE33FF0B679751A0456BF7F59A2E378CE56D37FD5D175EF08ADCEFEB1B34B05BDDFD468B8D0AE570870CEA8AE3EFEA1B711C904DFD67527115CF1C6BB22BA83688AEC17754EE5778343E318E578C31FD7F39C2F46FFAADB815CBD9CED17193A2BC63828472BD1BFE736E5A2BD9BFEAF538360BE03907772BEA5BB2AFC816787504AF09F8CD116018514AA1160422470BC91C58222666D59844F70B066B89DFF661156C76C0B9E4583052AA759155161109553BFA955A7295545861FC8AAA2C3870554587AE0AA85B94AABFB9623B82ABDAAF2AAE18A36055681C1551626382ABD4B02AA2C5544AAF53FD4AAA376563114AAC559155163616DDF51639149BCAB92AD56255B87175C55AAEC0AB7F564609569529561641382ADD2EE95616462255BA5FEA55A8DC2560B26BFA2560B288A5591653D72562255A09CB2C5C156ACEEA56159776055B7EE0AB0B2D9C156A6B02AC2CBF44AB537FA95646ECACCC293ECD59156166B16DE01166E149BEBDC9566A255A53D2AE2ACD6B8AB7F667B0956D794AB0B3B1C156E4D7956459EC8956E4FF42AC4AE6018B413FA1561686384AD1688D7156A729568472D1B70559BF74AB15A4B6055A74A5591690382ADC3B02AC2D2944AB70FFD4AB2376569B149F485915616A016DE0D16A3149BCC392AD4A25595E5AB8AB52D8256FF56B00956982956FFBB5689E06AD49A8180EDFECD5D0E35560D5E6762D5EECE53F46AF33F0357A2BC0D5E93512DFCCACD7358626C1AC34762D61FE193FD1AC347E06B0E6BC0D6193512FFCCACD735932B60D64A7B16B245E4BF46B253F81AC921E06B249A899FE6566B9ACFA0B06B3D4D8B59ED791FD1ACF53FE5ACFB0AE6767F96B550FF66B523CAF6C1ADA90CEEB5B68B1FFA35B473DFF76B6EC2B99E5FE5AE14BFD9AE08F2C6B06B9BE99DD6B9E971DFA35CDF4CFF0D73D85733E3FCB5D9AFF66BB23CB3EC1AF3006775AF3758AFD1AF30067F86BCEC2B99FDFE5AF957FD9AF88F2D8B06BFC199DD6BFF2B19FA35FE0CCFFCB5FE87C2D7F4E67F76BFB3B26BFC358B5FCFC5FE8D7F86F035FE97C2D7F7F33FBB5FE8EC9AFF1362D7F558AFD1AFF13E06BFB5F0B5FE08CFEED7FA7B26BFE56C5AFF51E8FE8D7FCAF81AFF57E16BFC219FDDAFF5364D7FD058B5FFDDC47E8D7FD0781AFEE5A03FE5AFE4EF35FCBCAC6C22E658B5FD6C4F7E2E6781AFEE5A03FE5AFF15DE6BF97958D845D5B16BFD679FDF8BABE06BFC119FF2D7FCD779AFE5E56361177AC5AFFF24477E2EF781AFF0867FCB5FE43BCD7F2F2B1B08BCF62D7FC71DDF8BCFE06BF93C2D7F94EF35FE1AC9AFECEC5AFEF187EFC5EDF035FE2BC2D7F96EF35FE26C9AFF4762D7F9A5BDF8BE7E06BFE6BC2D7F97EF35FF2B64D7FA7B16BFE6C9DF8BF3E06BFC8785AFF33DE6BFE82C9AFF5362D7FB5E17BF17FFC0D7F94F0B5FE87BCD7F91B26BFA474B18BFEB3BF17FDD3C117FFC2D7FA5EF35FE56C9AFEFAE96317FE1FBF17FDD3C117FFC2D7F6BDE6BFEBEC9AFF00E96317FF07DF8BFEE9E08BFFE16BFD5F79AFF3164D7F8374B18BFEAFBF17FDD3C117FFC2D7F4EE9DE8BFF64D7F86B16BF9F0DDF8BFFE06BFD2F85AFEFEE9DE8BFF64D7F89B16BFAAF3BBF17FFC0D7F6BE16BFC13A77A2FFD935FF2B62D7FA885EFC5FFF035FEAFC2D7F8474EF45FFB26BFE82C5AFFEEE03FFB6BFA2D7672D7145AD960D6872D6345ABCE4F622D5645AA28B535CB511CB4FDC34E9CB4D91698B969622D28B06923968FA2D1BAFD945A1C8B415167E3967A7FBB3AC862938C5A9CE3310FA7198E7F38CCB401C666E08E334306719A5842494E48847365ADE84D6327F4B76E4435AA25877D43E551AF2FBDE28972FBDBA8981471401F005F7F2A8665F7B751425F7BC514626EAB63838F1A34356A1AA343F528AB4686B5427752D008B0AEA3AA044A62EB90EA2AC9722F350851F2BCCED4E8BF992549AD0C6ECE77198DC5E2559FC5FBCF9D35491A73E008F146B4F9C3FEBECF1F7CDDBB74975A695873F97F79F3E7D2DD6A746F50A367A44E6771B3B5D79AD9D0CFC1570A3F9B5D983AEA19B3159F0790C9653273B2A5FB1334806D8A59A9A4360CBBB9BF096C4DBD9F47900C1D6F5DB76C70E454F8AC3893AFEA38CBAD3E8569FC45A7F2DC650DDDF3ADFA24E43683ADA2AD6CF21B91BB0A40DCC11438A43DF9DB062B6207DF8151C0F252240CFE2C87FD8AF1AAF18AF16AF158CC6E24925F74A098C3F90B41761926E3E0848DA421EB199104D8D18942DD990F4D10345855BA1AC56260A3BED938F8E64B25969F0818BDB0C62D64F9F7043745348DE54B34A98B3365075E220B2588C2112AFF54884302CBC3A8820DAE8AFC17214E02821D441D9D2173D8B1B364FD9F0F47327CF5C3D46D9D5C535C23BA448C59AFF38BA458684ED7FB0D0BC64742F691D0B668E853C3A178C1A13A8684B77966485FA4487E9E643FCC143F61001A269E54044FC046641F298BE96C2068D80013DBCACB247026FEA7226A047F221884EC3D37B755C6B0549173CAD64B943B545F04774136C474165112A43CE51001A37E210CAE35D31C732889858A59B79584448F4867C8862E608806210B766C326A7B0455D65DE91104BD52379F25FF0708BBF00BAC6174E7EF9B0E229AC00E5A37F6CA099F3E39C6DE606E57B2E0DF107183B0309A5D1861EA3E113CE011FE35039938FFB4327DC1176248463D836535236A02D2D4219FC4B1B0907B755F6E01B26511B16A761A57B755A6E0FAE18E90C6A9EC3638341DE1C5263FC0EAE262F38712632A52509BDF46FB583E9834843874D64FDD2C32448B14D137952C4F8110C4534D0C3114040A6C6144398451671C8E8786AFD8BE3C358994F98A27E9E44E3F7120DC58A19203AF9E0497D381105E1ACE048860DFE1D0848D6F1409B328A5BAC53D858A7A2B55375D8421FCA3EB620F6AB4FEAB7F184C95E80A8CCA0AD2C3AAA9D1A603C4D9E57DA03D43AAE7E8DE559F3EACFE302164A002ED30E893C6F40DB70071A508C69242D03B4A10C948AA7173B06B4DF129CE40A0A9C869E46BD4DDAC43D08F3A7960FFAF0FF2F3E9B8802A182840B680C31127BE861CA74A6B0C38B2A243E79F085409F7168941665376CE41EA61D24617DA0C2BA2E789F4C78E913064A26D4850A04DE98D6488A0A0150A0592A2000150ACC32265034EBC8716A6BBB4A8A49DC848011962E54360134B671B47D61A7953173C8DD46998F6F884EE34E0D1F6B5FA3FDB810D1E3DC68E0B5B8B0D1E225A35FF26B4FA96B2456BB8EB4FD8120B1CEA5AE7C2C9B62D6B09E763957DA6B1A80099AF96F291254531572C7358F6B2F36274D37A3862A0360EBF6D90C088AA9BAB85EEA268A087C05CEDF2A461401FD5F9CC6BE51B5C2D3E3A14A6E86198659F433AADF435AEC814D4310F6DE546314368CAE169373569773E1ADAEDCD2E06B91E9869E5922EF19C12C00A418C9AE1CFDCC57FD1CC7EB5B629EF410AE029892B096F8DF0A5222CDADAAB41080FAC38FEB0715C2494F0B4D32194C9ABC8ABC92BC9ABCA6488DE9C5B718C230FBA1087F0019F3778572DA8A924CD83634131C50E6E41BC46A5C737898611D9D8882F6AD9D018B884454B5F92B00A786CCC27A1327929D72E10170606BB107F0961F4043F9040E008C936FF00E766A3A059F4950E03832F679DA1BE115550716021B820E89382E68275138950097BF490FAD587D5B5D88AC41ED08634873FB9D89D317C129A46D7A21BF355439F5910C7B0B9E800253484ECF5559F6A73481814DA0C87D58D4EC0669059F49195B704E31D4A5B62E391A42C393384029D1B5E3D5D70A624DC4999459059304B469D374C9544AEF1BB746D2218B30D3D571173B64AA3695D6A43E854D12D5542B93CA5A5391BB46D55929D45E5E0C36050A07265079408E00BB2FF0E6EE68DA354F8437736D31629E2E0FCB4BEEAD6ECDB743B3468E968F936F63C000F661C0091B0C61C8266027CD85C524421139A8D7D578E24CB9749AD61D3323E0A2108AD01105387BFAE7AA95EF5874173D559156853AE765AC1A3FB6E661D04A0429D14B3706E80D9C001EFDEAC2A702FF201845037F2B08B64164D7D62B43D36B86F984F407F23DD050E864A181796EB3A6EA7685E00FC0029D08B0F6710CB4060709DEC0940C543AAE69BEBEB07857F316AAA55D876580B7C3EAB01E443DC700A88CBD43165FB01E410FED2EE82B55DC5D79075BB6E1296EB5555AEB8B2B6E075569B5919650C577EB4DF887D32E6AAD57340C8B79F0D4F47CA6D80390C5C181B731A4406D1E4A96F1A6450E6542BD55B6FC7F2FB14971B4AA5771B6AA4FC594B6AA16B4F3C3865B5F37B8DC710028C1589EDC66344871C082C7F2407580C258389A4138900CA01360B976C20A6B4895E3E7D629A45A543A1BD72AD23A4C302804F2A72A00D48CB4F4F40EE8A81E9944029EC46998E53488B491AB70447EC614C7D5D6D9AEE0B317B7F90A484FC349F60DF719B4FC13B0FB4C868017D6356B75EC376E5DBE1B28E0E26B55E3AB6337756F7F7AB3D99F42D1AA5B9240E49E7F024B9FDB0D2CCA34C283C380EA495176DED22492E7370F27F03EE3C1575996E1587208788F75AD6C6768FA1EFEAAC39CE8CBD91D6233EFFFE3512F238C522875120473E3847247284936EAE78AD5732382DE628167CA34A335743D9A397755E3D652A3A8567EC2D9C61FFB16E6B1A62BFBA22641DF09D177C6321C6C617A98217EC698AEF8BD292A6AE9B055CEF84217878271A0FBE60A5DCFCD2491352350195BC642FAD22BE226A1C6BFED042D8CFA245BC9B8E37F34935E16BCBEE38C2E2E6A1C6BAE0AF26CE434FBE800AB343E1AEE8B9866DE5CC5D06D974D24987548D7E27396B7CD3A0D7F6CB5FC6D0AFB34D488D7BC05797317C291964A7987920AE9975100914B7534428C94A2853C7DDB76F257F56BD76697F97339775A5EE2108DAEDBE8600A5B3EEFA183776CF03BA6FA1834E049C6AA4351377DC18168C28939BBAAC8C62DD7680FAD0E930049F02050A6BAE60A735580B216CAA7577A22DFF0B88DB8037FDFD69F3DADC85683867E51E0B73582D1A60C335B75506A9BBE83C688210403BFCA59F9CDE260099B5931EEEBAFA91088F95C3645346AB29AD8B5652D6BAF84613EF4B557617DDC331B4B0341ACBD14D0732187974AB8A8D4EAC37E8A0CCDD4BA04450B583AA1513AB04F98945DB95CB705647C3EA97C31C864A11649F617BC4431E62E4AAE7C0814DA4A903A0DCD0DF6897BB1B01CA240F5493367AEE791C52346CA24BA1562FE242DB6B07359B273428C303542C0669D4B6497491BC7BBFF61A05E6B451B818ED27446CA6E8BCD13835D8BB47E78B195C038799FCF0EF1F9E1C783D0B958D00724B9EDC6E9E380B4F7D886344223189486768052364ED01880180D93B7050566EE5D4883D30A7308C39DD92324AD087BA85A3D02C811853F502C686BAC5291A739312E5A8021751ED24476EB5124A7D23EA4668EEADDBF9D65BA98478ED36BA57E229DBB8C45C3F3079D11079769001A5D0DFFE82CF24B937EA01DB781B66D7BD53AB36AEAA0C57AA7A91E77713B45728F48D8BA48F3BF22D6220EA468D5E542591E7768B602DFC0E148EAB45DB256E964A77FF9D146B2671A1B6DDFFA76D81ED52C933A7995FDB4A6EBF894B1960E2E6B4F8304C5ABF38BC6B2A3CEAEE8C184C0D8F460D90F2D1437A39121FB89847D4B87A98D94A31F8C3DCA31D78DA0322A4FD52E2F0AFE39893D096A113825D1B27971A8C241FD21B278E0A41D535D3449A352E41B2835890B60FA917DA98696D2917BEB0D2BFA45EF2C34B6D0C65DD28BDE10569D05B001C501A13B3D03647C74A2CF19E6BE8071CC06053262CFB6DDBD02445AEC1DC43BAE9B8BDC8336811EFA00DA2991E819E4971A6C9D20581B014AB77AE55896C0EFE6B481CE4DAC2F6448E500890721A07F6AD6AC5A3A6C9A05E6B2C873CAE652067BE7D935DF03B3C6CEB4B66E85B3D9859CD371D26ABB03A4B453CC1727BD1AC3C618609242CD349A4C2C39F437248024AE858D4E1DC713465CC35E92ACD586CABAED824314676C618ADD68E3DF70485B4C6569E5AD55C8375308EA5E6AAF6DB8CA799513A59C68CA50BCD077B221E917C07A81BB9BB908176C6546E99600BC2F29975DD920211B5C8086A7C9FD621B3240C1B2238B2A11396C275D1B70CAECA232F7F781627BA9EC664B722DD29A458F9CB7058926E05BA80EBD0ED09B1900C2C7DC91C38670729349EC71DC4DB8838F4D9D635463A35BABC8ADE2F456F678D107F496C97484C5B82C5BB12C2D65F6A360C68FB63D9119089B1010811A53A507292AA35D0BD8937EA20B44EBAEA10D29F844F3773C108D4843E2AA46C444E0AC110A88DC240945559E1C4299BF9184E3EB69B88CE347F8D7756085872050E0E40A510759C6910937EA1ED8A6C335D0E44356FC20A1CABABA4F3438D807BD1DE835E105D5B0AC775E9959FCA6D6E56AB941CC7CFA261D54A7D2B0A97678A1566C3F29631A2873567043737F6DB0220C44D730A7771CBAB35634176602992112FE732DC6BCA72970C1E0632E556B04CF9F083C01C0498CC14E632F8CB83A86BBEAC283A6CA3B820330ED036244FE6A990E86CF9C119BAAC9C56B800B6E90A5C4CBB2577483FD6AAA05FE4153A4B65BB0F061B073E55C5776CA81F5ACC2176A75D6938FAD2717A7235A62E7E18610C1A276DF6A3864F503C12D15F891AC86364D8A470BF08453F830784954FB1131BB25A67D51F1A4645B23996F59B917C453311426012C107439434403FC7A756515242FA4BFFBE3935F78D51748C018B8DC0255D68C03A2FB602E3C0246CF62E97866C9A641C0811EEA74E55D08FD817B28CF360D0C3B2426B2EC913A2FD5AA0E2558240E32ADAE301F238264F7F88BD300941BB9A5A8D102CD573E4879A72B47B05A09B8830D29F478299058329A6AFC566E0B4BED4A8BDE26E0162D745D7443DC18410A2501603D504327086FA9185C935F86AA66E18EDF6FB7C0F51E535239858C9B425881F583C21421305A4213040A371155D700E9E24CBBB97FD1B0F528F5F38070E32EE86F1249B31CE2104CC4BCF18616C482761A84A9CDD4E7850C3509DF4DD4C59791E076477C8E1C283C48207326E82A2C1DD3772F97E0A328F97C1BB37E6A207773C358E15A33D379CA9171049FDA53F6F9702ECAAC61001E9AEC1521D0C2A587431A9E1F54F9F8C215FDFD5656D296D0E6CAA693B3547CE9FD969D24B0E7D1855ECB0ED44AB37DB9D61576B37DB9C5727C55F6857235862E7B9F4DB90CE98AF6A636DE1FCB0ECA07BF71879BD1E9B8F085057901D9A013B274975E29388D887DBB7E8DBD491A7DA94640BDD40935CB7D08213711A852DE1691E9B623876172DC0D69F4D8C2DAB0B96D07DAD43084230CABDA4610B7E1AEB9E40C8DF838FEA7088DC096842F2DC05943F14A2186941052875108E59EEFD6B841899EE2122621F5135513606A94D264AA3D9FCBF346EAFE890C21673CD18DC6C6A8BD6982367F0C0CBE1E11CA428262A09A7BCD2979D897D2D7FBFAB4ABACE3CCD7ABF93860C6C518B21F51701E810F56F6105E27C2A9FA17DD52DB4922E8CE8B9F6B6C772C231FDA18C3774EA7B460D2AE3185626D267CDDD51122ED3AC219D68C9A8048DF9E0BC20CA67B63486C98C2AD3758F23CDF7807C4BD9D170CFB5154911D9891630961FAFA3BAB7E00440D37D6DB242C74C3C6E1F2146E153421B850E2B69AF4156A1A05FC60A260D0082721B4122D1D59ACBBB958B6160400F9FB937D71AB703476E0815278DA26E78DA2A09339850E30AF9C60AADB4218F101BE6AFC7C089D12E4C617C6E6C7272E393856B36F08ED83896D796BF30566D74874219F563167EADBD5B7A1873E19F507CF4431ADE8877B8BE2091B2C761179B546EFB8301D30A4D512D35C20EED3580827BE440EF016E03E5FAFA284CD92822CA5004B397D17E7F54580B62BB66DC124986AFF787FF8C3FB671FF38DD143F0463064043048E4C11BBA7B79C19BBA4A60AE53D462A68BD38413B746DF6A9A202E4BDDE7D367EF80A9AA59FF4EDA90010BFA20617F151B2BFB69209B8D7CAF4769E413445F0A8ED79BE57A3B3E0A3B7A7DA459F96E231E62FB6865EEF7C162E3B851BB168BB580D5237CAEEAED5E35647BB0338B068EC0076348BFCF006504DFC4BCB51F369E657EFF780D46F37CAF6D14E881FD32BCFC5B5BE3085411A405ABADE412D700C5DE460A35934FA007A73D76F1D9A0911E43EC671C75060577F32C561A15103EDFE65890FE5358DAD4487FA82E0C03F69C8AAB17468A0C20FCB7CC1EB0E9AFC1E9AFBC1B49F56C29CD00D08D2A1D1873ADA0C41778008B2EF767BFBE70BCB063F81B8DC07B3104BA93D3330A27BE6E7CB1DDB0643E413A3A9FE3E59753FB43D35C2BAD8A2C5A3CCFEA8B15E5A6289F457B1EF09E5958F77F2B1EC58F149E5D795B9F41FB0E5DE9F2EFE5A2DF2BFC4A55FE7C8C2DA44205F901927E844A51955360D2945A483C33A19C34901A742F3EEA5ECB03511693CFA6E185648DB5026FEEC8F0DFD2C782AC25A8F811823B577B2B124DDCBF6BEF656F34E11A53875B1B7748B713A163CB234E800E8FDBFBD9487EE46E0FC886EEE5B4242EF906E783E965A73F667BA038381F5D7B45C1E8FB15BD1609816953CC2F45C6BD130B7F18E30E8FB4137C65DB912D54A08BD155E8BDDD56C38AAD2131E16EF2C15A636F7628B3D05252CF3CA6C1BC011CA9ABED5B86F07BE8BDEE74B087F42DC8AB940CCEAA1E13E5B18A0B1AB0B1A622AE168A408AA7745A25AE193683F4FA9291EDD07A6AFE53D37CDB0E54FB76C148BC070A43F40F67C53E8E34FEB041C06BD644615E38555521AB873624D1C5AB4A15694341C723A0E3D489D4E8B64A1237851FFDA52E51FCE3E9FBCBDAE5DCF16A7BA8DC9BEA8328CBBD83FCD8E0F1D945582FC4AE153AB0651539E1E9AE0DC1B8948A1B9E4A373421E8166EE831741BB67926F9DEBCBEFDD075E6EAAEC5832883F9A0EEA330A1CF751A7A0594A3D4882091498919610A6C1954DBEC2CB929F16A420F89C2CCDDCA6AA18440D3A0EA1CBA012868CE0E25E170B4C68B4CE20F0A7230E11A1713E4754D661014A94A129B1C29E57F066454D87A0A84978A58D315C50A5701048F0DB3DE0D950594A0E0CF7C28DFD37EC4614B6AC3D2D35651800F9CBD154D294214D6B869194043298BC4B1D6F9EE0D867295DAB96908553773C35039DAC18C5FC82AF52542229DAE369C7B0B4F66AC939B16E53A24087E2E5CDDD03F4ADCF81AF9F40D3A8EEA3510FAAB7535E40C083ADEE5EAF903CCFDFB740EBE8D80578E8DCA2BC9C6306F87D22C3775C9566EEAD38A18F0D082FC2CE523FE08DDCAEB43EC34254C7DCF057E87B459F4570F3D0D35A13C40439C7B1EDE375A957AA4AEED93434154F719C9B35F0230A858F24C1E0BCDE1CFA90EB1ED15843260F73FB5301007F51C116A169948B0DDCC7DA21CFEB30E79AB5A7E09566ABD3F196A17B0D4A68600E8F5F218F53471AFB9425D6800985600AF321343D5C8FDC647861DFACF792959A40E51A9A1D04CFF792B9C9AE8BED1ED249AAAF05BD660CAC4C1D2ADB508230193F30FB0F055328B2803991525E0BCC8AB16498C7DD5737223CC9C39F201CBE25E0B072E7EF08CC42567F021E131741A3F4415815AAEDAEC12C3EA8FAD1927FE3C3EAAF2B203EB3838A01B0F4283C1EF42D396DD1873F954750B1273035B0D8B8A12703941AD72D649F317CFAB6F3CDA6824492BEE4E36973772B2675511401CEA6128417761A467C17874F30194E2047CC5D7BB017E12F088BFEDFD8DDB86A0F968BC0A7A50AD1834688C70DDC9F6DBF0555B514377C6F771116DBC72F56E417CB8CB0E04747F1BD69CF4A15AB48EB0B672EAD8AFF647FD30A3F8763A18AC243E71CB6A67A42C7AF2399856115F6F9C11DCDBCF817FD15DEBBA09838B1F23EEE2C0C7CF78BE9FB5A3781014E21925740614E9EEE8F23105A7440A0DD210E72E31200CC7288238B09A1D55C529A42A84BE5C0BA5A625DC57BDF9D6EEE0560E12E5D3A84AEAA754D077772300EAAE46587FE4E8DE579562E8D9E8A70C884136CC2E4F74189AE112F1218E6262A473C7485CB8A9F9886157409EFDA70F61C4E3C79185D1F942F9DB30021F97945ED770CDF11063DFA9CCB19AFECB95E512082315C249560C2345FA008D3F06B50758843209C9378BA9D9DD2676B73381C10BEE249B9122EF63CDDDFC34CB55552F09C2C9BE01985A04CA0268612E84CFC9B96D1D6F8CA6868168E42E4499F5FA7EC60FB73988FD6B554EAFB50643415D90747449956F1312DACE16B8F736A16050AE0BFD98C82D98F1EE15F13A8F7EB59463C165439B1DA91A408649BB576070B4E741BB72C2DB05F83D64B9EB3607ACD79EB35A7AC963D64AC8C2FB97516E2C9B545DD92D7547B733139D820982396AFC984B16960E5C1F457C9F5C906A7333EB379B27123F6BA5942D0758723E0083F3E72434FF69D059E7A7DB9CC97CC9CCB31AD38FCB95BA12E1A86C08C97EBC61B1432D3EAD69FD56BAFF80609D67A60229716095568BE2E0F9E59E6AE1F33712D5EA9C7F3C6ADA42AAB9F58F62C353BC716B31BB9C53E4C0CDB020F5BCFDC0D0E84E59AC7DD17FA84DECE74FA6D7C1020EB9ED7B8B98000A5C27081F5230EC20037F92C0E17319F5E6B1E98747661A7987E37534E3321EFF8E6A6F3DCC4168D7EC7066F60125BB5E19FD68875F66BDBB742CC58C03BC2F33E7C860DA1869F600583F7174AD7851F707A99848E0C886B36A8FC63565D10CAE2F610E806907B4100B134E580E6EB045318B9C304A36D41B82F960206E222D68C36E3774EC4CDB386C11A3DF8B97742B1EF83742C0DB5EFA4A04DF40BFEAFB9F32687C01E0DD5D1C1E0B5770775D786EBB01D2E9E98BB94B05F5476BF2AE18CC1DCA56BF72751185D8F68D2A12B0E71AD1ABDDCAA435F78A12EBD80D61B269C1C7D80D71B2632E6701E79B27561B3603226C9A80402C064C119C6BE50D636F0AF350D21A732A39016210AA9572D7F93340F14866BF280FB35711A3F09483CFD7A3836C3C969E5C217FC357D96887A6FF98E51F811425C0C8EC268B843EB4433F43F70066EE2521F04C52E77266EA5A8C65763D8358E5C68026348C2AEB98B0C43BAF3E7A8146A15BA8C126CA0DA01366624D993E34B60A642C980682CA967288F295CE9603B2F980B89756E23CBB27EFDF6C478AEE30BC2BAE8C34F2E9F490216FAC2E8498DB9966844B97049C7473EF8E88E8F40B25C7436A9E9D1EBA1339699C6753F8BAFB2B4935EEF5AEDC5B635CE5C4DDD78F18C2B52AE4E1E5BAFB336CA9156153AF7622AF41F573C1BBAC6849E4E4B6E583AA02251B27EB60814CB243B5AD0EC2D8C801046F05046E88E0C1CEE0B1EFDD62F09B7CD15C9C074591B27FBE517F4FF98BFC8F21187538D28BF9C42225DAC3FD68C63FC989B364FDB7F21C8C558B4F66DF1A232C85F10592E115BA3EF0105E39CEEBEE6C2AE08D603D3B14B3A8803066D8D5A832A2706344A0F8E8004546F4B4D3606E6E39569026CE99526EE8C8C85E6BE9AC75C194415C3A18890FA1B955CDA1204261BF141D01CA1A8D2A3C891E73A938BD020CB93197C8D1ABC4D0E2A0685A74D3C0A2EF8CDF00522C87D09629A0284470F2F855CA3587EE0C4D42A1E2A3BB914DF7B29549F53D0195EAC9568AB9C7E7994AC335F89996C19F0E35247E4594A48F27354F2559BE0ABA4780B82C64958CA519CDFAB47656CFADAF2CA521B3BEA52D95B3FB6F1D348F0E6A62146D0DF5FA02C2E2DC401100089371BC92B6AAF172724403936331B304FBA0772A3F385BCC98658540CAB744D68A837F45BAC07911F6BC05B573F4D70864A5DE84E1949B2752CDB5B26A580796BE4575A4148F3AE012F3EF2130C03A046E688D87E10712D02D6312314D14B98418FF41CF90F7CBE7928B215335F9458B6C9F35B712F39EC901596BCAFA84EA42E5829C96A13DB81EE21C2FF2015749EA497AAF62697491E732DCE80A8F56EBB05C1C0F6ACA03786247B061FEB16E666DE198902F5CCC2276118FA93768D4EAB5830D7CFC204273D2656830A9C70CC332FA910C6BD8F548F186F30AFAC22C2C712F60E486A60D8E0C786BEF8A5715900B39C50BD36E38C3B82B21CC95BEB0CB064B10F6DFF5C5141808EF9850BD2BEAB3563C65A57C5F78BEF17DE2FBC5F78BEF17DFF6F9DDD932575743121B667C7B7A79784E93FD3EFD706EFA4BBE5ACDF86EF10F1EEDAE7B3CEFEC75E37417CD8C5D47C5EE65F5D7B94E659B6133F5696DF51C7A794E5527ECF28EDCBC6F74DB5F55DEF133D2F66DDBE95CBBEEEADDD680988D26866AAB9AAE6E4F4FC6E5F90DBA3F5E7B11B8E375E818F1586325B3BE33397CE65FD3ACF4BF2FBA8B31AAF432265A7959D768B8B8536B7F17C4ECA86033F7FC9BEE6CEE8ABCAF1E3F4399F8789D175C06178C970FF9317CA4CE6E1E7635B2FB8BE6765BE7C17228BE8E643566F3E5E867696F6BF234CB386F9C8FAB1D0BC4D9FCB07CAB9DF92737112388E4D0DAB2D47BEDC75F6F03E4CF6DB19EEFB9BBE19BB1DECCEBA65D2093CA26CA7CFCA7B9F5150F5B5F5F4D1D7AB39D9A8AD9DBB2CD216F119B99557F568DDB43CC337901492D767DE3F50F63B5D0DC371B976D8F477DFBBCBE34CB5F8689DE43CCAC099BF2EF1FADFE5ACDCEFA1F237517885CF27FD99DCBFB266462FEDEBE23399CF4B2D9DB6D2C1E618F9E93D2EB7DFEE3B677D4D8996C69D74FA9C6CF1BF8CFF2A63311351BAB474E666D0D34A4D76E9ADDF5FA4B6F1CB7EAAD371757983B8DDA8ACB7CF11C2C46E2FCB926F3E2E7C1FD1C657B1A6C3727DCA585CF4F71361DAEBF238B1B519BC1F6F959FF33092999FE1473395E5E8AF0961FECB73AF979439E76545A67CFAF01A14D13B256E9747D7D7A9BF5A01EE8AE5B8CD6376CC63A01E38D1935FBBA396E159AB39B9396A2E55D6E32D4731B797E4D078F0BB6C7493E6DEF37FC8EF775FC33BBB6700C267EFDCEDB7DFB5EF9FB975FDB49B55CAB0BC7E9FCDA4A4F7DFAE7CC4AC2D5F568AD0F517D093BFE5FF5606373CF92F8F8DF4DEB8766E8FBEDDE3DBBA6D123CED75CEFA787F6CDA9A4F893FAF9AEB196FEE4E3B66F258DF4BB3C1CCE6247437DCEDAFA4ED9CCFE8ED9C6EE53F95C53F1FE9F2DF7E9C37D7998EA69AACACEA4DEB30737BAF1F8FAC98CAB5F8B91C8F57DBE27C13D394B09E8F5A9BD0EAE67E8E26671BBCFDFF5BB6E3CEC3DF7E7272FCA7CA53AD8F3F37630CEB7DCAFA6955CEF532B4D8BA6CCCBF53599F53A59BCDE1357B6CB707C86119BCD7D2475CAFBCFCD7A3EE691EFE975C2BD7D1356A74B8DEAE69B17377FDDEFE09EB7BF3FB58FDBF9496376D91F8DBF26714F0E8F7086BB9577B76D39B90989CCBFBD73EB45FDDADF8A357BCFB79E74F91D9E35BE75F6CCD98E65DE3FC6C6A44DA9C36FBDCB4FA733CFDA795B5F5212D9B97BF7B5949ED3E4DF1FECDD753EAD340D3731EB8D49C5F5B05CFE3756AF6DF5E6B3EC39770FE36A9B92C85CE6BB1CED24C476930BBA7FFB71E9E53411A6B7E05FDDA3322F4659DBEBB31EC66F69E955F15BE732CB3879DB54873DE75FF8523E72714C12F9F39B6755596B9E67E3ECDAB2B471185CDADE66933F81C346F1369F563F91CA8C7AA686A9BFD2F9B89DA6F3E4DFEBBE8E6EA387F3E16A1BEE20EDBD6DEEF75539BBE041FB7B6C4FEFF8B6EA135F68F77EDBB727F8DD71BCDC56FB33CAF26F3E9C66DA670D969E74B666B25FABCA8EBB79B36F5B7F8358F73B6CA64D8F65D474BC44BA39602989228B4C18450C912638B4DE4B4F345A6F691204037CD8D391469C696DDF0B4F385B7469E74B6F662D3CF16DD1A79F2DB9A5A71C5B7469F77EB8ED20B932D7AD560BF8E63A9E4FB30FE6E86F57F88FBB87DA67B8DE730F92D8F33715109B82556427318AAD7469B73D5B3F571DEF7A16BF9B61A9F3BDA7F92CF72F8F93B6ECF04E8EBF14B6C9DE966E3B40B0CC830B46AFE7E8F9F36C54A8E7EDDBF1E774EF78B8E3FD4EB2EC94DC7ED7F5DAAFFDCDAF3646F968A0B97C6EDF57BDFAAE79BE1AEEAFBCD2930DD5D734CDFBDD1E85CBD084F5B0B37D4DC71F5684FFBED0DCC6B7E5D27D3F03DA85EE9E563A3AEF517861D1FDF9FD949AEC371BB4AF7BF57C93191DC626CEC3972959A3D9D3467E1E8E23032CFB7BDBDE33D3EA65FCE81ED4B50DD1D663A366EA4DD06A31DBE9CF625E16A9EA9E7EE5F1BCFB785F67E3E3C76A5D69F1117C076D067ECCF399BADD6D3DBBD526118BCEEAE973A2B9E461206D979FBEED3D8FEDE7AEF114DB6F73DFF19F21313CD8BF990C5689E7312FF3E9301F43CC6E8FEDDA7DF85BCD14E495FEF9A9B4B1BBC7FB59775EA9DF035DF7C2641E0B79878CF225FD9FD95747A0430F3907378A76F8B119DA07FF47A9C8B5B5F6B88FBF266316A1DBDDE45DA03438D637CB375E6789F45EF41AF8D7DA1A3A499B534D85AF13B357884F9DF36DFEEB6D6531DA425D25C9EF73B71BC98F322F1F45BFDB3DDB5570754AFA186D952F9FA2E845E93D0E06B721F14CFCFC8AA8EBCE6BB7F3F5325F86A7D85D95BD78F838E8BD8C847ECEA7E0B4C850C9FDCAB2FE42774A0D5C15BE2FC9AD6F372B51BDD4F35B6762760FDA59FF9FB3F1564DF95898FE875A43433FADCB5CFA7C2C0DD380EF38CE068267D6E25BDB53FF1E469F97D390E267FF0D47A1A7FC7771DE3C7ECB3352EDFBE5A87E1DFE9E2DE768F95936965BCEB3DB30B6DF1AF8F5A1B649E9D2F93F8E939905EDA8A086F5367C0BDEF26E89F9E66F5D8039F1F75B3D99B760F7382C0F066F8DBF3AEDEAE63B7ADBAEA31DF756F22FDB38EE0E53C78AC2E1B576B871855DFFAF9D2C6ABAA44B635E59C7896714259C54DF6FB7C920E0E0D20C1BF1F0DDC506EE2A044754831F224614BA32E4BE5B36D81FCF796E5FABE8B7FFFC49FE59F20C7A383AF00798DB1FFE9BFFFD401CB248B49A0499B49782FB5E1EE31247A4CC24D0853CC24CF85AE9430F8B48B5262429E5926742D5224880A31E933816E3818E0F2B82BC705E8DFE5C7039B8B8F8ED147462E80268D278BEF17DE2FBC5F78BEF17DFFBFA0CCB8A47D326124BFFBDF9DD655A4E4FB7C6B3FEDBF2495BA79CD222EA4C9FCBF94BBD43FCDE5FEEEE197FB452E2E235FC49A5FF12B95BEFB186B9DB7E8F8743D0C9FE3AEE26CB0DF27A8D36B0476DB4DA9D25EBAF7932D077BB8C5B59834BD981A395B2D7E0351672FB7DFD7656EFB6903266A9BD5C717FB7A4B65D5B1DB57AAB1EBAECD6F2D78504874E65D4B0938E07079EE05CB810776F5DAF6D0DC041D4D9B849701110452A2E04C4ED82B83D82AE170702E0A2761A0894DC450EC0EC2B62212FBFF7BDFCF6CD8D57B0170DF868FC547E0CFB98D5B8DB96EB25F16AF54E9FB9BE751EC542A7E8FC663A33F3ABE9D4EFF8CD30604E606EE6E3942C71C88BF0B9FAAF5DD1D17B6B6AEBD543F1046F96C555F396E7B60B2BB8E57DC79B6F07DE669AF454021C01861A0F504272013E0DCAF157732C1D84D04D110CECDC3FCC7A1C60B2D69960C509FEAAD47A1861BB749C0BE332C58606622AB8206985D6562135D82080D896AD3AE371043606C4AAAFC13AB75621D08B4E28023F0956A1DDEF4EAC09C24CBB562888A794CE68660B042CBB0D4089746602A62E00F1383C0EC576DDFC176EE06D6745DF103F610342232C4EF9B12A945B2E2596CB8685B64F2FAAF82C8B31AACF97BF0C36FDB0361CCE1746A715A6D1C3659B616F574B43434B55B2E255111198BECC5116CB18DB97AF60C17CC6BD09C3C321C2F8C167577B03C149CCAC8E03E5D556B5A1B47858A2F4C399A65249AA3AF54C6DD2026B5DC31A0A1C70CD3EDF6EDDB75D162C45D5412D31643757382CF6AA8B672F558D36AC85C07468502BAB5DC87FCF3CD26C26956F4186186FF5CF4CF66E122AB9B22AC9312A690C9A54EB92981F126A3511D5BB6D6656283AB3E2428D0B4CD2F93566690ED1ADFD17081D44B84CB67C4AF971241E8DD2839B95A73DA67A8577364F70860BC90195CFA672C864B807CBAEBBD99DE5694F36E5B7E06920879C1E86F0914A2B91B124B6AA9F7584DA316DFEE85D6AC1C3767E9F03A303262E006A16CFF7D8449B9A9A626E79A062BAEE1C2863868B66983A7621C387086943F8D04F1E6EB019B5785DD73349F2CB4E1D9669CFCB2D278E7D0BE967F1F1D2BF920F15E3158AF73F70315F245EFBE85EFBED35B1B083C20BE483E6990BE69965B8784C78C963378BDDF7D07EC6C34D09E3358D0F1C7CD320FE1E159613D0DF34DE38F18BC5BE27A1BF4E3C62F16F89E8A7FC95FE95FC5C486FF7EB7D07F17153A3F06D8B7D18F441E18F16FF2B2FE2361B200FC4673C3B3913EE81D38ED97C03F3F04FCF47FAD86AFE156D2D3F0FC4E5EA78BE378EC791CCD5AFE4D866CF95ADCDF2FCCD7F379D97F08F11ADADBDBD47D1DA18FE1518B164C92CB34D2CB93262C43F868A3686A295AB3CF4D33CF56ACA29511E1A8A7FBE3923D1CF38B37D43CE7F85E317FA9F9F3F74FE26E7E89DF31F9F77D7373D7B66E7DA3F3E85C373F3B68DCFE0EB1B9FD1B67DDBFC86E7F5F43E7CF52F6A74BE1F43AB1FA5EF79FF529F8B3D5FB6D6DF4FE5FB3D4FBBB1D9E529DEE658EB5FF737FA98FB39DAF67F4D4DFCFE0B1957FBBA9C18FA5EFFE5CDB5D3F8BF6F6B83B98BD6DB4F9CBA42424247FC09EF1E1EC23BB1EF471C7D88FE98FB51E4C7F9E3EF63971D492AC9A7231259939D23525A93A32269624242424242424242424242424247FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55AA4B00F10E00000B0064860100000000000000000064001C000000504349520210FF7300001800000000034B00000003800000474F5020414D44205245563A203030302E3030302E3030302E3032302E30303300323337323830340035393231313400BE94000078FE0000262088EF5EE15B49DBD809249292DB69C8050680890082D54468572DB60BA70BCA1C8928D30222A2039598C8651A73837AEA2D09684938451CBECAE775476728ABB6136D6CA0896942DA22A0028D51C20AE772B0068AF4003167FFDFBCE5A02BDDDDBBFBBFB7BBDF7C0F7BE0FBE77BF809EFE0DFBB876B246DB6E9BB269FFCFD62D3FFDFA9E95A34EDF4CE7EBC3B969877932DA5DA3CAE68D1FBD630F2CDDA34D536E668DE33FFFD7F7F1634FE67C0D5A4CFFEC0377FF397BFF69A34F1EF4C72F53861EFEF8EDDB7FF483E2D4EE2A541F785AE603F795DF60DEC8E79DC5F6FDAA9DA90678006FE40C367EF9DF9FB4696D8BD031A820EC843D533F7F6E9F1F8BA9634F9670D1A55EF1A34CE33F7FD74F51DC67FBBED981EEEC8197DFFAE9FFD45782B7C1954E1F05FA6B7BC9D14DCB593468856C618E9C3EADA6D87DAAA4087DB5CB44E3B998197063CDCCD7B469022D22E305DD7160B9F52C4F077E30727B11B98F7FF76B8F06DD359CEA07092B534ED406BD5BEB01C8294431745B63DCF54C6BAD86BA96836A9ADB6635327009C841807AAE4CC272192CD569C3D9BFECECFD8F61F90633E0315F5C414ACE7A23E3927AFC718B571610E27C73CB2C487DBBD9E19F84772A54A020C06036E48EF20C5CC66006DE37AC60391D34E2A4F5E0E4BDBF5CB0A6F96410C5CCBDA74E2E12E0FCF09F36C48514DA91232E606FFBBF5B99B8F27D7324DF57F03E93DC6061F97F797D417B4F77B8D9CF812008B3D056EB7389AD8FCC27166F285D945BFFDE3E01CE79800208E1E8375062CDD21E6B9487D232BC37ED4C78C5B1ABEB98D5BF3D5CAD4B3997C63328D8CCA0FB333EADA9E672F6A799BCFDFF5E67BAC665CB199EF1E600CE79395AA3D08220EA081BE77AFCBF609E938356C185DADB0D3F0935B929ADC44DF6D149C4DF6E1441390DD937D71CA2DDD174DE80D3A9DA415A6F523C2051651D2A8A71F50F758346BD2E1068AC2F0F83EB253129ADE29E5C18F58F4952202DB55F5D83099B47B451D26B7F1B8F30B96D7DE74BF91A926AC09E008A0040D7930F40613C9C5CB3D78484B3E0C87EAFDC4196DE56A674B78FB6886A3345E048871F2BF4415B6CC9CE1ADA5A0B14D6B6954F3E34C9AD6E86A2E872121B3BE77690A886E10ADF9079A3AD6E950E59EE490ADDE1916823AFA2F4092310ACD99E0F865E9A4EF9C712F658642D48EBFD168D21513642B7F4169EBF1F3BA4C73BA7A481F790ADF0C60243053450AF49D80FE0BB7105F7B2A67E12345F8714A7703191C526DF64A7675752DDC544B42A2CEB853368D5D53B6CD95EC296872CEDBD45397D748AE30B17A36BDEF46BF8D5264F959B3079122AAFE4BCBC3096383209ADE4CE9E4BC932C7F6EF5CE8B33379C33D9FBED48B2E427814BEC75A5E5B7329CF0CBE613D6B422ABFC60A097C3062E13585EAB59A6B7E77BAA2203A4D99218E6F35DA05CB4D6D9A71730EE0C57E48988E591EBD1CBA3D812EF91C3B57F6246F938B4F2135BE8A5A00B7CFDE2516A423ED492B662CC05E0418FF7367EA1747ED405B78C1D380FAD535B8F0565F477E4E44E3269A0BE5D0ADA69623D1A87A401AFEE60BED46F57A780FA55560C737BA01AD304000B445CB0E7181793D8DE235361C541A2EAEDF681342FE9AD84D88F0F58892A51F45C8B8D00D66BAC65DA34B0705D56B7D132AA01851D9E304555EB7C76713E157A41D9E6BFC7672F77D9F821F169DAB746992D12717421402B1EC43456DDC05BD84A26BDD9AD0523BB5A49D00EB4B587A93DC3868BD6BB641D5BDA04697F8B10C04EB7B48CCDA2DB85E61E913595A37AB7B4BDA620B4A86B97763238988BBE797FCC392B89A000A543D5C9D9938AD61C7C687F41803957419F124364F8B6E187BAD904046573183DAC18B54F5E71B730F80DE2E4B3A18A1ABFDA0876EFF599A452980599C831E9130D47E8A909C6E4D51AD48DA6F8BD618994E08D5202B700350A7838A586A2A15D37F83667C222825ED449BECB664027AA6761269FBB88D0C4F53AAABFBBE02B8D46C08B912643F2130388602D297C14E90CEBFBB872FA876CD80DADC0DB71D1B82637C348DA1DEE8D62E9F3C37B476AAE3C6BB5787648EDF06BF763D299F7CE86A3E4E5CA941A4C357EDDEEF0986D65AF3CB3409B5C6C4690814E9963E728A1E387CAB85FFF81DAA1D9DC79411B52A2D7D0969EFE3BCE9590ADC0EEFB01E726CD0AF58B357F0FA70D7FDAAF305A0F48B974A2EBCBEB13283D311D811F98B0C9B7E856FC44CA85BD95F7D863FB46676838D971B8496F24817A5091F2EBE3A7CD8F76E89822945F26DC706BF166EF0529249161B3D10E048B32C2C53A2371AFD612C12DC64A875FD48F1D12A857E454AD865BF532A1850BF039E337155901CFE9D84BFF1B526CFBEB945FC7A432D6A618B6A01E9730D57C6301643B62494CC8F492E8B8ACD73C48150CBA74E55A27A4B537FC3EB9A858834A2D796ED4A65F43DC9C02EC305F477FA0DD2FD34DB0413DE10DB83997E93D5ADE21F5138321658BCB11ECBEF6559F1C219A6E8A299569385A0DD119EE14D31BFF206FE1DB3F5B705E9B37866B9E039CA74605CF11ECB9133016EC06E9A6ADB293B0A95F7BAA5F417542BF74BFC18A1C8393CB8D0E7A5D81E1D215D6CBE8B0B7859E1848E4FD6BB63B5D81D2F762E643ED307AB96612AF1640872B0A612818270136C4BC37E584A4E9E6AF39E7A5CF2D75FEC908BF26718609E18BD3B8CF3037FA179C8DFC93FCC26BA7F43E912B82B349C7CB3DD00486E8BBA0510D39110BCAF1F1A74A93A5C06770AC9C87837FE231BF1D2A1A80E4869C5864998D952889BAEB22CB9BD0310D84BFF03609FE6F4CE22D6222A0C03F97F632C03A6DBC49BF7BF61B26F26229257F026B78868B51E6F2E3DD7F41F1EA20AD6F554430FF3161CDF597986206873BEB12D7648331DCFA470D95FFF3FA4CF5A99BB66DD0AE73C4D892F8FC2825B488E7F9143BEB0452CCBDA9287551CDA39A47B447326EDC61DB6E938B9D769C5D04EA61D668E583B69E506F6996E0405AE535AE13A2C2791B12801F966A09D5D5172145D83A52FD1F42E82F763BA0349CC0DC708958DF59AB0596E12F87E819642E08145A6E8183AFB75FB09865967ECC5F2C8B92F906C70752FE77E1BBC85F76381B8FDA6861BD1C9E8BBDC92D39F026FBC6218D59D63874070EC70317CB246593D6A71FC225D68D6743E59AEA6013A2BE293975629E92C895CA8F4E58B51F3042A4DF6D94C1882614E36E30DA72E0AEFCCB8889D8F7902559C17DBEA88AC81DE6973D88BB15FADC218E33AB6E07A2A5107EBEF576F9F5B3EF685359411E9AD54F533814F32215F87B3788E6416F10F33B280EDE270FBE78074F7D35B90A3FABD713C4C23D88D9DE415B4AE9A99D2463E51A9C3B1529AD920C4973C0F58888644A0D2D37A05E5B565EF90F13C2D834E67DD5F57C32FA53A31B3FD651FCAC6861DF6B4D0C33DE873527AD1CD9B1B1D81EC447D2E7F19EC7AE9E290F27DA30EF8BFE439DB41A4CBFACC49A022AA9BEE7D0E6C7375B6353381ED19C4EB774A4C9C53C6213AB473D99844C093CF73049F4F57CD1244333CFEC09B30B1C7655BB552606B6E0614AA70535CFC3B06253B1E90F13C4F54F90CC82E20AFE6CA1AB28FDD38B9F7A133947B3A70D6943534A71FA9430F45E8E608F6D67EC2395A523F345A5F4CF07F39A36C64D817F45B0274EFA3E99826888EABAE7402B77AB29565E31A2CAEC0B2F4F983F5F08D5F63E6E7840C1E8F9E659F77E794B7D9F10950055E324E63F859D263847EBD28317F696E87D16E97C27847C3539C433ECA97C1D07D1EA98EC342C6EBCB24DC4637FA2543FB493210D364493B293BC468F6885694E15068B3CED0AC608AB9FDFEA903D3270D6321F653CFBB0FA0C5CF4E8D6A6A7443531BD52D84CC18E663F83E7734F4C07DB4AA703589A5439EAA293336CBCE1BD49CF5E7CF523B1B84E3E7847ED3767E718EDF079CC8EC461DE5E70C86A34AC1FE9E21788EADFAEC5ECF08D73A51F53C945FC4D24E6809CA7624252995308A810AFA45288D694C2EDEEBF9C17757FF70BA70B715A7B854C3AEC43919FDD07C0D993039FD1D79946D2D85D4A94488D4E4B7B9825BDCE1B4A49A4BCE691868FE81E08FA5DFD7202D071602DC7A68FAC23394B1AAFA93431E43FFE0D528DF2A54769A2E9125BC23630D778C597023E4D442BDAA35B64A3746EB66CB269387F33555780B6F928DC31B4434DD0362E1947B9D077DF8C0ACF9CE69AD0BB42B7B6340CCABA80B7B4FE268740BF55E6145054051A90D04F2B3DAE68D6EE3D2F87CA38F1537D68A4D9E32FFC350C60C5C90415CACF215F92504C7EBE368CB94D0BC8B2D5B62ADF8165575CC1AD356AEDA788F97DFD7327C99A15D37B9F34CF8FCDE69ACA12B93C61D0D41C51D88745E8DD5A52681A8097BCE618A52F8FE8743DE1306F6CBCF3761D64004A23248AA2798F8A7FFB02E73BD396FFCD2D35A5DD18165DD018D5FE53541C5BBE864CB091B53A0E286DBB0FA74D66F4C4CC609F76A140DD34E65102F1EC096B2927490FFCD251362764B4D1E8C71AD8F8DD082EAA68C8B8A9DE907845A9848457AC305A8B429A98D1C5C1309EFFFC3F49A9B009AF023FF80800C354AF3526D8556C203E032740A4BBF8DC38B5E5908DD6D194AA4A8D85F1D2F0F9654882BCD81157FD1AA1A684682D5E7034D6F623E563E4D142BBE0340195B68BA7266227DB6ED38F39FF639C839219D50A1D9B6A5A0F9138BA73702B46C9ACC78BF6ADD9F28565F1B6851730D9FC6DF2543E79BD43B8A68F9A529687B6E5A853F6B7643FC732CB7456A4F1D0AEDDC54BC3708D1FC0A5F9967602397252A7F771493F72BF0338CBE6C7BDD59858F81F5AC7D4796236961CCB05EE757EC493063F5C1A89DB93148DADD056143C1CEEEBC8E8412A26537D57E83E9F387F46D8B60C1407DB3AA00F69CBCFFFF40C5ABC90334F9C602AE9BF45DA078DDE8E6128BE7994F8DC12610E70983952FE36D09853A0FAE14EB365CF6A5DBF816738D20816F7A0C7443C0E5C737EA799F5BD138E28DBBEE3D55F4D4EE92862D24FC4A4AA5E7E65876A4F97574250DD30EDC3E7D980EEC0A2352E6C35221D81CE7EABF5F167B9E393E0B1623D886C496BE23125AE4945FE2EA5A34ACB154BAF5FD798E750274EB6512FA1CF124052C7BB92168E9278FDE90164A42A52DC3D53104D452608652EEA64DDA76CC1275DA52778D24B8B4A4AA6B7907D93E6D28DF988EF52E377492DB5831F4A6D2FACDE894FB73A4297B6AC4060F025BA7503E2CA56D61EFD0143B27115ACAC9774F50468DFE864B8A57C9D4A97D46EB6503496248674B47BBA8B5A0B1122A588E8BD8A5A0DD52C6F5D2A1F0CD2B7A8831F49726A195EFFA8ADD92931022BA46D1FE31AC9BD5345774B129F188DD5933C4254714D1F377F4970EC37CC37BF3AF0A8F9315733909ADD6BAB57FBF767014044B8A99563F2A9FE28FD418F8F78447FCBBB43E0ABA11050D47A4725DB9EE3F79A28AA211CC8041C886DF58BE09967BD27174CC1ACC802CFE0B10FF153D310AAD3D49F0BDC3863388BDC868529F47CA05B3B6B4D314474D6EF4DD63829177CEEA0965AF54163718B676C9C5FB9B3FDC6002158ECE81D0ABD43A328D26DA56A6ACC522780963C05A93A080DE92F769A6703876E945F38E0F18BA63692ACC0551745529C5C868282C6D127B4E184E93DCA4449EE563D3CB42482D57DE5DFF3C53D60E4D28BE5122BAEB6043A5660A177DC2FF76FA303E2CD08DAF4C546651A3D9AB964E3E1278A20BA2C4E187D2891C4551EFF8A10D52FE0569878A742226EF4BD225CBD9A70EC9B409563092A2C32BF48D3F8BE9314AC245613D3ACDDC1A2C42BEAAFD05F8E810EE128BF9684E81B7B64D6EF697E91B996321FFDA5F12736AECD4A84A8BCEDF95001FC286B8FC30BA1BDA71FC12F1D6CB2F850ABD617C21271980261553B8239EDE02DEDA31BD846C254A2EA6BACA16428A6B1EA74590A2F374C7C5DFF1E857D61F95D5942C7C5718FABE44B546F0B509459AC7AF8CBDB1430812520811E2D9B642B3EE8F1B5D31508C2ACD0BF637D39E5C53C073742A22D811F28345AA236AAB7456095DBC5FFCFFA4A95AE6C6888299E5BAFC41EF41B506ECE562F19D951791414AD1D96EBB6FB2025B0FCA64E88B033012F8559FEF012CD634B1B2EECDDCA2891F14A138DB2FFC25986B4E27FBB1D73F8D60EF9BB972A5523D66FAE6AC050BFA57A60BCC105E4E848D27174C73AEA946946F068060A51B4D8D1F51BF2F532E5C87ACFCFEBCAEB0AA5A970ABF8BCF994014993DDF52597D75FC617BC74801CEB7D439E57E9D61CEC339FAB9D61066FF8EB0F3217A861D0D6D6178A107D80825166F62C6CEE4E01C63637F7DEA312796606BC6A3DB1642678EAE9E4DE15052A75F1AF981A70C585068BB77B4EADE97D1E382F0209AD967B982DE53AFD475978A88857A7DD26DEDA02DB34A870CA4BA72AC52A17AE95E30D4356A11E14032499B43D6B18515AB29DC8C63E8A4D004F88B8635AEE72BDDA69897E9A92C9B56561E405011201D3CAB64ECB7F38171678C4EC80755B9B10F97B8F73940E2DE590365DCD9F7B9D376F0A04E1A202DC1C08BD9BA8023AA811742CCAAFA4E09305AD2B0EB993747D527A98959E3B9C105BE000E1DB8F8C43527B34F2207AA93041E105C1C92E5C31A5C6B72D050C824E91ED8F6E59E2D9339FABCF3C0A43CF4E40AC3E5EDF78F3C329DCFC8C67E55C167E6078FDF2EBFB6723E2A0BC7432D21A0048B5D9C1AEDA0D752C7CFBDE496BB3A944CFBDA0205284B3F4033FF814B682D0B4520A2962FA498B71431BF679E63114976E07DC31F6D6045874EC66EABCF25568517D37A4CE118FA018E34C56291C0B8BF56347A81A37F13D37BF91A31F7E21EA693523FF5433B47E79FB6B07977E7E5E9670706306C460D48C146D29224D0F8F1E6C7C6AEF63E245F8676E0785B4E23133D6E0CAEFD7C5E18B9DFD87112D7F1E1095BEB30270687953CD8B0B6F0A2FE3FE8BD5620FCE2B75FDFC29DA55525928C9CD496E946F65FC6F6515B6708530ABFDD59064F042D7109649C3C1C62ABC9C81336E0060937DAC64BD8C3C4A4E50FAC444A416D2D2F6047499211E2BB84FBDC53054CDEC7AD59216E2FA5B28516F901BFB30969C743255E2428BD1A5133DA9B4A4F0DB840BEDBF2215D92E5215D49C987C04255B59701D07F94CDB6B1BE905B146B571725E290254764A73D7807C6AC71AA68C5A19F964F3C8576E4BB74DB86E278BEDEAC981933E018C02368B65F062A24DD698EABAB11304258C8C56F0EE8A344FCEF5E1D602ECDE405B3D3D016D0CFC05BE3BB97CFFB5047665EF0F8112F85CC15AE115AEA962CFC07DBC53B142BFB26D0A1F29D685C636C64D348FE9288BCF72E2137B751F23B126B1A4B7703ABF7036B17FA78A55D86D14F6C2FF28945CE4D4CF22122FBE1A8BE1EDE037F9C6D8606FBBDDB45EB358D7FDA69879FD6273CFB1139BF280CCA9289269FDC0C11EA8983FD4060B50708288750D610D4F52BF9E813BEB7A0C0E65EE31289997B411FAF962D4CF967E346A67DBAFA540DAAB997BD44249F0871745343D464A21887250A50D6F1E37E4749BF68D2E6523A45CAAC91CB3E2ACAEDDF855D9E11B33C2AB5CBF8D91622D0644517C8B36C42C77C81CCBE715BBA55C8730C99FABA3F015BE55F7CD6529725E52C7E5CF55D1B63F26EA4B8B8D4D936FB893C02848822F943F277DBC462FE65D" + diff --git a/opencore_legacy_patcher/efi_builder/graphics_audio.py b/opencore_legacy_patcher/efi_builder/graphics_audio.py index 93954291a1..6c2234bdba 100644 --- a/opencore_legacy_patcher/efi_builder/graphics_audio.py +++ b/opencore_legacy_patcher/efi_builder/graphics_audio.py @@ -246,10 +246,18 @@ def _amd_mxm_patch(self, backlight_path) -> None: self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1, "ATY,bin_image": binascii.unhexlify(video_bios_data.RX5500XT_64K) } logging.info(f"- Adding AMD RX5500XT boot-args") self.config["NVRAM"]["Add"]["7C436110-AB2A-4BBB-A880-FE41995C9F82"]["boot-args"] += " agdpmod=pikera applbkl=3" + elif self.computer.dgpu.device_id == 0x73FF: + logging.info(f"- Adding AMD RX6600M vBIOS injection") + self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1, "ATY,bin_image": binascii.unhexlify(video_bios_data.RX6600M_64K) } + logging.info(f"- Adding AMD RX6600M boot-args") + self.config["NVRAM"]["Add"]["7C436110-AB2A-4BBB-A880-FE41995C9F82"]["boot-args"] += " agdpmod=pikera applbkl=3" elif self.computer.dgpu.device_id_unspoofed == 0x6981: logging.info(f"- Adding AMD WX3200 device spoofing") self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1, "model": "AMD Radeon Pro WX 3200", "device-id": binascii.unhexlify("FF67")} - else: + elif self.computer.dgpu.device_id_unspoofed == 0x67FF: + logging.info(f"- Adding AMD WX3200 device spoofing") + self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1, "model": "AMD Radeon Pro WX 3200", "device-id": binascii.unhexlify("FF67")} + else: self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1} else: self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1} @@ -313,6 +321,18 @@ def _amd_mxm_patch(self, backlight_path) -> None: } logging.info(f"- Adding AMD RX5500XT boot-args") self.config["NVRAM"]["Add"]["7C436110-AB2A-4BBB-A880-FE41995C9F82"]["boot-args"] += " agdpmod=pikera applbkl=3" + elif self.constants.imac_model == "Navi23": + logging.info("- Adding Navi Spoofing Patches") + navi_backlight_path = backlight_path+"/Pci(0x0,0x0)/Pci(0x0,0x0)" + self.config["DeviceProperties"]["Add"][navi_backlight_path] = { + "ATY,bin_image": binascii.unhexlify(video_bios_data.RX6600M_64K), + "shikigva": 128, + "unfairgva": 1, + "rebuild-device-tree": 1, + "enable-gva-support": 1 + } + logging.info(f"- Adding AMD RX6600M boot-args") + self.config["NVRAM"]["Add"]["7C436110-AB2A-4BBB-A880-FE41995C9F82"]["boot-args"] += " agdpmod=pikera applbkl=3" def _audio_handling(self) -> None: diff --git a/opencore_legacy_patcher/sys_patch/patchsets/detect.py b/opencore_legacy_patcher/sys_patch/patchsets/detect.py index 011fdab201..b45059e66f 100644 --- a/opencore_legacy_patcher/sys_patch/patchsets/detect.py +++ b/opencore_legacy_patcher/sys_patch/patchsets/detect.py @@ -31,6 +31,7 @@ amd_legacy_gcn, amd_polaris, amd_vega, + amd_navi, ) from .hardware.networking import ( legacy_wireless, @@ -122,6 +123,7 @@ def __init__(self, constants: constants.Constants, amd_legacy_gcn.AMDLegacyGCN, amd_polaris.AMDPolaris, amd_vega.AMDVega, + amd_navi.AMDNavi, legacy_wireless.LegacyWireless, modern_wireless.ModernWireless, diff --git a/opencore_legacy_patcher/sys_patch/patchsets/hardware/base.py b/opencore_legacy_patcher/sys_patch/patchsets/hardware/base.py index bbb9ec58e5..53517db63a 100644 --- a/opencore_legacy_patcher/sys_patch/patchsets/hardware/base.py +++ b/opencore_legacy_patcher/sys_patch/patchsets/hardware/base.py @@ -161,6 +161,7 @@ def _resolve_monterey_framebuffers(self) -> str: - AppleIntelSKLGraphicsFramebuffer.kext - AMDRadeonX4000.kext - AMDRadeonX5000.kext + - AMDRadeonX6000.kext """ if self._xnu_major < os_data.sonoma.value: return "12.5" diff --git a/opencore_legacy_patcher/wx_gui/gui_settings.py b/opencore_legacy_patcher/wx_gui/gui_settings.py index ab0d470f6f..9fbce3bff2 100644 --- a/opencore_legacy_patcher/wx_gui/gui_settings.py +++ b/opencore_legacy_patcher/wx_gui/gui_settings.py @@ -575,6 +575,7 @@ def _settings(self) -> dict: "AMD Polaris", "AMD Lexa", "AMD Navi", + "AMD Navi23", ], "value": "None", "variable": "", @@ -1296,6 +1297,8 @@ def gpu_selection_click(self, event: wx.Event) -> None: self.constants.imac_model = "Lexa" elif "Navi" in gpu_choice: self.constants.imac_model = "Navi" + elif "Navi23" in gpu_choice: + self.constants.imac_model = "Navi23" else: raise Exception("Unknown GPU Model") global_settings.GlobalEnviromentSettings().write_property("GUI:imac_vendor", "AMD") @@ -1394,4 +1397,4 @@ def on_bless_root_vol(self, event: wx.Event) -> None: if sys_patch.PatchSysVolume("",self.constants)._rebuild_root_volume() == True: wx.MessageDialog(self.parent, "Root Volume saved, please reboot to apply changes", "Success", wx.OK | wx.ICON_INFORMATION).ShowModal() else: - wx.MessageDialog(self.parent, "Root Volume update Failed, check terminal output", "Error", wx.OK | wx.ICON_ERROR).ShowModal() \ No newline at end of file + wx.MessageDialog(self.parent, "Root Volume update Failed, check terminal output", "Error", wx.OK | wx.ICON_ERROR).ShowModal() From 2867f8972c0da6836d126668fec843dc67f7a9e0 Mon Sep 17 00:00:00 2001 From: Ausdauersportler Date: Sun, 17 Nov 2024 16:36:43 +0100 Subject: [PATCH 2/6] revert GVA for NAVI, too --- opencore_legacy_patcher/efi_builder/graphics_audio.py | 5 +++-- .../sys_patch/patchsets/hardware/graphics/amd_navi.py | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/opencore_legacy_patcher/efi_builder/graphics_audio.py b/opencore_legacy_patcher/efi_builder/graphics_audio.py index 6c2234bdba..14722fa8e1 100644 --- a/opencore_legacy_patcher/efi_builder/graphics_audio.py +++ b/opencore_legacy_patcher/efi_builder/graphics_audio.py @@ -254,10 +254,11 @@ def _amd_mxm_patch(self, backlight_path) -> None: elif self.computer.dgpu.device_id_unspoofed == 0x6981: logging.info(f"- Adding AMD WX3200 device spoofing") self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1, "model": "AMD Radeon Pro WX 3200", "device-id": binascii.unhexlify("FF67")} - elif self.computer.dgpu.device_id_unspoofed == 0x67FF: + elif self.computer.dgpu.device_id_unspoofed == 0x67FF: + # we cannot detect a spoofed Lexa card and have to assume the 0x67FF is such a card and add the spoofing logging.info(f"- Adding AMD WX3200 device spoofing") self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1, "model": "AMD Radeon Pro WX 3200", "device-id": binascii.unhexlify("FF67")} - else: + else: self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1} else: self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1} diff --git a/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py b/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py index 3e9b436420..9bd763458f 100644 --- a/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py +++ b/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py @@ -6,6 +6,7 @@ from ...base import PatchType +from ...shared_patches.monterey_gva import MontereyGVA from ...shared_patches.monterey_opencl import MontereyOpenCL from ...shared_patches.amd_opencl import AMDOpenCL @@ -120,6 +121,9 @@ def patches(self) -> dict: return {} return { + # AMD GCN and newer GPUs can still use the native GVA stack + **MontereyGVA(self._xnu_major, self._xnu_minor, self._constants.detected_os_version).revert_patches(), + **MontereyOpenCL(self._xnu_major, self._xnu_minor, self._constants.detected_os_version).patches(), **AMDOpenCL(self._xnu_major, self._xnu_minor, self._constants.detected_os_version).patches(), **self._model_specific_patches(), From 2eb6c26affb47fc9b63f85fbcaf2901247130e78 Mon Sep 17 00:00:00 2001 From: Ausdauersportler Date: Sun, 17 Nov 2024 21:20:01 +0100 Subject: [PATCH 3/6] Patch logic fix by Mykola --- .../patchsets/hardware/graphics/amd_navi.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py b/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py index 9bd763458f..dcc3ddd1c7 100644 --- a/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py +++ b/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py @@ -74,16 +74,18 @@ def _model_specific_patches(self) -> dict: """ return { "AMD Navi": { - PatchType.OVERWRITE_SYSTEM_VOLUME: { - "AMDRadeonX6000.kext": self._resolve_monterey_framebuffers(), - "AMDRadeonX6000Framebuffer.kext": "12.5", + PatchType.OVERWRITE_SYSTEM_VOLUME: { + "/System/Library/Extensions": { + "AMDRadeonX6000.kext": self._resolve_monterey_framebuffers(), + "AMDRadeonX6000Framebuffer.kext": "12.5", - "AMDRadeonVADriver2.bundle": "12.5", - "AMDRadeonX6000GLDriver.bundle": "12.5", - "AMDRadeonX6000MTLDriver.bundle": "12.5" if self._xnu_major < os_data.sequoia else "12.5-24", - "AMDRadeonX6000Shared.bundle": "12.5", + "AMDRadeonVADriver2.bundle": "12.5", + "AMDRadeonX6000GLDriver.bundle": "12.5", + "AMDRadeonX6000MTLDriver.bundle": "12.5" if self._xnu_major < os_data.sequoia else "12.5-24", + "AMDRadeonX6000Shared.bundle": "12.5", - "AMDShared.bundle": "12.5", + "AMDShared.bundle": "12.5", + }, } } } From f31dced42895ddde0f93546bdb01fa1c8a1cf186 Mon Sep 17 00:00:00 2001 From: Ausdauersportler Date: Tue, 19 Nov 2024 10:59:25 +0100 Subject: [PATCH 4/6] added AMDRadeonX6000HWServices.kext to standard patch set --- .../sys_patch/patchsets/hardware/graphics/amd_navi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py b/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py index dcc3ddd1c7..6d5d2dcd94 100644 --- a/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py +++ b/opencore_legacy_patcher/sys_patch/patchsets/hardware/graphics/amd_navi.py @@ -83,6 +83,7 @@ def _model_specific_patches(self) -> dict: "AMDRadeonX6000GLDriver.bundle": "12.5", "AMDRadeonX6000MTLDriver.bundle": "12.5" if self._xnu_major < os_data.sequoia else "12.5-24", "AMDRadeonX6000Shared.bundle": "12.5", + "AMDRadeonX6000HWServices.kext": "12.5", "AMDShared.bundle": "12.5", }, From 0e03e9d155ecfb2895b3a5166847eb72de268267 Mon Sep 17 00:00:00 2001 From: Ausdauersportler Date: Mon, 25 Nov 2024 23:40:03 +0100 Subject: [PATCH 5/6] iMac internal display enabler --- opencore_legacy_patcher/efi_builder/graphics_audio.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/opencore_legacy_patcher/efi_builder/graphics_audio.py b/opencore_legacy_patcher/efi_builder/graphics_audio.py index 14722fa8e1..6cfdc876fd 100644 --- a/opencore_legacy_patcher/efi_builder/graphics_audio.py +++ b/opencore_legacy_patcher/efi_builder/graphics_audio.py @@ -243,12 +243,12 @@ def _amd_mxm_patch(self, backlight_path) -> None: if not self.constants.custom_model: if self.computer.dgpu.device_id == 0x7340: logging.info(f"- Adding AMD RX5500XT vBIOS injection") - self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1, "ATY,bin_image": binascii.unhexlify(video_bios_data.RX5500XT_64K) } + self.config["DeviceProperties"]["Add"][backlight_path] = {"CFG_LINK_FIXED_MAP": 2, "shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1, "ATY,bin_image": binascii.unhexlify(video_bios_data.RX5500XT_64K) } logging.info(f"- Adding AMD RX5500XT boot-args") self.config["NVRAM"]["Add"]["7C436110-AB2A-4BBB-A880-FE41995C9F82"]["boot-args"] += " agdpmod=pikera applbkl=3" elif self.computer.dgpu.device_id == 0x73FF: logging.info(f"- Adding AMD RX6600M vBIOS injection") - self.config["DeviceProperties"]["Add"][backlight_path] = {"shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1, "ATY,bin_image": binascii.unhexlify(video_bios_data.RX6600M_64K) } + self.config["DeviceProperties"]["Add"][backlight_path] = {"model": "AMD Radeon RX6600M", "CFG_LINK_FIXED_MAP": 2, "shikigva": 128, "unfairgva": 1, "agdpmod": "pikera", "rebuild-device-tree": 1, "enable-gva-support": 1, "ATY,bin_image": binascii.unhexlify(video_bios_data.RX6600M_64K) } logging.info(f"- Adding AMD RX6600M boot-args") self.config["NVRAM"]["Add"]["7C436110-AB2A-4BBB-A880-FE41995C9F82"]["boot-args"] += " agdpmod=pikera applbkl=3" elif self.computer.dgpu.device_id_unspoofed == 0x6981: @@ -315,6 +315,7 @@ def _amd_mxm_patch(self, backlight_path) -> None: navi_backlight_path = backlight_path+"/Pci(0x0,0x0)/Pci(0x0,0x0)" self.config["DeviceProperties"]["Add"][navi_backlight_path] = { "ATY,bin_image": binascii.unhexlify(video_bios_data.RX5500XT_64K), + "CFG_LINK_FIXED_MAP": 2, "shikigva": 128, "unfairgva": 1, "rebuild-device-tree": 1, @@ -327,6 +328,7 @@ def _amd_mxm_patch(self, backlight_path) -> None: navi_backlight_path = backlight_path+"/Pci(0x0,0x0)/Pci(0x0,0x0)" self.config["DeviceProperties"]["Add"][navi_backlight_path] = { "ATY,bin_image": binascii.unhexlify(video_bios_data.RX6600M_64K), + "CFG_LINK_FIXED_MAP": 2, "shikigva": 128, "unfairgva": 1, "rebuild-device-tree": 1, From e5f9d87deaca5221a8b49730ac399d682b6281e6 Mon Sep 17 00:00:00 2001 From: Ausdauersportler Date: Wed, 27 Nov 2024 13:21:32 +0100 Subject: [PATCH 6/6] fixing framebuffer selection for backlight patch to Monterey due to patching on later macOS versions --- .../WhateverGreen-Navi-Backlight.patch | 6667 +++++++++-------- 1 file changed, 3348 insertions(+), 3319 deletions(-) diff --git a/payloads/Kexts/Acidanthera/WhateverGreen-Navi-Backlight.patch b/payloads/Kexts/Acidanthera/WhateverGreen-Navi-Backlight.patch index 49c2057855..b75c2bd522 100644 --- a/payloads/Kexts/Acidanthera/WhateverGreen-Navi-Backlight.patch +++ b/payloads/Kexts/Acidanthera/WhateverGreen-Navi-Backlight.patch @@ -1,3319 +1,3348 @@ -diff --git a/WhateverGreen/kern_rad.cpp b/WhateverGreen/kern_rad.cpp -index b4f271f..162dc82 100644 ---- a/WhateverGreen/kern_rad.cpp -+++ b/WhateverGreen/kern_rad.cpp -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - - #include -@@ -190,1015 +191,2399 @@ void RAD::processKernel(KernelPatcher &patcher, DeviceInfo *info) { - } - - void RAD::updatePwmMaxBrightnessFromInternalDisplay() { -- OSDictionary * matching = IOService::serviceMatching("AppleBacklightDisplay"); -- if (matching == nullptr) { -- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null AppleBacklightDisplay"); -- return; -- } -- -- OSIterator *iter = IOService::getMatchingServices(matching); -- if (iter == nullptr) { -- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null matching"); -- matching->release(); -- return; -- } -- -- IORegistryEntry* display = OSDynamicCast(IORegistryEntry, iter->getNextObject()); -- if (display == nullptr) { -- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null display"); -- iter->release(); -- matching->release(); -- return; -- } -- -- OSDictionary* iodispparm = OSDynamicCast(OSDictionary, display->getProperty("IODisplayParameters")); -- if (iodispparm == nullptr) { -- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null IODisplayParameters"); -- iter->release(); -- matching->release(); -- return; -- } -- -- OSDictionary* linearbri = OSDynamicCast(OSDictionary, iodispparm->getObject("linear-brightness")); -- if (linearbri == nullptr) { -- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null linear-brightness"); -- iter->release(); -- matching->release(); -- return; -- } -- -- OSNumber* maxbri = OSDynamicCast(OSNumber, linearbri->getObject("max")); -- if (maxbri == nullptr) { -- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null max"); -- iter->release(); -- matching->release(); -- return; -- } -+ OSDictionary * matching = IOService::serviceMatching("AppleBacklightDisplay"); -+ if (matching == nullptr) { -+ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null AppleBacklightDisplay"); -+ return; -+ } -+ -+ OSIterator *iter = IOService::getMatchingServices(matching); -+ if (iter == nullptr) { -+ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null matching"); -+ matching->release(); -+ return; -+ } -+ -+ IORegistryEntry* display = OSDynamicCast(IORegistryEntry, iter->getNextObject()); -+ if (display == nullptr) { -+ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null display"); -+ iter->release(); -+ matching->release(); -+ return; -+ } -+ -+ OSDictionary* iodispparm = OSDynamicCast(OSDictionary, display->getProperty("IODisplayParameters")); -+ if (iodispparm == nullptr) { -+ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null IODisplayParameters"); -+ iter->release(); -+ matching->release(); -+ return; -+ } -+ -+ OSDictionary* linearbri = OSDynamicCast(OSDictionary, iodispparm->getObject("linear-brightness")); -+ if (linearbri == nullptr) { -+ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null linear-brightness"); -+ iter->release(); -+ matching->release(); -+ return; -+ } -+ -+ OSNumber* maxbri = OSDynamicCast(OSNumber, linearbri->getObject("max")); -+ if (maxbri == nullptr) { -+ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null max"); -+ iter->release(); -+ matching->release(); -+ return; -+ } -+ -+ callbackRAD->maxPwmBacklightLvl = maxbri->unsigned32BitValue(); -+ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay get max brightness: 0x%x", callbackRAD->maxPwmBacklightLvl); -+ -+ iter->release(); -+ matching->release(); -+} - -- callbackRAD->maxPwmBacklightLvl = maxbri->unsigned32BitValue(); -- DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay get max brightness: 0x%x", callbackRAD->maxPwmBacklightLvl); -+typedef int64_t __int64; -+typedef int64_t _QWORD; -+typedef int32_t _DWORD; -+typedef int8_t __int8; - -- iter->release(); -- matching->release(); --} -+struct dc_stream_state { -+ void *sink; -+ void *link; -+}; - --uint32_t RAD::wrapDcePanelCntlHwInit(void *panel_cntl) { -- callbackRAD->panelCntlPtr = panel_cntl; -- callbackRAD->updatePwmMaxBrightnessFromInternalDisplay(); // read max brightness value from IOReg -- uint32_t ret = FunctionCast(wrapDcePanelCntlHwInit, callbackRAD->orgDcePanelCntlHwInit)(panel_cntl); -- return ret; --} -+#define MAX_SINKS_PER_LINK 4 - --IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t value) { -- IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute)(framebuffer, connectIndex, attribute, value); -- if (attribute != (UInt32)'bklt') { -- return ret; -- } -- -- if (callbackRAD->maxPwmBacklightLvl == 0) { -- DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute zero maxPwmBacklightLvl"); -- return 0; -- } -- -- if (callbackRAD->panelCntlPtr == nullptr) { -- DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null panel cntl"); -- return 0; -- } -- -- if (callbackRAD->orgDceDriverSetBacklight == nullptr) { -- DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null orgDcLinkSetBacklightLevel"); -- return 0; -- } -- -- // set the backlight of AMD navi10 driver -- callbackRAD->curPwmBacklightLvl = (uint32_t)value; -- uint32_t btlper = callbackRAD->curPwmBacklightLvl * 100 / callbackRAD->maxPwmBacklightLvl; -- uint32_t pwmval = 0; -- if (btlper >= 100) { -- // This is from the dmcu_set_backlight_level function of Linux source -- // ... -- // if (backlight_pwm_u16_16 & 0x10000) -- // backlight_8_bit = 0xFF; -- // else -- // backlight_8_bit = (backlight_pwm_u16_16 >> 8) & 0xFF; -- // ... -- // The max brightness should have 0x10000 bit set -- pwmval = 0x1FF00; -- } else { -- pwmval = ((btlper * 0xFF) / 100) << 8U; -- } -+struct dc_link_settings { -+ uint32_t lane_count; -+ uint32_t link_rate; -+ uint32_t link_spread; -+ bool use_link_rate_set; -+ uint8_t link_rate_set; -+}; - -- callbackRAD->orgDceDriverSetBacklight(callbackRAD->panelCntlPtr, pwmval); -- return 0; --} -+struct dc_lane_settings { -+ uint32_t VOLTAGE_SWING; -+ uint32_t PRE_EMPHASIS; -+ uint32_t POST_CURSOR2; -+}; - --IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t * value) { -- IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute)(framebuffer, connectIndex, attribute, value); -- if (attribute == (UInt32)'bklt') { -- // enable the backlight feature of AMD navi10 driver -- *value = callbackRAD->curPwmBacklightLvl; -- ret = 0; -- } -- return ret; --} -+struct dc_link_training_overrides { -+ uint32_t *voltage_swing; -+ uint32_t *pre_emphasis; -+ uint32_t *post_cursor2; -+ -+ uint16_t *cr_pattern_time; -+ uint16_t *eq_pattern_time; -+ uint32_t *pattern_for_cr; -+ uint32_t *pattern_for_eq; -+ -+ uint32_t *downspread; -+ bool *alternate_scrambler_reset; -+ bool *enhanced_framing; -+ bool *mst_enable; -+ bool *fec_enable; -+}; - --bool RAD::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { -- if (kextRadeonX6000Framebuffer.loadIndex == index) { -- KernelPatcher::RouteRequest requests[] = { -- {"_dce_panel_cntl_hw_init", wrapDcePanelCntlHwInit, orgDcePanelCntlHwInit}, -- {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25setAttributeForConnectionEijm", wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute}, -- {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25getAttributeForConnectionEijPm", wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute}, -- }; -+struct dp_audio_test_data_flags { -+ uint8_t test_requested :1; -+ uint8_t disable_video :1; -+}; - -- if (!patcher.routeMultiple(index, requests, address, size, true, true)) -- SYSLOG("igfx", "Failed to route redeon x6000 gpu tracing."); -- -- orgDceDriverSetBacklight = reinterpret_cast(patcher.solveSymbol(index, "_dce_driver_set_backlight")); -- if (patcher.getError() != KernelPatcher::Error::NoError) { -- SYSLOG("igfx", "failed to resolve _dce_driver_set_backlight"); -- patcher.clearError(); -- } -- } -- -- if (kextRadeonFramebuffer.loadIndex == index) { -- if (force24BppMode) -- process24BitOutput(patcher, kextRadeonFramebuffer, address, size); -- return true; -- } -+struct dp_audio_test_data { - -- if (kextRadeonLegacyFramebuffer.loadIndex == index) { -- if (force24BppMode) -- process24BitOutput(patcher, kextRadeonLegacyFramebuffer, address, size); -- return true; -- } -+ struct dp_audio_test_data_flags flags; -+ uint8_t sampling_rate; -+ uint8_t channel_count; -+ uint8_t pattern_type; -+ uint8_t pattern_period[8]; -+}; - -- if (kextRadeonSupport.loadIndex == index) { -- processConnectorOverrides(patcher, address, size, true); -+union compliance_test_state { -+ struct { -+ unsigned char STEREO_3D_RUNNING : 1; -+ unsigned char RESERVED : 7; -+ } bits; -+ unsigned char raw; -+}; - -- if (getKernelVersion() > KernelVersion::Mojave || -- (getKernelVersion() == KernelVersion::Mojave && getKernelMinorVersion() >= 5)) { -- KernelPatcher::RouteRequest request("__ZN13ATIController8TestVRAME13PCI_REG_INDEXb", doNotTestVram); -- patcher.routeMultiple(index, &request, 1, address, size); -- } -+struct graphics_object_id { -+ uint32_t id:8; -+ uint32_t enum_id:4; -+ uint32_t type:4; -+ uint32_t reserved:16; /* for padding. total size should be u32 */ -+}; - -- if (useCustomAgdpDecision) { -- KernelPatcher::RouteRequest request("__ZN16AtiDeviceControl16notifyLinkChangeE31kAGDCRegisterLinkControlEvent_tmj", wrapNotifyLinkChange, orgNotifyLinkChange); -- patcher.routeMultiple(index, &request, 1, address, size); -- } -+union ddi_channel_mapping { -+ struct mapping { -+ uint8_t lane0:2; /* Mapping for lane 0 */ -+ uint8_t lane1:2; /* Mapping for lane 1 */ -+ uint8_t lane2:2; /* Mapping for lane 2 */ -+ uint8_t lane3:2; /* Mapping for lane 3 */ -+ } mapping; -+ uint8_t raw; -+}; - -- return true; -- } -+struct device_id { -+ uint32_t device_type:16; -+ uint32_t enum_id:16; /* 1 based enum */ -+ uint16_t raw_device_tag; -+}; - -- if (kextRadeonLegacySupport.loadIndex == index) { -- processConnectorOverrides(patcher, address, size, false); -- return true; -- } -+struct connector_device_tag_info { -+ uint32_t acpi_device; -+ struct device_id dev_id; -+}; - -- if (kextPolarisController.loadIndex == index) { -- KernelPatcher::RouteRequest request("__ZN17AMD9500Controller23findProjectByPartNumberEP20ControllerProperties", findProjectByPartNumber); -- patcher.routeMultiple(index, &request, 1, address, size); -- } -+union dpcd_rev { -+ struct { -+ uint8_t MINOR:4; -+ uint8_t MAJOR:4; -+ } bits; -+ uint8_t raw; -+}; - -- for (size_t i = 0; i < maxHardwareKexts; i++) { -- if (kextRadeonHardware[i].loadIndex == index) { -- processHardwareKext(patcher, i, address, size); -- return true; -- } -- } -+union max_lane_count { -+ struct { -+ uint8_t MAX_LANE_COUNT:5; -+ uint8_t POST_LT_ADJ_REQ_SUPPORTED:1; -+ uint8_t TPS3_SUPPORTED:1; -+ uint8_t ENHANCED_FRAME_CAP:1; -+ } bits; -+ uint8_t raw; -+}; - -- return false; --} -+union max_down_spread { -+ struct { -+ uint8_t MAX_DOWN_SPREAD:1; -+ uint8_t RESERVED:5; -+ uint8_t NO_AUX_HANDSHAKE_LINK_TRAINING:1; -+ uint8_t TPS4_SUPPORTED:1; -+ } bits; -+ uint8_t raw; -+}; - --void RAD::initHardwareKextMods() { -- // Decide on kext amount present for optimal performance. -- // 10.15+ has X4000, X5000, and X6000 -- // 10.14+ has X4000 and X5000 -- // 10.13.4+ has X3000, X4000, and X5000 -- if (getKernelVersion() >= KernelVersion::Catalina) -- maxHardwareKexts = MaxRadeonHardwareCatalina; -- else if (getKernelVersion() >= KernelVersion::Mojave) -- maxHardwareKexts = MaxRadeonHardwareMojave; -- else if (getKernelVersion() == KernelVersion::HighSierra && getKernelMinorVersion() >= 5) -- maxHardwareKexts = MaxRadeonHardwareModernHighSierra; -- -- // 10.13.4 fixed black screen issues -- if (maxHardwareKexts != MaxRadeonHardware) { -- for (size_t i = 0; i < MaxGetFrameBufferProcs; i++) -- getFrameBufferProcNames[IndexRadeonHardwareX4000][i] = nullptr; -- -- // We have nothing to do for these kexts on recent systems -- if (!fixConfigName && !forceOpenGL && !forceCodecInfo) { -- // X4000 kext is not included in this list as we need to fix GVA properties for most of its GPUs -- kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); -- kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); -- } -- } -+union dprx_feature { -+ struct { -+ uint8_t GTC_CAP:1; // bit 0: DP 1.3+ -+ uint8_t SST_SPLIT_SDP_CAP:1; // bit 1: DP 1.4 -+ uint8_t AV_SYNC_CAP:1; // bit 2: DP 1.3+ -+ uint8_t VSC_SDP_COLORIMETRY_SUPPORTED:1; // bit 3: DP 1.3+ -+ uint8_t VSC_EXT_VESA_SDP_SUPPORTED:1; // bit 4: DP 1.4 -+ uint8_t VSC_EXT_VESA_SDP_CHAINING_SUPPORTED:1; // bit 5: DP 1.4 -+ uint8_t VSC_EXT_CEA_SDP_SUPPORTED:1; // bit 6: DP 1.4 -+ uint8_t VSC_EXT_CEA_SDP_CHAINING_SUPPORTED:1; // bit 7: DP 1.4 -+ } bits; -+ uint8_t raw; -+}; - -- if (getKernelVersion() < KernelVersion::Catalina) { -- kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); -- } -+union sink_count { -+ struct { -+ uint8_t SINK_COUNT:6; -+ uint8_t CPREADY:1; -+ uint8_t RESERVED:1; -+ } bits; -+ uint8_t raw; -+}; - -- if (getKernelVersion() < KernelVersion::HighSierra) { -- // Versions before 10.13 do not support X4250 and X5000 -- kextRadeonHardware[IndexRadeonHardwareX4250].switchOff(); -- kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); -+struct dc_dongle_caps { -+ /* dongle type (DP converter, CV smart dongle) */ -+ uint32_t dongle_type; -+ bool extendedCapValid; -+ /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, -+ indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ -+ bool is_dp_hdmi_s3d_converter; -+ bool is_dp_hdmi_ycbcr422_pass_through; -+ bool is_dp_hdmi_ycbcr420_pass_through; -+ bool is_dp_hdmi_ycbcr422_converter; -+ bool is_dp_hdmi_ycbcr420_converter; -+ uint32_t dp_hdmi_max_bpc; -+ uint32_t dp_hdmi_max_pixel_clk_in_khz; -+}; - -- // Versions before 10.13 have legacy X3000 and X4000 IDs -- kextRadeonHardware[IndexRadeonHardwareX3000].id = idRadeonX3000Old; -- kextRadeonHardware[IndexRadeonHardwareX4000].id = idRadeonX4000Old; -+union dpcd_fec_capability { -+ struct { -+ uint8_t FEC_CAPABLE:1; -+ uint8_t UNCORRECTED_BLOCK_ERROR_COUNT_CAPABLE:1; -+ uint8_t CORRECTED_BLOCK_ERROR_COUNT_CAPABLE:1; -+ uint8_t BIT_ERROR_COUNT_CAPABLE:1; -+ uint8_t RESERVED:4; -+ } bits; -+ uint8_t raw; -+}; - -- bool preSierra = getKernelVersion() < KernelVersion::Sierra; -+union dpcd_dsc_branch_decoder_capabilities { -+ struct { -+ uint8_t BRANCH_OVERALL_THROUGHPUT_0; -+ uint8_t BRANCH_OVERALL_THROUGHPUT_1; -+ uint8_t BRANCH_MAX_LINE_WIDTH; -+ } fields; -+ uint8_t raw[3]; -+}; - -- if (preSierra) { -- // Versions before 10.12 do not support X4100 -- kextRadeonHardware[IndexRadeonHardwareX4100].switchOff(); -- } -+struct dpcd_dsc_support { -+ uint8_t DSC_SUPPORT :1; -+ uint8_t DSC_PASSTHROUGH_SUPPORT :1; -+ uint8_t RESERVED :6; -+}; - -- if (preSierra || (getKernelVersion() == KernelVersion::Sierra && getKernelMinorVersion() < 7)) { -- // Versions before 10.12.6 do not support X4150, X4200 -- kextRadeonHardware[IndexRadeonHardwareX4150].switchOff(); -- kextRadeonHardware[IndexRadeonHardwareX4200].switchOff(); -- } -- } -+struct dpcd_dsc_algorithm_revision { -+ uint8_t DSC_VERSION_MAJOR :4; -+ uint8_t DSC_VERSION_MINOR :4; -+}; - -- lilu.onKextLoadForce(kextRadeonHardware, maxHardwareKexts); --} -+struct dpcd_dsc_rc_buffer_block_size { -+ uint8_t RC_BLOCK_BUFFER_SIZE :2; -+ uint8_t RESERVED :6; -+}; - --void RAD::process24BitOutput(KernelPatcher &patcher, KernelPatcher::KextInfo &info, mach_vm_address_t address, size_t size) { -- auto bitsPerComponent = patcher.solveSymbol(info.loadIndex, "__ZL18BITS_PER_COMPONENT", address, size); -- if (bitsPerComponent) { -- while (bitsPerComponent && *bitsPerComponent) { -- if (*bitsPerComponent == 10) { -- auto ret = MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock); -- if (ret == KERN_SUCCESS) { -- DBGLOG("rad", "fixing BITS_PER_COMPONENT"); -- *bitsPerComponent = 8; -- MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock); -- } else { -- SYSLOG("rad", "failed to disable write protection for BITS_PER_COMPONENT"); -- } -- } -- bitsPerComponent++; -- } -- } else { -- SYSLOG("rad", "failed to find BITS_PER_COMPONENT"); -- patcher.clearError(); -- } -+struct dpcd_dsc_slice_capability1 { -+ uint8_t ONE_SLICE_PER_DP_DSC_SINK_DEVICE :1; -+ uint8_t TWO_SLICES_PER_DP_DSC_SINK_DEVICE :1; -+ uint8_t RESERVED :1; -+ uint8_t FOUR_SLICES_PER_DP_DSC_SINK_DEVICE :1; -+ uint8_t SIX_SLICES_PER_DP_DSC_SINK_DEVICE :1; -+ uint8_t EIGHT_SLICES_PER_DP_DSC_SINK_DEVICE :1; -+ uint8_t TEN_SLICES_PER_DP_DSC_SINK_DEVICE :1; -+ uint8_t TWELVE_SLICES_PER_DP_DSC_SINK_DEVICE :1; -+}; - -- DBGLOG("rad", "fixing pixel types"); -+struct dpcd_dsc_line_buffer_bit_depth { -+ uint8_t LINE_BUFFER_BIT_DEPTH :4; -+ uint8_t RESERVED :4; -+}; - -- KernelPatcher::LookupPatch pixelPatch { -- &info, -- reinterpret_cast("--RRRRRRRRRRGGGGGGGGGGBBBBBBBBBB"), -- reinterpret_cast("--------RRRRRRRRGGGGGGGGBBBBBBBB"), -- 32, 2 -- }; -+struct dpcd_dsc_block_prediction_support { -+ uint8_t BLOCK_PREDICTION_SUPPORT:1; -+ uint8_t RESERVED :7; -+}; - -- patcher.applyLookupPatch(&pixelPatch); -- if (patcher.getError() != KernelPatcher::Error::NoError) { -- SYSLOG("rad", "failed to patch RGB mask for 24-bit output"); -- patcher.clearError(); -- } --} -+struct dpcd_maximum_bits_per_pixel_supported_by_the_decompressor { -+ uint8_t MAXIMUM_BITS_PER_PIXEL_SUPPORTED_BY_THE_DECOMPRESSOR_LOW :7; -+ uint8_t MAXIMUM_BITS_PER_PIXEL_SUPPORTED_BY_THE_DECOMPRESSOR_HIGH :7; -+ uint8_t RESERVED :2; -+}; - --void RAD::processConnectorOverrides(KernelPatcher &patcher, mach_vm_address_t address, size_t size, bool modern) { -- if (modern) { -- if (getKernelVersion() >= KernelVersion::HighSierra) { -- KernelPatcher::RouteRequest requests[] { -- KernelPatcher::RouteRequest("__ZN14AtiBiosParser116getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), -- KernelPatcher::RouteRequest("__ZN14AtiBiosParser216getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV2, orgGetConnectorsInfoV2), -- KernelPatcher::RouteRequest("__ZN14AtiBiosParser126translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V117AtomConnectorInfoER13ConnectorInfo", -- wrapTranslateAtomConnectorInfoV1, orgTranslateAtomConnectorInfoV1), -- KernelPatcher::RouteRequest("__ZN14AtiBiosParser226translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V217AtomConnectorInfoER13ConnectorInfo", -- wrapTranslateAtomConnectorInfoV2, orgTranslateAtomConnectorInfoV2), -- KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart) -- }; -- patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); -- } else { -- KernelPatcher::RouteRequest requests[] { -- KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), -- KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart), -- }; -- patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); -- -- orgGetAtomObjectTableForType = reinterpret_cast(patcher.solveSymbol(kextRadeonSupport.loadIndex, -- "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size)); -- if (!orgGetAtomObjectTableForType) { -- SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); -- patcher.clearError(); -- } -- } -- } else { -- KernelPatcher::RouteRequest requests[] { -- KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapLegacyGetConnectorsInfo, orgLegacyGetConnectorsInfo), -- KernelPatcher::RouteRequest("__ZN19AMDLegacyController5startEP9IOService", wrapLegacyATIControllerStart, orgLegacyATIControllerStart), -- }; -- patcher.routeMultiple(kextRadeonLegacySupport.loadIndex, requests, address, size); -+struct dpcd_dsc_decoder_color_format_capabilities { -+ uint8_t RGB_SUPPORT :1; -+ uint8_t Y_CB_CR_444_SUPPORT :1; -+ uint8_t Y_CB_CR_SIMPLE_422_SUPPORT :1; -+ uint8_t Y_CB_CR_NATIVE_422_SUPPORT :1; -+ uint8_t Y_CB_CR_NATIVE_420_SUPPORT :1; -+ uint8_t RESERVED :3; -+}; - -- orgLegacyGetAtomObjectTableForType = patcher.solveSymbol(kextRadeonLegacySupport.loadIndex, -- "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size); -- if (!orgLegacyGetAtomObjectTableForType) { -- SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); -- patcher.clearError(); -- } -- } --} -+struct dpcd_dsc_decoder_color_depth_capabilities { -+ uint8_t RESERVED0 :1; -+ uint8_t EIGHT_BITS_PER_COLOR_SUPPORT :1; -+ uint8_t TEN_BITS_PER_COLOR_SUPPORT :1; -+ uint8_t TWELVE_BITS_PER_COLOR_SUPPORT :1; -+ uint8_t RESERVED1 :4; -+}; - --void RAD::processHardwareKext(KernelPatcher &patcher, size_t hwIndex, mach_vm_address_t address, size_t size) { -- auto getFrame = getFrameBufferProcNames[hwIndex]; -- auto &hardware = kextRadeonHardware[hwIndex]; -- -- // Fix boot and wake to black screen -- for (size_t j = 0; j < MaxGetFrameBufferProcs && getFrame[j] != nullptr; j++) { -- auto getFB = patcher.solveSymbol(hardware.loadIndex, getFrame[j], address, size); -- if (getFB) { -- // Initially it was discovered that the only problematic register is PRIMARY_SURFACE_ADDRESS_HIGH (0x1A07). -- // This register must be nulled to solve most of the issues. -- // Depending on the amount of connected screens PRIMARY_SURFACE_ADDRESS (0x1A04) may not be null. -- // However, as of AMD Vega drivers in 10.13 DP1 both of these registers are now ignored. -- // Furthermore, there are no (extra) issues from just returning 0 in framebuffer base address. -- -- // xor rax, rax -- // ret -- uint8_t ret[] {0x48, 0x31, 0xC0, 0xC3}; -- patcher.routeBlock(getFB, ret, sizeof(ret)); -- if (patcher.getError() == KernelPatcher::Error::NoError) { -- DBGLOG("rad", "patched %s", getFrame[j]); -- } else { -- SYSLOG("rad", "failed to patch %s code %d", getFrame[j], patcher.getError()); -- patcher.clearError(); -- } -- } else { -- SYSLOG("rad", "failed to find %s code %d", getFrame[j], patcher.getError()); -- patcher.clearError(); -- } -- } -+struct dpcd_peak_dsc_throughput_dsc_sink { -+ uint8_t THROUGHPUT_MODE_0:4; -+ uint8_t THROUGHPUT_MODE_1:4; -+}; - -- // Fix reported Accelerator name to support WhateverName.app -- // Also fix GVA properties for X4000. -- if (fixConfigName || hwIndex == IndexRadeonHardwareX4000) { -- KernelPatcher::RouteRequest request(populateAccelConfigProcNames[hwIndex], wrapPopulateAccelConfig[hwIndex], orgPopulateAccelConfig[hwIndex]); -- patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); -- } -+struct dpcd_dsc_slice_capabilities_2 { -+ uint8_t SIXTEEN_SLICES_PER_DSC_SINK_DEVICE :1; -+ uint8_t TWENTY_SLICES_PER_DSC_SINK_DEVICE :1; -+ uint8_t TWENTYFOUR_SLICES_PER_DSC_SINK_DEVICE :1; -+ uint8_t RESERVED :5; -+}; - -- // Enforce OpenGL support if requested -- if (forceOpenGL) { -- DBGLOG("rad", "disabling Metal support"); -- uint8_t find1[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; -- uint8_t find2[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; -- uint8_t repl1[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; -- uint8_t repl2[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; -- -- KernelPatcher::LookupPatch antimetal[] { -- {&hardware, find1, repl1, sizeof(find1), 2}, -- {&hardware, find2, repl2, sizeof(find1), 2} -- }; -+struct dpcd_bits_per_pixel_increment{ -+ uint8_t INCREMENT_OF_BITS_PER_PIXEL_SUPPORTED :3; -+ uint8_t RESERVED :5; -+}; -+union dpcd_dsc_basic_capabilities { -+ struct { -+ struct dpcd_dsc_support dsc_support; -+ struct dpcd_dsc_algorithm_revision dsc_algorithm_revision; -+ struct dpcd_dsc_rc_buffer_block_size dsc_rc_buffer_block_size; -+ uint8_t dsc_rc_buffer_size; -+ struct dpcd_dsc_slice_capability1 dsc_slice_capabilities_1; -+ struct dpcd_dsc_line_buffer_bit_depth dsc_line_buffer_bit_depth; -+ struct dpcd_dsc_block_prediction_support dsc_block_prediction_support; -+ struct dpcd_maximum_bits_per_pixel_supported_by_the_decompressor maximum_bits_per_pixel_supported_by_the_decompressor; -+ struct dpcd_dsc_decoder_color_format_capabilities dsc_decoder_color_format_capabilities; -+ struct dpcd_dsc_decoder_color_depth_capabilities dsc_decoder_color_depth_capabilities; -+ struct dpcd_peak_dsc_throughput_dsc_sink peak_dsc_throughput_dsc_sink; -+ uint8_t dsc_maximum_slice_width; -+ struct dpcd_dsc_slice_capabilities_2 dsc_slice_capabilities_2; -+ uint8_t reserved; -+ struct dpcd_bits_per_pixel_increment bits_per_pixel_increment; -+ } fields; -+ uint8_t raw[16]; -+}; - -- for (auto &p : antimetal) { -- patcher.applyLookupPatch(&p); -- patcher.clearError(); -- } -- } -+struct dpcd_dsc_capabilities { -+ union dpcd_dsc_basic_capabilities dsc_basic_caps; -+ union dpcd_dsc_branch_decoder_capabilities dsc_branch_decoder_caps; -+}; - -- // Patch AppleGVA support for non-supported models -- if (forceCodecInfo && getHWInfoProcNames[hwIndex] != nullptr) { -- KernelPatcher::RouteRequest request(getHWInfoProcNames[hwIndex], wrapGetHWInfo[hwIndex], orgGetHWInfo[hwIndex]); -- patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); -- } --} -+#define MAX_REPEATER_CNT 8 - --void RAD::mergeProperty(OSDictionary *props, const char *name, OSObject *value) { -- // The only type we could make from device properties is data. -- // To be able to override other types we do a conversion here. -- auto data = OSDynamicCast(OSData, value); -- if (data) { -- // It is hard to make a boolean even from ACPI, so we make a hack here: -- // 1-byte OSData with 0x01 / 0x00 values becomes boolean. -- auto val = static_cast(data->getBytesNoCopy()); -- auto len = data->getLength(); -- if (val && len == sizeof(uint8_t)) { -- if (val[0] == 1) { -- props->setObject(name, kOSBooleanTrue); -- DBGLOG("rad", "prop %s was merged as kOSBooleanTrue", name); -- return; -- } else if (val[0] == 0) { -- props->setObject(name, kOSBooleanFalse); -- DBGLOG("rad", "prop %s was merged as kOSBooleanFalse", name); -- return; -- } -- } -+struct dc_lttpr_caps { -+ union dpcd_rev revision; -+ uint8_t mode; -+ uint8_t max_lane_count; -+ uint8_t max_link_rate; -+ uint8_t phy_repeater_cnt; -+ uint8_t max_ext_timeout; -+ uint8_t aux_rd_interval[MAX_REPEATER_CNT - 1]; -+}; - -- // Consult the original value to make a decision -- auto orgValue = props->getObject(name); -- if (val && orgValue) { -- DBGLOG("rad", "prop %s has original value", name); -- if (len == sizeof(uint32_t) && OSDynamicCast(OSNumber, orgValue)) { -- auto num = *reinterpret_cast(val); -- auto osnum = OSNumber::withNumber(num, 32); -- if (osnum) { -- DBGLOG("rad", "prop %s was merged as number %u", name, num); -- props->setObject(name, osnum); -- osnum->release(); -- } -- return; -- } else if (len > 0 && val[len-1] == '\0' && OSDynamicCast(OSString, orgValue)) { -- auto str = reinterpret_cast(val); -- auto osstr = OSString::withCString(str); -- if (osstr) { -- DBGLOG("rad", "prop %s was merged as string %s", name, str); -- props->setObject(name, osstr); -- osstr->release(); -- } -- return; -- } -- } else { -- DBGLOG("rad", "prop %s has no original value", name); -- } -- } -+struct psr_caps { -+ unsigned char psr_version; -+ unsigned int psr_rfb_setup_time; -+ bool psr_exit_link_training_required; -+}; - -- // Default merge as is -- props->setObject(name, value); -- DBGLOG("rad", "prop %s was merged", name); --} -+struct dpcd_caps { -+ union dpcd_rev dpcd_rev; -+ union max_lane_count max_ln_count; -+ union max_down_spread max_down_spread; -+ union dprx_feature dprx_feature; -+ -+ /* valid only for eDP v1.4 or higher*/ -+ uint8_t edp_supported_link_rates_count; -+ uint32_t edp_supported_link_rates[8]; -+ -+ /* dongle type (DP converter, CV smart dongle) */ -+ uint32_t dongle_type; -+ /* branch device or sink device */ -+ bool is_branch_dev; -+ /* Dongle's downstream count. */ -+ union sink_count sink_count; -+ /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, -+ indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ -+ struct dc_dongle_caps dongle_caps; -+ -+ uint32_t sink_dev_id; -+ int8_t sink_dev_id_str[6]; -+ int8_t sink_hw_revision; -+ int8_t sink_fw_revision[2]; -+ -+ uint32_t branch_dev_id; -+ int8_t branch_dev_name[6]; -+ int8_t branch_hw_revision; -+ int8_t branch_fw_revision[2]; -+ -+ bool allow_invalid_MSA_timing_param; -+ bool panel_mode_edp; -+ bool dpcd_display_control_capable; -+ bool ext_receiver_cap_field_present; -+ bool dynamic_backlight_capable_edp; -+ union dpcd_fec_capability fec_cap; -+ struct dpcd_dsc_capabilities dsc_caps; -+ struct dc_lttpr_caps lttpr_caps; -+ struct psr_caps psr_caps; - --void RAD::mergeProperties(OSDictionary *props, const char *prefix, IOService *provider) { -- // Should be ok, but in case there are issues switch to dictionaryWithProperties(); -- auto dict = provider->getPropertyTable(); -- if (dict) { -- auto iterator = OSCollectionIterator::withCollection(dict); -- if (iterator) { -- OSSymbol *propname; -- size_t prefixlen = strlen(prefix); -- while ((propname = OSDynamicCast(OSSymbol, iterator->getNextObject())) != nullptr) { -- auto name = propname->getCStringNoCopy(); -- if (name && propname->getLength() > prefixlen && !strncmp(name, prefix, prefixlen)) { -- auto prop = dict->getObject(propname); -- if (prop) -- mergeProperty(props, name + prefixlen, prop); -- else -- DBGLOG("rad", "prop %s was not merged due to no value", name); -- } else { -- //DBGLOG("rad", "prop %s does not match %s prefix", safeString(name), prefix); -- } -- } -+}; - -- iterator->release(); -- } else { -- SYSLOG("rad", "prop merge failed to iterate over properties"); -- } -- } else { -- SYSLOG("rad", "prop merge failed to get properties"); -- } -+union dpcd_sink_ext_caps { -+ struct { -+ /* 0 - Sink supports backlight adjust via PWM during SDR/HDR mode -+ * 1 - Sink supports backlight adjust via AUX during SDR/HDR mode. -+ */ -+ uint8_t sdr_aux_backlight_control : 1; -+ uint8_t hdr_aux_backlight_control : 1; -+ uint8_t reserved_1 : 2; -+ uint8_t oled : 1; -+ uint8_t reserved : 3; -+ } bits; -+ uint8_t raw; -+}; - -- if (!strcmp(prefix, "CAIL,")) { -- for (size_t i = 0; i < arrsize(powerGatingFlags); i++) { -- if (powerGatingFlags[i] && props->getObject(powerGatingFlags[i])) { -- DBGLOG("rad", "cail prop merge found %s, replacing", powerGatingFlags[i]); -- auto num = OSNumber::withNumber(1, 32); -- if (num) { -- props->setObject(powerGatingFlags[i], num); -- num->release(); -- } -- } -- } -- } --} -+struct dc; -+ -+typedef struct { -+ void *remote_sinks[MAX_SINKS_PER_LINK]; -+ unsigned int sink_count; -+ void *local_sink; -+ unsigned int link_index; -+ uint32_t type; -+ uint32_t connector_signal; -+ uint32_t irq_source_hpd; -+ uint32_t irq_source_hpd_rx;/* aka DP Short Pulse */ -+ bool is_hpd_filter_disabled; -+ bool dp_ss_off; -+ bool link_state_valid; -+ bool aux_access_disabled; -+ bool sync_lt_in_progress; -+ uint32_t lttpr_mode; -+ bool is_internal_display; -+ -+ /* TODO: Rename. Flag an endpoint as having a programmable mapping to a -+ * DIG encoder. */ -+ bool is_dig_mapping_flexible; -+ bool hpd_status; /* HPD status of link without physical HPD pin. */ -+ -+ bool edp_sink_present; -+ -+ /* caps is the same as reported_link_cap. link_traing use -+ * reported_link_cap. Will clean up. TODO -+ */ -+ struct dc_link_settings reported_link_cap; -+ struct dc_link_settings verified_link_cap; -+ struct dc_link_settings cur_link_settings; -+ struct dc_lane_settings cur_lane_setting; -+ struct dc_link_settings preferred_link_setting; -+ struct dc_link_training_overrides preferred_training_settings; -+ struct dp_audio_test_data audio_test_data; -+ -+ uint8_t ddc_hw_inst; -+ -+ uint8_t hpd_src; -+ -+ uint8_t link_enc_hw_inst; -+ /* DIG link encoder ID. Used as index in link encoder resource pool. -+ * For links with fixed mapping to DIG, this is not changed after dc_link -+ * object creation. -+ */ -+ uint32_t eng_id; -+ -+ bool test_pattern_enabled; -+ union compliance_test_state compliance_test_state; -+ -+ void *priv; -+ -+ void *ddc; -+ -+ bool aux_mode; -+ -+ /* Private to DC core */ -+ -+ struct dc *dc; -+ -+ void *ctx; -+ -+ void *panel_cntl; -+ void *link_enc; -+ struct graphics_object_id link_id; -+ /* Endpoint type distinguishes display endpoints which do not have entries -+ * in the BIOS connector table from those that do. Helps when tracking link -+ * encoder to display endpoint assignments. -+ */ -+ uint32_t ep_type; -+ union ddi_channel_mapping ddi_channel_mapping; -+ struct connector_device_tag_info device_tag; -+ struct dpcd_caps dpcd_caps; -+ uint32_t dongle_max_pix_clk; -+ unsigned short chip_caps; -+ unsigned int dpcd_sink_count; -+//#if defined(CONFIG_DRM_AMD_DC_HDCP) -+// struct hdcp_caps hdcp_caps; -+//#endif -+ uint32_t edp_revision; -+ union dpcd_sink_ext_caps dpcd_sink_ext_caps; -+ -+ /* -+ struct psr_settings psr_settings; -+ -+ // MST record stream using this link -+ struct link_flags { -+ bool dp_keep_receiver_powered; -+ bool dp_skip_DID2; -+ bool dp_skip_reset_segment; -+ } wa_flags; -+ struct link_mst_stream_allocation_table mst_stream_alloc_table; -+ -+ struct dc_link_status link_status; -+ -+ struct link_trace link_trace; -+ struct gpio *hpd_gpio; -+ enum dc_link_fec_state fec_state; -+ */ -+} my_dc_link_t; -+ -+typedef struct { -+ void* plane_state; -+ struct dc_stream_state *stream; -+} my_pipe_ctx_t; -+ -+struct panel_cntl_backlight_registers { -+ unsigned int BL_PWM_CNTL; -+ unsigned int BL_PWM_CNTL2; -+ unsigned int BL_PWM_PERIOD_CNTL; -+ unsigned int LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV; -+}; - --void RAD::applyPropertyFixes(IOService *service, uint32_t connectorNum) { -- if (service && getKernelVersion() >= KernelVersion::HighSierra) { -- // Starting with 10.13.2 this is important to fix sleep issues due to enforced 6 screens -- if (!service->getProperty("CFG,CFG_FB_LIMIT")) { -- DBGLOG("rad", "setting fb limit to %u", connectorNum); -- service->setProperty("CFG_FB_LIMIT", connectorNum, 32); -- } -+struct hw_asic_id { -+ uint32_t chip_id; -+ uint32_t chip_family; -+ uint32_t pci_revision_id; -+ uint32_t hw_internal_rev; -+ uint32_t vram_type; -+ uint32_t vram_width; -+ uint32_t feature_flags; -+ uint32_t fake_paths_num; -+ void *atombios_base_address; -+}; - -- // In the past we set CFG_USE_AGDC to false, which caused visual glitches and broken multimonitor support. -- // A better workaround is to disable AGDP just like we do globally. -- } --} -+struct dc_vram_info { -+ unsigned int num_chans; -+ unsigned int dram_channel_width_bytes; -+}; - --void RAD::updateConnectorsInfo(void *atomutils, t_getAtomObjectTableForType gettable, IOService *ctrl, RADConnectors::Connector *connectors, uint8_t *sz) { -- if (atomutils) { -- DBGLOG("rad", "getConnectorsInfo found %u connectors", *sz); -- RADConnectors::print(connectors, *sz); -- } -+struct dc_golden_table { -+ uint16_t dc_golden_table_ver; -+ uint32_t aux_dphy_rx_control0_val; -+ uint32_t aux_dphy_tx_control_val; -+ uint32_t aux_dphy_rx_control1_val; -+ uint32_t dc_gpio_aux_ctrl_0_val; -+ uint32_t dc_gpio_aux_ctrl_1_val; -+ uint32_t dc_gpio_aux_ctrl_2_val; -+ uint32_t dc_gpio_aux_ctrl_3_val; -+ uint32_t dc_gpio_aux_ctrl_4_val; -+ uint32_t dc_gpio_aux_ctrl_5_val; -+}; - -- // Check if the user wants to override automatically detected connectors -- auto cons = ctrl->getProperty("connectors"); -- if (cons) { -- auto consData = OSDynamicCast(OSData, cons); -- if (consData) { -- auto consPtr = consData->getBytesNoCopy(); -- auto consSize = consData->getLength(); -- -- uint32_t consCount; -- if (WIOKit::getOSDataValue(ctrl, "connector-count", consCount)) { -- *sz = consCount; -- DBGLOG("rad", "getConnectorsInfo got size override to %u", *sz); -- } -+struct dc_firmware_info { -+ struct pll_info { -+ uint32_t crystal_frequency; /* in KHz */ -+ uint32_t min_input_pxl_clk_pll_frequency; /* in KHz */ -+ uint32_t max_input_pxl_clk_pll_frequency; /* in KHz */ -+ uint32_t min_output_pxl_clk_pll_frequency; /* in KHz */ -+ uint32_t max_output_pxl_clk_pll_frequency; /* in KHz */ -+ } pll_info; -+ -+ struct firmware_feature { -+ uint32_t memory_clk_ss_percentage; -+ uint32_t engine_clk_ss_percentage; -+ } feature; -+ -+ uint32_t default_display_engine_pll_frequency; /* in KHz */ -+ uint32_t external_clock_source_frequency_for_dp; /* in KHz */ -+ uint32_t smu_gpu_pll_output_freq; /* in KHz */ -+ uint8_t min_allowed_bl_level; -+ uint8_t remote_display_config; -+ uint32_t default_memory_clk; /* in KHz */ -+ uint32_t default_engine_clk; /* in KHz */ -+ uint32_t dp_phy_ref_clk; /* in KHz - DCE12 only */ -+ uint32_t i2c_engine_ref_clk; /* in KHz - DCE12 only */ -+ bool oem_i2c_present; -+ uint8_t oem_i2c_obj_id; - -- if (consPtr && consSize > 0 && *sz > 0 && RADConnectors::valid(consSize, *sz)) { -- RADConnectors::copy(connectors, *sz, static_cast(consPtr), consSize); -- DBGLOG("rad", "getConnectorsInfo installed %u connectors", *sz); -- applyPropertyFixes(ctrl, *sz); -- } else { -- DBGLOG("rad", "getConnectorsInfo conoverrides have invalid size %u for %u num", consSize, *sz); -- } -- } else { -- DBGLOG("rad", "getConnectorsInfo conoverrides have invalid type"); -- } -- } else { -- if (atomutils) { -- DBGLOG("rad", "getConnectorsInfo attempting to autofix connectors"); -- uint8_t sHeader = 0, displayPathNum = 0, connectorObjectNum = 0; -- auto baseAddr = static_cast(gettable(atomutils, AtomObjectTableType::Common, &sHeader)) - sizeof(uint32_t); -- auto displayPaths = static_cast(gettable(atomutils, AtomObjectTableType::DisplayPath, &displayPathNum)); -- auto connectorObjects = static_cast(gettable(atomutils, AtomObjectTableType::ConnectorObject, &connectorObjectNum)); -- if (displayPathNum == connectorObjectNum) -- autocorrectConnectors(baseAddr, displayPaths, displayPathNum, connectorObjects, connectorObjectNum, connectors, *sz); -- else -- DBGLOG("rad", "getConnectorsInfo found different displaypaths %u and connectors %u", displayPathNum, connectorObjectNum); -- } -+}; - -- applyPropertyFixes(ctrl, *sz); -- -- // Prioritise connectors, since it may cause black screen on e.g. R9 370 -- const uint8_t *senseList = nullptr; -- uint8_t senseNum = 0; -- auto priData = OSDynamicCast(OSData, ctrl->getProperty("connector-priority")); -- if (priData) { -- senseList = static_cast(priData->getBytesNoCopy()); -- senseNum = static_cast(priData->getLength()); -- DBGLOG("rad", "getConnectorInfo found %u senses in connector-priority", senseNum); -- reprioritiseConnectors(senseList, senseNum, connectors, *sz); -- } else { -- DBGLOG("rad", "getConnectorInfo leaving unchaged priority"); -- } -- } -+#define NUMBER_OF_UCHAR_FOR_GUID 16 -+#define MAX_NUMBER_OF_EXT_DISPLAY_PATH 7 -+#define NUMBER_OF_CSR_M3_ARB 10 -+#define NUMBER_OF_DISP_CLK_VOLTAGE 4 -+#define NUMBER_OF_AVAILABLE_SCLK 5 - -- DBGLOG("rad", "getConnectorsInfo resulting %u connectors follow", *sz); -- RADConnectors::print(connectors, *sz); --} -+struct i2c_reg_info { -+ unsigned char i2c_reg_index; -+ unsigned char i2c_reg_val; -+}; - --void RAD::autocorrectConnectors(uint8_t *baseAddr, AtomDisplayObjectPath *displayPaths, uint8_t displayPathNum, AtomConnectorObject *connectorObjects, -- uint8_t connectorObjectNum, RADConnectors::Connector *connectors, uint8_t sz) { -- for (uint8_t i = 0; i < displayPathNum; i++) { -- if (!isEncoder(displayPaths[i].usGraphicObjIds)) { -- DBGLOG("rad", "autocorrectConnectors not encoder %X at %u", displayPaths[i].usGraphicObjIds, i); -- continue; -- } -+struct edp_info { -+ uint16_t edp_backlight_pwm_hz; -+ uint16_t edp_ss_percentage; -+ uint16_t edp_ss_rate_10hz; -+ uint8_t edp_pwr_on_off_delay; -+ uint8_t edp_pwr_on_vary_bl_to_blon; -+ uint8_t edp_pwr_down_bloff_to_vary_bloff; -+ uint8_t edp_panel_bpc; -+ uint8_t edp_bootup_bl_level; -+}; - -- uint8_t txmit = 0, enc = 0; -- if (!getTxEnc(displayPaths[i].usGraphicObjIds, txmit, enc)) -- continue; -+struct integrated_info { -+ struct clock_voltage_caps { -+ /* The Voltage Index indicated by FUSE, same voltage index -+ shared with SCLK DPM fuse table */ -+ uint32_t voltage_index; -+ /* Maximum clock supported with specified voltage index */ -+ uint32_t max_supported_clk; /* in KHz */ -+ } disp_clk_voltage[NUMBER_OF_DISP_CLK_VOLTAGE]; -+ -+ struct display_connection_info { -+ struct external_display_path { -+ /* A bit vector to show what devices are supported */ -+ uint32_t device_tag; -+ /* 16bit device ACPI id. */ -+ uint32_t device_acpi_enum; -+ /* A physical connector for displays to plug in, -+ using object connector definitions */ -+ struct graphics_object_id device_connector_id; -+ /* An index into external AUX/DDC channel LUT */ -+ uint8_t ext_aux_ddc_lut_index; -+ /* An index into external HPD pin LUT */ -+ uint8_t ext_hpd_pin_lut_index; -+ /* external encoder object id */ -+ struct graphics_object_id ext_encoder_obj_id; -+ /* XBAR mapping of the PHY channels */ -+ union ddi_channel_mapping channel_mapping; -+ -+ unsigned short caps; -+ } path[MAX_NUMBER_OF_EXT_DISPLAY_PATH]; -+ -+ uint8_t gu_id[NUMBER_OF_UCHAR_FOR_GUID]; -+ uint8_t checksum; -+ } ext_disp_conn_info; /* exiting long long time */ -+ -+ struct available_s_clk_list { -+ /* Maximum clock supported with specified voltage index */ -+ uint32_t supported_s_clk; /* in KHz */ -+ /* The Voltage Index indicated by FUSE for specified SCLK */ -+ uint32_t voltage_index; -+ /* The Voltage ID indicated by FUSE for specified SCLK */ -+ uint32_t voltage_id; -+ } avail_s_clk[NUMBER_OF_AVAILABLE_SCLK]; -+ -+ uint8_t memory_type; -+ uint8_t ma_channel_number; -+ uint32_t boot_up_engine_clock; /* in KHz */ -+ uint32_t dentist_vco_freq; /* in KHz */ -+ uint32_t boot_up_uma_clock; /* in KHz */ -+ uint32_t boot_up_req_display_vector; -+ uint32_t other_display_misc; -+ uint32_t gpu_cap_info; -+ uint32_t sb_mmio_base_addr; -+ uint32_t system_config; -+ uint32_t cpu_cap_info; -+ uint32_t max_nb_voltage; -+ uint32_t min_nb_voltage; -+ uint32_t boot_up_nb_voltage; -+ uint32_t ext_disp_conn_info_offset; -+ uint32_t csr_m3_arb_cntl_default[NUMBER_OF_CSR_M3_ARB]; -+ uint32_t csr_m3_arb_cntl_uvd[NUMBER_OF_CSR_M3_ARB]; -+ uint32_t csr_m3_arb_cntl_fs3d[NUMBER_OF_CSR_M3_ARB]; -+ uint32_t gmc_restore_reset_time; -+ uint32_t minimum_n_clk; -+ uint32_t idle_n_clk; -+ uint32_t ddr_dll_power_up_time; -+ uint32_t ddr_pll_power_up_time; -+ /* start for V6 */ -+ uint32_t pcie_clk_ss_type; -+ uint32_t lvds_ss_percentage; -+ uint32_t lvds_sspread_rate_in_10hz; -+ uint32_t hdmi_ss_percentage; -+ uint32_t hdmi_sspread_rate_in_10hz; -+ uint32_t dvi_ss_percentage; -+ uint32_t dvi_sspread_rate_in_10_hz; -+ uint32_t sclk_dpm_boost_margin; -+ uint32_t sclk_dpm_throttle_margin; -+ uint32_t sclk_dpm_tdp_limit_pg; -+ uint32_t sclk_dpm_tdp_limit_boost; -+ uint32_t boost_engine_clock; -+ uint32_t boost_vid_2bit; -+ uint32_t enable_boost; -+ uint32_t gnb_tdp_limit; -+ /* Start from V7 */ -+ uint32_t max_lvds_pclk_freq_in_single_link; -+ uint32_t lvds_misc; -+ uint32_t lvds_pwr_on_seq_dig_on_to_de_in_4ms; -+ uint32_t lvds_pwr_on_seq_de_to_vary_bl_in_4ms; -+ uint32_t lvds_pwr_off_seq_vary_bl_to_de_in4ms; -+ uint32_t lvds_pwr_off_seq_de_to_dig_on_in4ms; -+ uint32_t lvds_off_to_on_delay_in_4ms; -+ uint32_t lvds_pwr_on_seq_vary_bl_to_blon_in_4ms; -+ uint32_t lvds_pwr_off_seq_blon_to_vary_bl_in_4ms; -+ uint32_t lvds_reserved1; -+ uint32_t lvds_bit_depth_control_val; -+ //Start from V9 -+ unsigned char dp0_ext_hdmi_slv_addr; -+ unsigned char dp0_ext_hdmi_reg_num; -+ struct i2c_reg_info dp0_ext_hdmi_reg_settings[9]; -+ unsigned char dp0_ext_hdmi_6g_reg_num; -+ struct i2c_reg_info dp0_ext_hdmi_6g_reg_settings[3]; -+ unsigned char dp1_ext_hdmi_slv_addr; -+ unsigned char dp1_ext_hdmi_reg_num; -+ struct i2c_reg_info dp1_ext_hdmi_reg_settings[9]; -+ unsigned char dp1_ext_hdmi_6g_reg_num; -+ struct i2c_reg_info dp1_ext_hdmi_6g_reg_settings[3]; -+ unsigned char dp2_ext_hdmi_slv_addr; -+ unsigned char dp2_ext_hdmi_reg_num; -+ struct i2c_reg_info dp2_ext_hdmi_reg_settings[9]; -+ unsigned char dp2_ext_hdmi_6g_reg_num; -+ struct i2c_reg_info dp2_ext_hdmi_6g_reg_settings[3]; -+ unsigned char dp3_ext_hdmi_slv_addr; -+ unsigned char dp3_ext_hdmi_reg_num; -+ struct i2c_reg_info dp3_ext_hdmi_reg_settings[9]; -+ unsigned char dp3_ext_hdmi_6g_reg_num; -+ struct i2c_reg_info dp3_ext_hdmi_6g_reg_settings[3]; -+ /* V11 */ -+ uint32_t dp_ss_control; -+ /* V2.1 */ -+ struct edp_info edp1_info; -+ struct edp_info edp2_info; -+}; - -- uint8_t sense = getSenseID(baseAddr + connectorObjects[i].usRecordOffset); -- if (!sense) { -- DBGLOG("rad", "autocorrectConnectors failed to detect sense for %u connector", i); -- continue; -- } -+struct dc_bios { -+ void *funcs; - -- DBGLOG("rad", "autocorrectConnectors found txmit %02X enc %02X sense %02X for %u connector", txmit, enc, sense, i); -+ uint8_t *bios; -+ uint32_t bios_size; - -- autocorrectConnector(getConnectorID(displayPaths[i].usConnObjectId), sense, txmit, enc, connectors, sz); -- } --} -+ uint8_t *bios_local_image; - --void RAD::autocorrectConnector(uint8_t connector, uint8_t sense, uint8_t txmit, uint8_t enc, RADConnectors::Connector *connectors, uint8_t sz) { -- // This function attempts to fix the following issues: -- // -- // 1. Incompatible DVI transmitter on 290X, 370 and probably some other models -- // In this case a correct transmitter is detected by AtiAtomBiosDce60::getPropertiesForEncoderObject, however, later -- // in AtiAtomBiosDce60::getPropertiesForConnectorObject for DVI DL and TITFP513 this value is conjuncted with 0xCF, -- // which makes it wrong: 0x10 -> 0, 0x11 -> 1. As a result one gets black screen when connecting multiple displays. -- // getPropertiesForEncoderObject takes usGraphicObjIds and getPropertiesForConnectorObject takes usConnObjectId -- -- if (callbackRAD->dviSingleLink) { -- if (connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I && -- connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D && -- connector != CONNECTOR_OBJECT_ID_LVDS) { -- DBGLOG("rad", "autocorrectConnector found unsupported connector type %02X", connector); -- return; -- } -+ void *ctx; -+ void *regs; -+ struct integrated_info *integrated_info; -+ struct dc_firmware_info fw_info; -+ bool fw_info_valid; -+ struct dc_vram_info vram_info; -+ struct dc_golden_table golden_table; -+}; - -- auto fixTransmit = [](auto &con, uint8_t idx, uint8_t sense, uint8_t txmit) { -- if (con.sense == sense) { -- if (con.transmitter != txmit && (con.transmitter & 0xCF) == con.transmitter) { -- DBGLOG("rad", "autocorrectConnector replacing txmit %02X with %02X for %u connector sense %02X", -- con.transmitter, txmit, idx, sense); -- con.transmitter = txmit; -- } -- return true; -- } -- return false; -- }; -+struct dmcu_version { -+ unsigned int interface_version; -+ unsigned int abm_version; -+ unsigned int psr_version; -+ unsigned int build_version; -+}; - -- bool isModern = RADConnectors::modern(); -- for (uint8_t j = 0; j < sz; j++) { -- if (isModern) { -- auto &con = (&connectors->modern)[j]; -- if (fixTransmit(con, j, sense, txmit)) -- break; -- } else { -- auto &con = (&connectors->legacy)[j]; -- if (fixTransmit(con, j, sense, txmit)) -- break; -- } -- } -- } else { -- DBGLOG("rad", "autocorrectConnector use -raddvi to enable dvi autocorrection"); -- } -+struct dc_versions { -+ const char *dc_ver; -+ struct dmcu_version dmcu_version; -+}; -+ -+#define MAX_PLANES 6 -+ -+struct dc_plane_cap { -+ uint32_t type; -+ uint32_t blends_with_above : 1; -+ uint32_t blends_with_below : 1; -+ uint32_t per_pixel_alpha : 1; -+ struct { -+ uint32_t argb8888 : 1; -+ uint32_t nv12 : 1; -+ uint32_t fp16 : 1; -+ uint32_t p010 : 1; -+ uint32_t ayuv : 1; -+ } pixel_format_support; -+ // max upscaling factor x1000 -+ // upscaling factors are always >= 1 -+ // for example, 1080p -> 8K is 4.0, or 4000 raw value -+ struct { -+ uint32_t argb8888; -+ uint32_t nv12; -+ uint32_t fp16; -+ } max_upscale_factor; -+ // max downscale factor x1000 -+ // downscale factors are always <= 1 -+ // for example, 8K -> 1080p is 0.25, or 250 raw value -+ struct { -+ uint32_t argb8888; -+ uint32_t nv12; -+ uint32_t fp16; -+ } max_downscale_factor; -+ // minimal width/height -+ uint32_t min_width; -+ uint32_t min_height; -+}; -+ -+struct rom_curve_caps { -+ uint16_t srgb : 1; -+ uint16_t bt2020 : 1; -+ uint16_t gamma2_2 : 1; -+ uint16_t pq : 1; -+ uint16_t hlg : 1; -+}; -+ -+struct dpp_color_caps { -+ uint16_t dcn_arch : 1; // all DCE generations treated the same -+ // input lut is different than most LUTs, just plain 256-entry lookup -+ uint16_t input_lut_shared : 1; // shared with DGAM -+ uint16_t icsc : 1; -+ uint16_t dgam_ram : 1; -+ uint16_t post_csc : 1; // before gamut remap -+ uint16_t gamma_corr : 1; -+ -+ // hdr_mult and gamut remap always available in DPP (in that order) -+ // 3d lut implies shaper LUT, -+ // it may be shared with MPC - check MPC:shared_3d_lut flag -+ uint16_t hw_3d_lut : 1; -+ uint16_t ogam_ram : 1; // blnd gam -+ uint16_t ocsc : 1; -+ uint16_t dgam_rom_for_yuv : 1; -+ struct rom_curve_caps dgam_rom_caps; -+ struct rom_curve_caps ogam_rom_caps; -+}; -+ -+struct mpc_color_caps { -+ uint16_t gamut_remap : 1; -+ uint16_t ogam_ram : 1; -+ uint16_t ocsc : 1; -+ uint16_t num_3dluts : 3; //3d lut always assumes a preceding shaper LUT -+ uint16_t shared_3d_lut:1; //can be in either DPP or MPC, but single instance -+ -+ struct rom_curve_caps ogam_rom_caps; -+}; -+ -+struct dc_color_caps { -+ struct dpp_color_caps dpp; -+ struct mpc_color_caps mpc; -+}; -+ -+struct dc_caps { -+ uint32_t max_streams; -+ uint32_t max_links; -+ uint32_t max_audios; -+ uint32_t max_slave_planes; -+ uint32_t max_slave_yuv_planes; -+ uint32_t max_slave_rgb_planes; -+ uint32_t max_planes; -+ uint32_t max_downscale_ratio; -+ uint32_t i2c_speed_in_khz; -+ uint32_t i2c_speed_in_khz_hdcp; -+ uint32_t dmdata_alloc_size; -+ unsigned int max_cursor_size; -+ unsigned int max_video_width; -+ unsigned int min_horizontal_blanking_period; -+ int linear_pitch_alignment; -+ bool dcc_const_color; -+ bool dynamic_audio; -+ bool is_apu; -+ bool dual_link_dvi; -+ bool post_blend_color_processing; -+ bool force_dp_tps4_for_cp2520; -+ bool disable_dp_clk_share; -+ bool psp_setup_panel_mode; -+ bool extended_aux_timeout_support; -+ bool dmcub_support; -+ uint32_t num_of_internal_disp; -+ uint32_t max_dp_protocol_version; -+ unsigned int mall_size_per_mem_channel; -+ unsigned int mall_size_total; -+ unsigned int cursor_cache_size; -+ struct dc_plane_cap planes[MAX_PLANES]; -+ struct dc_color_caps color; -+ bool vbios_lttpr_aware; -+ bool vbios_lttpr_enable; -+}; -+ -+struct dc_cap_funcs { -+ bool (*get_dcc_compression_cap)(const struct dc *dc, -+ const struct dc_dcc_surface_param *input, -+ struct dc_surface_dcc_cap *output); -+}; -+ -+struct dc_config { -+ bool gpu_vm_support; -+ bool disable_disp_pll_sharing; -+ bool fbc_support; -+ bool disable_fractional_pwm; -+ bool allow_seamless_boot_optimization; -+ bool power_down_display_on_boot; -+ bool edp_not_connected; -+ bool edp_no_power_sequencing; -+ bool force_enum_edp; -+ bool forced_clocks; -+ bool allow_lttpr_non_transparent_mode; -+ bool multi_mon_pp_mclk_switch; -+ bool disable_dmcu; -+ bool enable_4to1MPC; -+ bool allow_edp_hotplug_detection; -+//#if defined(CONFIG_DRM_AMD_DC_DCN) -+// bool clamp_min_dcfclk; -+//#endif -+ uint64_t vblank_alignment_dto_params; -+ uint8_t vblank_alignment_max_frame_time_diff; -+ bool is_asymmetric_memory; -+ bool is_single_rank_dimm; -+}; -+ -+struct dc_bw_validation_profile { -+ bool enable; -+ -+ unsigned long long total_ticks; -+ unsigned long long voltage_level_ticks; -+ unsigned long long watermark_ticks; -+ unsigned long long rq_dlg_ticks; -+ -+ unsigned long long total_count; -+ unsigned long long skip_fast_count; -+ unsigned long long skip_pass_count; -+ unsigned long long skip_fail_count; -+}; -+ -+union mem_low_power_enable_options { -+ struct { -+ bool vga: 1; -+ bool i2c: 1; -+ bool dmcu: 1; -+ bool dscl: 1; -+ bool cm: 1; -+ bool mpc: 1; -+ bool optc: 1; -+ } bits; -+ uint32_t u32All; -+}; -+ -+struct dc_debug_options { -+ uint32_t visual_confirm; -+ bool sanity_checks; -+ bool max_disp_clk; -+ bool surface_trace; -+ bool timing_trace; -+ bool clock_trace; -+ bool validation_trace; -+ bool bandwidth_calcs_trace; -+ int max_downscale_src_width; -+ -+ /* stutter efficiency related */ -+ bool disable_stutter; -+ bool use_max_lb; -+ uint32_t disable_dcc; -+ uint32_t pipe_split_policy; -+ bool force_single_disp_pipe_split; -+ bool voltage_align_fclk; -+ bool disable_min_fclk; -+ -+ bool disable_dfs_bypass; -+ bool disable_dpp_power_gate; -+ bool disable_hubp_power_gate; -+ bool disable_dsc_power_gate; -+ int dsc_min_slice_height_override; -+ int dsc_bpp_increment_div; -+ bool native422_support; -+ bool disable_pplib_wm_range; -+ uint32_t pplib_wm_report_mode; -+ unsigned int min_disp_clk_khz; -+ unsigned int min_dpp_clk_khz; -+ int sr_exit_time_dpm0_ns; -+ int sr_enter_plus_exit_time_dpm0_ns; -+ int sr_exit_time_ns; -+ int sr_enter_plus_exit_time_ns; -+ int urgent_latency_ns; -+ uint32_t underflow_assert_delay_us; -+ int percent_of_ideal_drambw; -+ int dram_clock_change_latency_ns; -+ bool optimized_watermark; -+ int always_scale; -+ bool disable_pplib_clock_request; -+ bool disable_clock_gate; -+ bool disable_mem_low_power; -+//#if defined(CONFIG_DRM_AMD_DC_DCN) -+// bool pstate_enabled; -+//#endif -+ bool disable_dmcu; -+ bool disable_psr; -+ bool force_abm_enable; -+ bool disable_stereo_support; -+ bool vsr_support; -+ bool performance_trace; -+ bool az_endpoint_mute_only; -+ bool always_use_regamma; -+ bool recovery_enabled; -+ bool avoid_vbios_exec_table; -+ bool scl_reset_length10; -+ bool hdmi20_disable; -+ bool skip_detection_link_training; -+ uint32_t edid_read_retry_times; -+ bool remove_disconnect_edp; -+ unsigned int force_odm_combine; //bit vector based on otg inst -+//#if defined(CONFIG_DRM_AMD_DC_DCN) -+// unsigned int force_odm_combine_4to1; //bit vector based on otg inst -+// bool disable_z9_mpc; -+//#endif -+ unsigned int force_fclk_khz; -+ bool enable_tri_buf; -+ bool dmub_offload_enabled; -+ bool dmcub_emulation; -+//#if defined(CONFIG_DRM_AMD_DC_DCN) -+// bool disable_idle_power_optimizations; -+// unsigned int mall_size_override; -+// unsigned int mall_additional_timer_percent; -+// bool mall_error_as_fatal; -+//#endif -+ bool dmub_command_table; /* for testing only */ -+ struct dc_bw_validation_profile bw_val_profile; -+ bool disable_fec; -+ bool disable_48mhz_pwrdwn; -+ /* This forces a hard min on the DCFCLK requested to SMU/PP -+ * watermarks are not affected. -+ */ -+ unsigned int force_min_dcfclk_mhz; -+//#if defined(CONFIG_DRM_AMD_DC_DCN) -+// int dwb_fi_phase; -+//#endif -+ bool disable_timing_sync; -+ bool cm_in_bypass; -+ int force_clock_mode;/*every mode change.*/ -+ -+ bool disable_dram_clock_change_vactive_support; -+ bool validate_dml_output; -+ bool enable_dmcub_surface_flip; -+ bool usbc_combo_phy_reset_wa; -+ bool disable_dsc; -+ bool enable_dram_clock_change_one_display_vactive; -+ union mem_low_power_enable_options enable_mem_low_power; -+ bool force_vblank_alignment; -+ -+ /* Enable dmub aux for legacy ddc */ -+ bool enable_dmub_aux_for_legacy_ddc; -+ bool optimize_edp_link_rate; /* eDP ILR */ -+ /* force enable edp FEC */ -+ bool force_enable_edp_fec; -+ /* FEC/PSR1 sequence enable delay in 100us */ -+ uint8_t fec_enable_delay_in100us; -+//#if defined(CONFIG_DRM_AMD_DC_DCN) -+// bool disable_z10; -+// bool enable_sw_cntl_psr; -+//#endif -+}; -+ -+struct dc_bounding_box_overrides { -+ int sr_exit_time_ns; -+ int sr_enter_plus_exit_time_ns; -+ int urgent_latency_ns; -+ int percent_of_ideal_drambw; -+ int dram_clock_change_latency_ns; -+ int dummy_clock_change_latency_ns; -+ /* This forces a hard min on the DCFCLK we use -+ * for DML. Unlike the debug option for forcing -+ * DCFCLK, this override affects watermark calculations -+ */ -+ int min_dcfclk_mhz; -+}; -+ -+struct dc_bug_wa { -+ bool no_connect_phy_config; -+ bool dedcn20_305_wa; -+ bool skip_clock_update; -+ bool lt_early_cr_pattern; -+}; -+ -+struct dc { -+ struct dc_versions versions; -+ struct dc_caps caps; -+ struct dc_cap_funcs cap_funcs; -+ struct dc_config config; -+ struct dc_debug_options debug; -+ struct dc_bounding_box_overrides bb_overrides; -+ struct dc_bug_wa work_arounds; -+ /* -+ struct dc_context *ctx; -+ struct dc_phy_addr_space_config vm_pa_config; -+ -+ uint8_t link_count; -+ struct dc_link *links[MAX_PIPES * 2]; -+ -+ struct dc_state *current_state; -+ struct resource_pool *res_pool; -+ -+ struct clk_mgr *clk_mgr; -+ -+ // Display Engine Clock levels -+ struct dm_pp_clock_levels sclk_lvls; -+ -+ // Inputs into BW and WM calculations. -+ struct bw_calcs_dceip *bw_dceip; -+ struct bw_calcs_vbios *bw_vbios; -+#ifdef CONFIG_DRM_AMD_DC_DCN -+ struct dcn_soc_bounding_box *dcn_soc; -+ struct dcn_ip_params *dcn_ip; -+ struct display_mode_lib dml; -+#endif -+ -+ // HW functions -+ struct hw_sequencer_funcs hwss; -+ struct dce_hwseq *hwseq; -+ -+ // Require to optimize clocks and bandwidth for added/removed planes -+ bool optimized_required; -+ bool wm_optimized_required; -+#if defined(CONFIG_DRM_AMD_DC_DCN) -+ bool idle_optimizations_allowed; -+#endif -+ -+ // Require to maintain clocks and bandwidth for UEFI enabled HW -+ -+ // FBC compressor -+ struct compressor *fbc_compressor; -+ -+ struct dc_debug_data debug_data; -+ struct dpcd_vendor_signature vendor_signature; -+ -+ const char *build_id; -+ struct vm_helper *vm_helper; -+ */ -+}; -+ -+struct dc_context { -+ struct dc *dc; -+ -+ void *driver_context; /* e.g. amdgpu_device */ -+ void *perf_trace; -+ void *cgs_device; -+ -+ uint32_t dce_environment; -+ struct hw_asic_id asic_id; -+ -+ /* todo: below should probably move to dc. to facilitate removal -+ * of AS we will store these here -+ */ -+ uint32_t dce_version; -+ struct dc_bios *dc_bios; -+ bool created_bios; -+ void *gpio_service; -+ uint32_t dc_sink_id_count; -+ uint32_t dc_stream_id_count; -+ uint32_t dc_edp_id_count; -+ uint64_t fbc_gpu_addr; -+ void *dmub_srv; -+}; -+ -+struct panel_cntl_funcs { -+ void (*destroy)(struct panel_cntl **panel_cntl); -+ uint32_t (*hw_init)(struct panel_cntl *panel_cntl); -+ bool (*is_panel_backlight_on)(struct panel_cntl *panel_cntl); -+ bool (*is_panel_powered_on)(struct panel_cntl *panel_cntl); -+ void (*store_backlight_level)(struct panel_cntl *panel_cntl); -+ void (*driver_set_backlight)(struct panel_cntl *panel_cntl, -+ uint32_t backlight_pwm_u16_16); -+ uint32_t (*get_current_backlight)(struct panel_cntl *panel_cntl); -+}; -+struct panel_cntl { -+ const struct panel_cntl_funcs *funcs; -+ struct dc_context *ctx; -+ uint32_t inst; -+ /* registers setting needs to be saved and restored at InitBacklight */ -+ struct panel_cntl_backlight_registers stored_backlight_registers; -+}; -+bool g_is_sleep = false; -+uint64_t g_last_sleep_time = 0; -+static my_dc_link_t* g_dc_link_ptr = NULL; -+static mach_vm_address_t orig_dce110_edp_backlight_control; -+ -+static mach_vm_address_t orig_dce110_edp_power_control; -+static void wrap_dce110_edp_power_control(my_dc_link_t *dc_link, bool power_up) { -+ g_dc_link_ptr = dc_link; -+ if (orig_dce110_edp_power_control) { -+ FunctionCast(wrap_dce110_edp_power_control, orig_dce110_edp_power_control)(dc_link, power_up); -+ } -+ SYSLOG("igfx", "wrap_dce110_edp_power_control %d end - %p", power_up, g_dc_link_ptr); - } - --void RAD::reprioritiseConnectors(const uint8_t *senseList, uint8_t senseNum, RADConnectors::Connector *connectors, uint8_t sz) { -- static constexpr uint32_t typeList[] { -- RADConnectors::ConnectorLVDS, -- RADConnectors::ConnectorDigitalDVI, -- RADConnectors::ConnectorHDMI, -- RADConnectors::ConnectorDP, -- RADConnectors::ConnectorVGA -- }; -- static constexpr uint8_t typeNum {static_cast(arrsize(typeList))}; -- -- bool isModern = RADConnectors::modern(); -- uint16_t priCount = 1; -- // Automatically detected connectors have equal priority (0), which often results in black screen -- // This allows to change this firstly by user-defined list, then by type list. -- //TODO: priority is ignored for 5xxx and 6xxx GPUs, should we manually reorder items? -- for (uint8_t i = 0; i < senseNum + typeNum + 1; i++) { -- for (uint8_t j = 0; j < sz; j++) { -- auto reorder = [&](auto &con) { -- if (i == senseNum + typeNum) { -- if (con.priority == 0) -- con.priority = priCount++; -- } else if (i < senseNum) { -- if (con.sense == senseList[i]) { -- DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by sense", con.sense, priCount); -- con.priority = priCount++; -- return true; -- } -- } else { -- if (con.priority == 0 && con.type == typeList[i-senseNum]) { -- DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by type", con.sense, priCount); -- con.priority = priCount++; -- } -- } -- return false; -- }; -- -- if ((isModern && reorder((&connectors->modern)[j])) || -- (!isModern && reorder((&connectors->legacy)[j]))) -- break; -- } -- } -+static char wrap_dce110_edp_backlight_control(my_dc_link_t *that, uint8_t a2) { -+ //SYSLOG("igfx", "wrap_dce110_edp_backlight_control start %p:%lu,%lu,%lu,%lu,%lu", that, on_or_off, a3, a4, a5, a6); -+ g_dc_link_ptr = that; -+ //if (g_dc_link_ptr && g_dc_link_ptr->dc) { -+ // SYSLOG("igfx", "dce110_edp_backlight_control disable_fractional_pwm: %u", g_dc_link_ptr->dc->config.disable_fractional_pwm); -+ //} -+ char ret = FunctionCast(wrap_dce110_edp_backlight_control, orig_dce110_edp_backlight_control)(that, a2); -+ SYSLOG("igfx", "wrap_dce110_edp_backlight_control end - %p, %d", g_dc_link_ptr, ret); -+ -+ if (g_is_sleep) { -+ g_is_sleep = false; -+ -+ if (g_dc_link_ptr) { -+ wrap_dce110_edp_backlight_control(g_dc_link_ptr, false); -+ wrap_dce110_edp_power_control(g_dc_link_ptr, false); -+ IOSleep(200); -+ wrap_dce110_edp_power_control(g_dc_link_ptr, true); -+ wrap_dce110_edp_backlight_control(g_dc_link_ptr, true); -+ } -+ } -+ -+ return ret; - } - --void RAD::setGvaProperties(IOService *accelService) { -- auto codecStr = OSDynamicCast(OSString, accelService->getProperty("IOGVACodec")); -- if (codecStr == nullptr) { -- DBGLOG("rad", "updating X4000 accelerator IOGVACodec to VCE"); -- accelService->setProperty("IOGVACodec", "VCE"); -- } else { -- auto codec = codecStr->getCStringNoCopy(); -- DBGLOG("rad", "X4000 accelerator IOGVACodec is already set to %s", safeString(codec)); -- if (codec != nullptr && strncmp(codec, "AMD", strlen("AMD")) == 0) { -- bool needsDecode = accelService->getProperty("IOGVAHEVCDecode") == nullptr; -- bool needsEncode = accelService->getProperty("IOGVAHEVCEncode") == nullptr; -- if (needsDecode) { -- OSObject *VTMaxDecodeLevel = OSNumber::withNumber(153, 32); -- OSString *VTMaxDecodeLevelKey = OSString::withCString("VTMaxDecodeLevel"); -- OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); -- OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(3); -- OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); -- OSString *VTPerProfileDetailsKey2 = OSString::withCString("2"); -- OSString *VTPerProfileDetailsKey3 = OSString::withCString("3"); -- -- OSArray *VTSupportedProfileArray = OSArray::withCapacity(3); -- OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); -- OSNumber *VTSupportedProfileArray2 = OSNumber::withNumber(2, 32); -- OSNumber *VTSupportedProfileArray3 = OSNumber::withNumber(3, 32); -- -- OSDictionary *IOGVAHEVCDecodeCapabilities = OSDictionary::withCapacity(2); -- OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); -- OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); -- -- if (VTMaxDecodeLevel != nullptr && VTMaxDecodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && -- VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTPerProfileDetailsKey2 != nullptr && -- VTPerProfileDetailsKey3 != nullptr && VTSupportedProfileArrayKey != nullptr && VTSupportedProfileArray1 != nullptr && -- VTSupportedProfileArray2 != nullptr && VTSupportedProfileArray3 != nullptr && VTSupportedProfileArray != nullptr && -- VTPerProfileDetailsKey != nullptr && IOGVAHEVCDecodeCapabilities != nullptr) { -- VTPerProfileDetailsInner->setObject(VTMaxDecodeLevelKey, VTMaxDecodeLevel); -- VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); -- VTPerProfileDetails->setObject(VTPerProfileDetailsKey2, VTPerProfileDetailsInner); -- VTPerProfileDetails->setObject(VTPerProfileDetailsKey3, VTPerProfileDetailsInner); -- -- VTSupportedProfileArray->setObject(VTSupportedProfileArray1); -- VTSupportedProfileArray->setObject(VTSupportedProfileArray2); -- VTSupportedProfileArray->setObject(VTSupportedProfileArray3); -- -- IOGVAHEVCDecodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); -- IOGVAHEVCDecodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); -- -- accelService->setProperty("IOGVAHEVCDecode", "1"); -- accelService->setProperty("IOGVAHEVCDecodeCapabilities", IOGVAHEVCDecodeCapabilities); -- -- DBGLOG("rad", "recovering IOGVAHEVCDecode"); -- } else { -- SYSLOG("rad", "allocation failure in IOGVAHEVCDecode"); -- } -- -- OSSafeReleaseNULL(VTMaxDecodeLevel); -- OSSafeReleaseNULL(VTMaxDecodeLevelKey); -- OSSafeReleaseNULL(VTPerProfileDetailsInner); -- OSSafeReleaseNULL(VTPerProfileDetails); -- OSSafeReleaseNULL(VTPerProfileDetailsKey1); -- OSSafeReleaseNULL(VTPerProfileDetailsKey2); -- OSSafeReleaseNULL(VTPerProfileDetailsKey3); -- OSSafeReleaseNULL(VTSupportedProfileArrayKey); -- OSSafeReleaseNULL(VTSupportedProfileArray1); -- OSSafeReleaseNULL(VTSupportedProfileArray2); -- OSSafeReleaseNULL(VTSupportedProfileArray3); -- OSSafeReleaseNULL(VTSupportedProfileArray); -- OSSafeReleaseNULL(VTPerProfileDetailsKey); -- OSSafeReleaseNULL(IOGVAHEVCDecodeCapabilities); -- } -+IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t value) { -+ IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute)(framebuffer, connectIndex, attribute, value); -+ if (attribute != (UInt32)'bklt') { -+ return ret; -+ } -+ -+ if (callbackRAD->maxPwmBacklightLvl == 0) { -+ DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute zero maxPwmBacklightLvl"); -+ return 0; -+ } -+ -+ if (callbackRAD->panelCntlPtr == nullptr) { -+ DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null panel cntl"); -+ return 0; -+ } -+ -+ if (callbackRAD->orgDceDriverSetBacklight == nullptr) { -+ DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null orgDcLinkSetBacklightLevel"); -+ return 0; -+ } -+ -+ // set the backlight of AMD navi10 driver -+ callbackRAD->curPwmBacklightLvl = (uint32_t)value; -+ uint32_t btlper = callbackRAD->curPwmBacklightLvl*100.0 / callbackRAD->maxPwmBacklightLvl; -+ if (btlper < 0) { -+ btlper = 0; -+ } else if (btlper > 100) { -+ btlper = 100; -+ } -+ -+ int pwmval = (int)((btlper / 100.0) * 0xFF) << 8; -+ if (pwmval >= 0xFF00) { -+ // This is from the dmcu_set_backlight_level function of Linux source -+ // ... -+ // if (backlight_pwm_u16_16 & 0x10000) -+ // backlight_8_bit = 0xFF; -+ // else -+ // backlight_8_bit = (backlight_pwm_u16_16 >> 8) & 0xFF; -+ // ... -+ // The max brightness should have 0x10000 bit set -+ pwmval = 0x1FF00; -+ } -+ -+ /*if (g_dc_link_ptr) { -+ __int64 v10 = 0; -+ __int64 v9 = *(_QWORD *)(*(_QWORD *)((__int64)g_dc_link_ptr + 304) + 944LL); -+ while ( true ) { -+ __int64 stream = *(_QWORD *)(v9 + v10 + 496); -+ if ( stream ) { -+ if (*(_QWORD *)(stream + 8) == (__int64)g_dc_link_ptr) { -+ __int64 pipe_ctx = (v9 + v10 + 488); -+ //SYSLOG("igfx", "my_set_backlight_lvl %p:%p, %d, %d", g_dc_link_ptr, pipe_ctx, backlight_pwm_u16_16, ramp); -+ _QWORD *a1 = (_QWORD *)pipe_ctx; -+ __int64 v5 = *(_QWORD *)(a1[1] + 8LL); -+ __int64 v6 = *(_QWORD *)(v5 + 320); -+ struct panel_cntl *panel_cntl = (struct panel_cntl *)v6; -+ SYSLOG("igfx", "panel_cntl: %p", panel_cntl); -+ break; -+ } else { -+ SYSLOG("igfx", "steam link %lu != %p", *(_QWORD *)(stream + 8), g_dc_link_ptr); -+ } -+ } -+ v10 += 1280LL; -+ if ( v10 >= 7680 ) -+ break; -+ } -+ } else { -+ DBGLOG("igfx", "null g_dc_link_ptr"); -+ }*/ -+ -+ struct panel_cntl* ppc = (struct panel_cntl*)callbackRAD->panelCntlPtr; -+ DBGLOG("igfx", "fb 0x%p, idx: %d, set brightness: 0x%p -> 0x%x, st: %d", framebuffer, connectIndex, callbackRAD->panelCntlPtr, pwmval, ppc->funcs->is_panel_powered_on(ppc)); -+ callbackRAD->orgDceDriverSetBacklight(callbackRAD->panelCntlPtr, pwmval); -+ return 0; -+} - -- if (needsEncode) { -- OSObject *VTMaxEncodeLevel = OSNumber::withNumber(153, 32); -- OSString *VTMaxEncodeLevelKey = OSString::withCString("VTMaxEncodeLevel"); -- -- OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); -- OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(1); -- OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); -- -- OSArray *VTSupportedProfileArray = OSArray::withCapacity(1); -- OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); -- -- OSDictionary *IOGVAHEVCEncodeCapabilities = OSDictionary::withCapacity(4); -- OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); -- OSString *VTQualityRatingKey = OSString::withCString("VTQualityRating"); -- OSNumber *VTQualityRating = OSNumber::withNumber(50, 32); -- OSString *VTRatingKey = OSString::withCString("VTRating"); -- OSNumber *VTRating = OSNumber::withNumber(350, 32); -- OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); -- -- if (VTMaxEncodeLevel != nullptr && VTMaxEncodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && -- VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTSupportedProfileArrayKey != nullptr && -- VTSupportedProfileArray1 != nullptr && VTSupportedProfileArray != nullptr && VTPerProfileDetailsKey != nullptr && -- VTQualityRatingKey != nullptr && VTQualityRating != nullptr && VTRatingKey != nullptr && VTRating != nullptr && -- IOGVAHEVCEncodeCapabilities != nullptr) { -- -- VTPerProfileDetailsInner->setObject(VTMaxEncodeLevelKey, VTMaxEncodeLevel); -- VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); -- VTSupportedProfileArray->setObject(VTSupportedProfileArray1); -- -- IOGVAHEVCEncodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); -- IOGVAHEVCEncodeCapabilities->setObject(VTQualityRatingKey, VTQualityRating); -- IOGVAHEVCEncodeCapabilities->setObject(VTRatingKey, VTRating); -- IOGVAHEVCEncodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); -- -- accelService->setProperty("IOGVAHEVCEncode", "1"); -- accelService->setProperty("IOGVAHEVCEncodeCapabilities", IOGVAHEVCEncodeCapabilities); -- -- DBGLOG("rad", "recovering IOGVAHEVCEncode"); -- } else { -- SYSLOG("rad", "allocation failure in IOGVAHEVCEncode"); -- } -- -- OSSafeReleaseNULL(VTMaxEncodeLevel); -- OSSafeReleaseNULL(VTMaxEncodeLevelKey); -- OSSafeReleaseNULL(VTPerProfileDetailsInner); -- OSSafeReleaseNULL(VTPerProfileDetails); -- OSSafeReleaseNULL(VTPerProfileDetailsKey1); -- OSSafeReleaseNULL(VTSupportedProfileArrayKey); -- OSSafeReleaseNULL(VTSupportedProfileArray1); -- OSSafeReleaseNULL(VTSupportedProfileArray); -- OSSafeReleaseNULL(VTPerProfileDetailsKey); -- OSSafeReleaseNULL(VTQualityRatingKey); -- OSSafeReleaseNULL(VTQualityRating); -- OSSafeReleaseNULL(VTRatingKey); -- OSSafeReleaseNULL(VTRating); -- OSSafeReleaseNULL(IOGVAHEVCEncodeCapabilities); -- } -- } -- } -+uint32_t RAD::wrapDcePanelCntlHwInit(void *panel_cntl) { -+ callbackRAD->panelCntlPtr = panel_cntl; -+ callbackRAD->updatePwmMaxBrightnessFromInternalDisplay(); // read max brightness value from IOReg -+ struct panel_cntl* ppc = (struct panel_cntl*)callbackRAD->panelCntlPtr; -+ DBGLOG("igfx", "wrapDcePanelCntlHwInit: %p, bl: %d, pw: %d, bl: %d", panel_cntl, ppc->funcs->is_panel_backlight_on(ppc), ppc->funcs->is_panel_powered_on(ppc), ppc->funcs->get_current_backlight(ppc)); -+ uint32_t ret = FunctionCast(wrapDcePanelCntlHwInit, callbackRAD->orgDcePanelCntlHwInit)(panel_cntl); -+ //if (ppc->funcs->is_panel_backlight_on(ppc) == 0 && ppc->funcs->is_panel_powered_on(ppc) == 1 && g_dc_link_ptr) { -+ //wrap_dce110_edp_backlight_control(g_dc_link_ptr, 0); -+ //wrap_dce110_edp_power_control(g_dc_link_ptr, false); -+ // g_is_sleep = true; -+ //} -+ DBGLOG("igfx", "wrapDcePanelCntlHwInit: %p - %u, st: %d", panel_cntl, ret, ppc->funcs->is_panel_powered_on(ppc)); -+ return ret; - } - --void RAD::updateAccelConfig(size_t hwIndex, IOService *accelService, const char **accelConfig) { -- if (accelService && accelConfig) { -- if (fixConfigName) { -- auto gpuService = accelService->getParentEntry(gIOServicePlane); -- -- if (gpuService) { -- auto model = OSDynamicCast(OSData, gpuService->getProperty("model")); -- if (model) { -- auto modelStr = static_cast(model->getBytesNoCopy()); -- if (modelStr) { -- if (modelStr[0] == 'A' && ((modelStr[1] == 'M' && modelStr[2] == 'D') || -- (modelStr[1] == 'T' && modelStr[2] == 'I')) && modelStr[3] == ' ') { -- modelStr += 4; -- } -- -- DBGLOG("rad", "updateAccelConfig found gpu model %s", modelStr); -- *accelConfig = modelStr; -- } else { -- DBGLOG("rad", "updateAccelConfig found null gpu model"); -- } -- } else { -- DBGLOG("rad", "updateAccelConfig failed to find gpu model"); -- } -- -- } else { -- DBGLOG("rad", "updateAccelConfig failed to find accelerator parent"); -- } -- } -+IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t * value) { -+ IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute)(framebuffer, connectIndex, attribute, value); -+ if (attribute == (UInt32)'bklt') { -+ // enable the backlight feature of AMD navi10 driver -+ *value = callbackRAD->curPwmBacklightLvl; -+ ret = 0; -+ } -+ return ret; -+} - -- if (enableGvaSupport && hwIndex == IndexRadeonHardwareX4000) { -- setGvaProperties(accelService); -- } -- } -+static mach_vm_address_t Orig_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts; -+static int64_t Wrap_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts(__int64 a1, __int64 a2) { -+ SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdInterruptManager_sleepInterrupts"" start " "%lu:%lu", a1, a2); -+ uint64_t currNs = getCurrentTimeNs(); -+ if (currNs - g_last_sleep_time >= 1000000000) { -+ g_is_sleep = true; -+ g_last_sleep_time = getCurrentTimeNs(); -+ } -+ int64_t ret = FunctionCast(Wrap_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts, Orig_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts)(a1, a2); -+ SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdInterruptManager_sleepInterrupts"" end - " "%lu", ret); -+ return ret; - } - --bool RAD::wrapSetProperty(IORegistryEntry *that, const char *aKey, void *bytes, unsigned length) { -- if (length > 10 && aKey && reinterpret_cast(aKey)[0] == 'edom' && reinterpret_cast(aKey)[2] == 'l') { -- DBGLOG("rad", "SetProperty caught model %u (%.*s)", length, length, static_cast(bytes)); -- if (*static_cast(bytes) == ' DMA' || *static_cast(bytes) == ' ITA' || *static_cast(bytes) == 'edaR') { -- if (FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey)) { -- DBGLOG("rad", "SetProperty ignored setting %s to %s", aKey, static_cast(bytes)); -- return true; -- } -- DBGLOG("rad", "SetProperty missing %s, fallback to %s", aKey, static_cast(bytes)); -- } -- } -+static mach_vm_address_t Orig_AMDRadeonX6000_AmdLogger_setDebugLevel; -+static int64_t Wrap_AMDRadeonX6000_AmdLogger_setDebugLevel(__int64 a1, int a2) { -+ SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdLogger_setDebugLevel"" start " "%lu:%d", a1, a2); -+ a2 = 9999; -+ int64_t ret = FunctionCast(Wrap_AMDRadeonX6000_AmdLogger_setDebugLevel, Orig_AMDRadeonX6000_AmdLogger_setDebugLevel)(a1, a2); -+ SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdLogger_setDebugLevel"" end - " "%lu", ret); -+ return ret; -+} - -- return FunctionCast(wrapSetProperty, callbackRAD->orgSetProperty)(that, aKey, bytes, length); -+static mach_vm_address_t Orig_my_dm_logger_write; -+static char g_buffer[81920] = {0}; -+static int64_t Wrap_my_dm_logger_write(int64_t a1, unsigned int a2, const char *a3, ...) { -+ va_list args; -+ va_start(args, a3); -+ vsnprintf(g_buffer, 8192, a3, args); -+ SYSLOG("igfx", "dm log, type: %d, msg: %s", a2, g_buffer); -+ va_end(args); -+ return 0; - } - --OSObject *RAD::wrapGetProperty(IORegistryEntry *that, const char *aKey) { -- auto obj = FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey); -- auto props = OSDynamicCast(OSDictionary, obj); -- -- if (props && aKey) { -- const char *prefix {nullptr}; -- auto provider = OSDynamicCast(IOService, that->getParentEntry(gIOServicePlane)); -- if (provider) { -- if (aKey[0] == 'a') { -- if (!strcmp(aKey, "aty_config")) -- prefix = "CFG,"; -- else if (!strcmp(aKey, "aty_properties")) -- prefix = "PP,"; -- } else if (aKey[0] == 'c' && !strcmp(aKey, "cail_properties")) { -- prefix = "CAIL,"; -- } -+bool RAD::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { -+ if (kextRadeonX6000Framebuffer.loadIndex == index) { -+ SYSLOG("igfx", "RAD::processKext, index: %lu, address: %p, size: %lu", index, address, size); -+ if (getKernelVersion() >= KernelVersion::Monterey) { -+ KernelPatcher::RouteRequest requests[] = { -+ {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25setAttributeForConnectionEijm", wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute}, -+ {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25getAttributeForConnectionEijPm", wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute}, -+ //{"__ZN24AMDRadeonX6000_AmdLogger13setDebugLevelE11LogSeverity", Wrap_AMDRadeonX6000_AmdLogger_setDebugLevel, Orig_AMDRadeonX6000_AmdLogger_setDebugLevel}, -+ //{"__ZN34AMDRadeonX6000_AmdInterruptManager15sleepInterruptsEv", Wrap_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts, Orig_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts}, -+ }; -+ -+ if (!patcher.routeMultiple(index, requests, address, size, true, true)) -+ SYSLOG("igfx", "Failed to route redeon x6000 gpu tracing."); -+ -+ SYSLOG("igfx", "route _dce_panel_cntl_hw_init"); -+ mach_vm_address_t dpchi_cal = address + 0x12CB94; // For Monterey -+ if (getKernelVersion() >= KernelVersion::Ventura) { -+ dpchi_cal = address + 0x12EC5F; // For Ventura -+ } -+ -+ // Verify the expect code of the destinate address, otherwise will not call to prevent kernel panic -+ const uint8_t dpchi_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x50, 0x49, 0x89, 0xFD, 0x4C, 0x8D, 0x45 }; -+ unsigned char* dpchi_cal_charp = (unsigned char*)dpchi_cal; -+ bool expect_match = true; -+ for (int i = 0; i < 20; i++) { -+ //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); -+ if (dpchi_cal_charp[i] != dpchi_expect[i]) { -+ SYSLOG("igfx", "_dce_panel_cntl_hw_init dismatch: %d => %x != %x", i, dpchi_cal_charp[i], dpchi_expect[i]); -+ expect_match = false; -+ break; -+ } -+ } -+ -+ if (expect_match) { -+ orgDcePanelCntlHwInit = patcher.routeFunction(dpchi_cal, reinterpret_cast(wrapDcePanelCntlHwInit), true); -+ if (patcher.getError() == KernelPatcher::Error::NoError) { -+ DBGLOG("igfx", "routed _dce_panel_cntl_hw_init"); -+ } else { -+ SYSLOG("igfx", "failed to route _dce_panel_cntl_hw_init %d", patcher.getError()); -+ patcher.clearError(); -+ } -+ -+ mach_vm_address_t ddsb_cal = address + 0x12CFC9; -+ if (getKernelVersion() >= KernelVersion::Ventura) { -+ ddsb_cal = address + 0x12F094; -+ } -+ SYSLOG("igfx", "got Monterey _dce_driver_set_backlight address: %p", ddsb_cal); -+ const uint8_t ddsb_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x50, 0x41, 0x89, 0xF7, 0x49, 0x89, 0xFE }; -+ unsigned char* ddsb_cal_charp = (unsigned char*)ddsb_cal; -+ expect_match = true; -+ for (int i = 0; i < 20; i++) { -+ //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); -+ if (ddsb_cal_charp[i] != ddsb_expect[i]) { -+ SYSLOG("igfx", "_dce_driver_set_backlight dismatch: %d => %x != %x", i, ddsb_cal_charp[i], ddsb_expect[i]); -+ expect_match = false; -+ break; -+ } -+ } -+ -+ //for (int i = 0; i < 0x200000; i++) { -+ // ddsb_cal_charp = (unsigned char*)(address + i); -+ // int j = 0; -+ // for (j = 0; j < 20; j++) { -+ // //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); -+ // if (ddsb_cal_charp[j] != ddsb_expect[j]) { -+ // //SYSLOG("igfx", "_dce_panel_cntl_hw_init dismatch: %d => %x != %x", i, dpchi_cal_charp[i], dpchi_expect[i]); -+ // //expect_match = false; -+ // break; -+ // } -+ // } -+ // if (j == 20) { -+ // SYSLOG("igfx", "found address %u", i); -+ // } -+ //} -+ -+ if (expect_match) { -+ orgDceDriverSetBacklight = reinterpret_cast(ddsb_cal); -+ } -+ } -+ -+ /*SYSLOG("igfx", "route _dm_logger_write"); -+ mach_vm_address_t dlw_cal = address + 0x169117; -+ const uint8_t dlw_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x48, 0x81, 0xEC, 0x88, 0x04, 0x00, 0x00 }; -+ unsigned char* dlw_cal_charp = (unsigned char*)dlw_cal; -+ expect_match = true; -+ for (int i = 0; i < 20; i++) { -+ //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); -+ if (dlw_cal_charp[i] != dlw_expect[i]) { -+ SYSLOG("igfx", "_dm_logger_write dismatch: %d => %x != %x", i, dlw_cal_charp[i], dlw_expect[i]); -+ expect_match = false; -+ break; -+ } -+ } -+ if (expect_match) { -+ Orig_my_dm_logger_write = patcher.routeFunction(dlw_cal, reinterpret_cast(Wrap_my_dm_logger_write), true); -+ if (patcher.getError() == KernelPatcher::Error::NoError) { -+ DBGLOG("igfx", "routed _dm_logger_write"); -+ } else { -+ SYSLOG("igfx", "failed to route _dm_logger_write %d", patcher.getError()); -+ patcher.clearError(); -+ } -+ }*/ -+ -+ /* -+ SYSLOG("igfx", "route _dce110_edp_power_control"); -+ mach_vm_address_t depc_cal = address + 0x1B04B9; -+ const uint8_t depc_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x48, 0x83, 0xEC, 0x48, 0x4C, 0x8B, 0xB7 }; -+ unsigned char* depc_cal_charp = (unsigned char*)depc_cal; -+ expect_match = true; -+ for (int i = 0; i < 20; i++) { -+ //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); -+ if (depc_cal_charp[i] != depc_expect[i]) { -+ SYSLOG("igfx", "_dce110_edp_power_control dismatch: %d => %x != %x", i, depc_cal_charp[i], depc_expect[i]); -+ expect_match = false; -+ break; -+ } -+ } -+ if (expect_match) { -+ orig_dce110_edp_power_control = patcher.routeFunction(depc_cal, reinterpret_cast(wrap_dce110_edp_power_control), true); -+ if (patcher.getError() == KernelPatcher::Error::NoError) { -+ DBGLOG("igfx", "routed _dce110_edp_power_control"); -+ } else { -+ SYSLOG("igfx", "failed to route _dce110_edp_power_control %d", patcher.getError()); -+ patcher.clearError(); -+ } -+ } -+ -+ SYSLOG("igfx", "route _dce110_edp_backlight_control"); -+ mach_vm_address_t debc_cal = address + 0x1B0926; -+ const uint8_t debc_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x48, 0x83, 0xEC, 0x48, 0x4C, 0x8B, 0xAF }; -+ unsigned char* debc_cal_charp = (unsigned char*)debc_cal; -+ expect_match = true; -+ for (int i = 0; i < 20; i++) { -+ //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); -+ if (debc_cal_charp[i] != debc_expect[i]) { -+ SYSLOG("igfx", "_dce110_edp_backlight_control dismatch: %d => %x != %x", i, debc_cal_charp[i], debc_expect[i]); -+ expect_match = false; -+ break; -+ } -+ } -+ if (expect_match) { -+ orig_dce110_edp_backlight_control = patcher.routeFunction(debc_cal, reinterpret_cast(wrap_dce110_edp_backlight_control), true); -+ if (patcher.getError() == KernelPatcher::Error::NoError) { -+ DBGLOG("igfx", "routed _dce110_edp_backlight_control"); -+ } else { -+ SYSLOG("igfx", "failed to route _dce110_edp_backlight_control %d", patcher.getError()); -+ patcher.clearError(); -+ } -+ }*/ -+ } else { -+ KernelPatcher::RouteRequest requests[] = { -+ {"_dce_panel_cntl_hw_init", wrapDcePanelCntlHwInit, orgDcePanelCntlHwInit}, -+ {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25setAttributeForConnectionEijm", wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute}, -+ {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25getAttributeForConnectionEijPm", wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute}, -+ //{"_dc_link_bandwidth_kbps", wrapDcLinkBandwidthKbps, orgDcLinkBandwidthKbps}, -+ }; -+ -+ mach_vm_address_t dpchi_cal = address + 0x124F56; -+ unsigned char* dpchi_cal_charp = (unsigned char*)dpchi_cal; -+ for (int i = 0; i < 20; i++) { -+ SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); -+ } -+ mach_vm_address_t ddsb_cal = address + 0x124B21; -+ unsigned char* ddsb_cal_charp = (unsigned char*)ddsb_cal; -+ for (int i = 0; i < 20; i++) { -+ SYSLOG("igfx", "%d => %x", i, ddsb_cal_charp[i]); -+ } -+ -+ if (!patcher.routeMultiple(index, requests, address, size, true, true)) -+ SYSLOG("igfx", "Failed to route redeon x6000 gpu tracing."); -+ -+ mach_vm_address_t ddsb = patcher.solveSymbol(index, "_dce_driver_set_backlight"); -+ SYSLOG("igfx", "got Bigsur _dce_driver_set_backlight address: %p", ddsb); -+ orgDceDriverSetBacklight = reinterpret_cast(ddsb); -+ } -+ //mach_vm_address_t ddsb_cal = address + 0x124F56; -+ //SYSLOG("igfx", "got _dce_driver_set_backlight address: %p, %p", ddsb, ddsb_cal); -+ //orgDceDriverSetBacklight = reinterpret_cast(ddsb); -+ if (patcher.getError() != KernelPatcher::Error::NoError) { -+ SYSLOG("igfx", "failed to resolve _dce_driver_set_backlight"); -+ patcher.clearError(); -+ return false; -+ } -+ } -+ -+ if (kextRadeonFramebuffer.loadIndex == index) { -+ if (force24BppMode) -+ process24BitOutput(patcher, kextRadeonFramebuffer, address, size); -+ return true; -+ } -+ -+ if (kextRadeonLegacyFramebuffer.loadIndex == index) { -+ if (force24BppMode) -+ process24BitOutput(patcher, kextRadeonLegacyFramebuffer, address, size); -+ return true; -+ } -+ -+ if (kextRadeonSupport.loadIndex == index) { -+ processConnectorOverrides(patcher, address, size, true); -+ -+ if (getKernelVersion() > KernelVersion::Mojave || -+ (getKernelVersion() == KernelVersion::Mojave && getKernelMinorVersion() >= 5)) { -+ KernelPatcher::RouteRequest request("__ZN13ATIController8TestVRAME13PCI_REG_INDEXb", doNotTestVram); -+ patcher.routeMultiple(index, &request, 1, address, size); -+ } -+ -+ if (useCustomAgdpDecision) { -+ KernelPatcher::RouteRequest request("__ZN16AtiDeviceControl16notifyLinkChangeE31kAGDCRegisterLinkControlEvent_tmj", wrapNotifyLinkChange, orgNotifyLinkChange); -+ patcher.routeMultiple(index, &request, 1, address, size); -+ } -+ -+ return true; -+ } -+ -+ if (kextRadeonLegacySupport.loadIndex == index) { -+ processConnectorOverrides(patcher, address, size, false); -+ return true; -+ } -+ -+ if (kextPolarisController.loadIndex == index) { -+ KernelPatcher::RouteRequest request("__ZN17AMD9500Controller23findProjectByPartNumberEP20ControllerProperties", findProjectByPartNumber); -+ patcher.routeMultiple(index, &request, 1, address, size); -+ } -+ -+ for (size_t i = 0; i < maxHardwareKexts; i++) { -+ if (kextRadeonHardware[i].loadIndex == index) { -+ processHardwareKext(patcher, i, address, size); -+ return true; -+ } -+ } -+ -+ return false; -+} - -- if (prefix) { -- DBGLOG("rad", "GetProperty discovered property merge request for %s", aKey); -- auto rawProps = props->copyCollection(); -- if (rawProps) { -- auto newProps = OSDynamicCast(OSDictionary, rawProps); -- if (newProps) { -- callbackRAD->mergeProperties(newProps, prefix, provider); -- that->setProperty(aKey, newProps); -- obj = newProps; -- } -- rawProps->release(); -- } -+void RAD::initHardwareKextMods() { -+ // Decide on kext amount present for optimal performance. -+ // 10.15+ has X4000, X5000, and X6000 -+ // 10.14+ has X4000 and X5000 -+ // 10.13.4+ has X3000, X4000, and X5000 -+ if (getKernelVersion() >= KernelVersion::Catalina) -+ maxHardwareKexts = MaxRadeonHardwareCatalina; -+ else if (getKernelVersion() >= KernelVersion::Mojave) -+ maxHardwareKexts = MaxRadeonHardwareMojave; -+ else if (getKernelVersion() == KernelVersion::HighSierra && getKernelMinorVersion() >= 5) -+ maxHardwareKexts = MaxRadeonHardwareModernHighSierra; -+ -+ // 10.13.4 fixed black screen issues -+ if (maxHardwareKexts != MaxRadeonHardware) { -+ for (size_t i = 0; i < MaxGetFrameBufferProcs; i++) -+ getFrameBufferProcNames[IndexRadeonHardwareX4000][i] = nullptr; -+ -+ // We have nothing to do for these kexts on recent systems -+ if (!fixConfigName && !forceOpenGL && !forceCodecInfo) { -+ // X4000 kext is not included in this list as we need to fix GVA properties for most of its GPUs -+ kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); -+ kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); -+ } -+ } -+ -+ if (getKernelVersion() < KernelVersion::Catalina) { -+ kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); -+ } -+ -+ if (getKernelVersion() < KernelVersion::HighSierra) { -+ // Versions before 10.13 do not support X4250 and X5000 -+ kextRadeonHardware[IndexRadeonHardwareX4250].switchOff(); -+ kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); -+ -+ // Versions before 10.13 have legacy X3000 and X4000 IDs -+ kextRadeonHardware[IndexRadeonHardwareX3000].id = idRadeonX3000Old; -+ kextRadeonHardware[IndexRadeonHardwareX4000].id = idRadeonX4000Old; -+ -+ bool preSierra = getKernelVersion() < KernelVersion::Sierra; -+ -+ if (preSierra) { -+ // Versions before 10.12 do not support X4100 -+ kextRadeonHardware[IndexRadeonHardwareX4100].switchOff(); -+ } -+ -+ if (preSierra || (getKernelVersion() == KernelVersion::Sierra && getKernelMinorVersion() < 7)) { -+ // Versions before 10.12.6 do not support X4150, X4200 -+ kextRadeonHardware[IndexRadeonHardwareX4150].switchOff(); -+ kextRadeonHardware[IndexRadeonHardwareX4200].switchOff(); -+ } -+ } -+ -+ lilu.onKextLoadForce(kextRadeonHardware, maxHardwareKexts); -+} - -- } -- } -- } -+void RAD::process24BitOutput(KernelPatcher &patcher, KernelPatcher::KextInfo &info, mach_vm_address_t address, size_t size) { -+ auto bitsPerComponent = patcher.solveSymbol(info.loadIndex, "__ZL18BITS_PER_COMPONENT", address, size); -+ if (bitsPerComponent) { -+ while (bitsPerComponent && *bitsPerComponent) { -+ if (*bitsPerComponent == 10) { -+ auto ret = MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock); -+ if (ret == KERN_SUCCESS) { -+ DBGLOG("rad", "fixing BITS_PER_COMPONENT"); -+ *bitsPerComponent = 8; -+ MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock); -+ } else { -+ SYSLOG("rad", "failed to disable write protection for BITS_PER_COMPONENT"); -+ } -+ } -+ bitsPerComponent++; -+ } -+ } else { -+ SYSLOG("rad", "failed to find BITS_PER_COMPONENT"); -+ patcher.clearError(); -+ } -+ -+ DBGLOG("rad", "fixing pixel types"); -+ -+ KernelPatcher::LookupPatch pixelPatch { -+ &info, -+ reinterpret_cast("--RRRRRRRRRRGGGGGGGGGGBBBBBBBBBB"), -+ reinterpret_cast("--------RRRRRRRRGGGGGGGGBBBBBBBB"), -+ 32, 2 -+ }; -+ -+ patcher.applyLookupPatch(&pixelPatch); -+ if (patcher.getError() != KernelPatcher::Error::NoError) { -+ SYSLOG("rad", "failed to patch RGB mask for 24-bit output"); -+ patcher.clearError(); -+ } -+} - -- return obj; -+void RAD::processConnectorOverrides(KernelPatcher &patcher, mach_vm_address_t address, size_t size, bool modern) { -+ if (modern) { -+ if (getKernelVersion() >= KernelVersion::HighSierra) { -+ KernelPatcher::RouteRequest requests[] { -+ KernelPatcher::RouteRequest("__ZN14AtiBiosParser116getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), -+ KernelPatcher::RouteRequest("__ZN14AtiBiosParser216getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV2, orgGetConnectorsInfoV2), -+ KernelPatcher::RouteRequest("__ZN14AtiBiosParser126translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V117AtomConnectorInfoER13ConnectorInfo", -+ wrapTranslateAtomConnectorInfoV1, orgTranslateAtomConnectorInfoV1), -+ KernelPatcher::RouteRequest("__ZN14AtiBiosParser226translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V217AtomConnectorInfoER13ConnectorInfo", -+ wrapTranslateAtomConnectorInfoV2, orgTranslateAtomConnectorInfoV2), -+ KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart) -+ }; -+ patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); -+ } else { -+ KernelPatcher::RouteRequest requests[] { -+ KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), -+ KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart), -+ }; -+ patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); -+ -+ orgGetAtomObjectTableForType = reinterpret_cast(patcher.solveSymbol(kextRadeonSupport.loadIndex, -+ "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size)); -+ if (!orgGetAtomObjectTableForType) { -+ SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); -+ patcher.clearError(); -+ } -+ } -+ } else { -+ KernelPatcher::RouteRequest requests[] { -+ KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapLegacyGetConnectorsInfo, orgLegacyGetConnectorsInfo), -+ KernelPatcher::RouteRequest("__ZN19AMDLegacyController5startEP9IOService", wrapLegacyATIControllerStart, orgLegacyATIControllerStart), -+ }; -+ patcher.routeMultiple(kextRadeonLegacySupport.loadIndex, requests, address, size); -+ -+ orgLegacyGetAtomObjectTableForType = patcher.solveSymbol(kextRadeonLegacySupport.loadIndex, -+ "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size); -+ if (!orgLegacyGetAtomObjectTableForType) { -+ SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); -+ patcher.clearError(); -+ } -+ } - } - --uint32_t RAD::wrapGetConnectorsInfoV1(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { -- uint32_t code = FunctionCast(wrapGetConnectorsInfoV1, callbackRAD->orgGetConnectorsInfoV1)(that, connectors, sz); -- auto props = callbackRAD->currentPropProvider.get(); -- -- if (code == 0 && sz && props && *props) { -- if (getKernelVersion() >= KernelVersion::HighSierra) -- callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); -- else -- callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgGetAtomObjectTableForType, *props, connectors, sz); -- } else { -- DBGLOG("rad", "getConnectorsInfoV1 failed %X or undefined %d", code, props == nullptr); -- } -+void RAD::processHardwareKext(KernelPatcher &patcher, size_t hwIndex, mach_vm_address_t address, size_t size) { -+ auto getFrame = getFrameBufferProcNames[hwIndex]; -+ auto &hardware = kextRadeonHardware[hwIndex]; -+ -+ // Fix boot and wake to black screen -+ for (size_t j = 0; j < MaxGetFrameBufferProcs && getFrame[j] != nullptr; j++) { -+ auto getFB = patcher.solveSymbol(hardware.loadIndex, getFrame[j], address, size); -+ if (getFB) { -+ // Initially it was discovered that the only problematic register is PRIMARY_SURFACE_ADDRESS_HIGH (0x1A07). -+ // This register must be nulled to solve most of the issues. -+ // Depending on the amount of connected screens PRIMARY_SURFACE_ADDRESS (0x1A04) may not be null. -+ // However, as of AMD Vega drivers in 10.13 DP1 both of these registers are now ignored. -+ // Furthermore, there are no (extra) issues from just returning 0 in framebuffer base address. -+ -+ // xor rax, rax -+ // ret -+ uint8_t ret[] {0x48, 0x31, 0xC0, 0xC3}; -+ patcher.routeBlock(getFB, ret, sizeof(ret)); -+ if (patcher.getError() == KernelPatcher::Error::NoError) { -+ DBGLOG("rad", "patched %s", getFrame[j]); -+ } else { -+ SYSLOG("rad", "failed to patch %s code %d", getFrame[j], patcher.getError()); -+ patcher.clearError(); -+ } -+ } else { -+ SYSLOG("rad", "failed to find %s code %d", getFrame[j], patcher.getError()); -+ patcher.clearError(); -+ } -+ } -+ -+ // Fix reported Accelerator name to support WhateverName.app -+ // Also fix GVA properties for X4000. -+ if (fixConfigName || hwIndex == IndexRadeonHardwareX4000) { -+ KernelPatcher::RouteRequest request(populateAccelConfigProcNames[hwIndex], wrapPopulateAccelConfig[hwIndex], orgPopulateAccelConfig[hwIndex]); -+ patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); -+ } -+ -+ // Enforce OpenGL support if requested -+ if (forceOpenGL) { -+ DBGLOG("rad", "disabling Metal support"); -+ uint8_t find1[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; -+ uint8_t find2[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; -+ uint8_t repl1[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; -+ uint8_t repl2[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; -+ -+ KernelPatcher::LookupPatch antimetal[] { -+ {&hardware, find1, repl1, sizeof(find1), 2}, -+ {&hardware, find2, repl2, sizeof(find1), 2} -+ }; -+ -+ for (auto &p : antimetal) { -+ patcher.applyLookupPatch(&p); -+ patcher.clearError(); -+ } -+ } -+ -+ // Patch AppleGVA support for non-supported models -+ if (forceCodecInfo && getHWInfoProcNames[hwIndex] != nullptr) { -+ KernelPatcher::RouteRequest request(getHWInfoProcNames[hwIndex], wrapGetHWInfo[hwIndex], orgGetHWInfo[hwIndex]); -+ patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); -+ } -+} - -- return code; -+void RAD::mergeProperty(OSDictionary *props, const char *name, OSObject *value) { -+ // The only type we could make from device properties is data. -+ // To be able to override other types we do a conversion here. -+ auto data = OSDynamicCast(OSData, value); -+ if (data) { -+ // It is hard to make a boolean even from ACPI, so we make a hack here: -+ // 1-byte OSData with 0x01 / 0x00 values becomes boolean. -+ auto val = static_cast(data->getBytesNoCopy()); -+ auto len = data->getLength(); -+ if (val && len == sizeof(uint8_t)) { -+ if (val[0] == 1) { -+ props->setObject(name, kOSBooleanTrue); -+ DBGLOG("rad", "prop %s was merged as kOSBooleanTrue", name); -+ return; -+ } else if (val[0] == 0) { -+ props->setObject(name, kOSBooleanFalse); -+ DBGLOG("rad", "prop %s was merged as kOSBooleanFalse", name); -+ return; -+ } -+ } -+ -+ // Consult the original value to make a decision -+ auto orgValue = props->getObject(name); -+ if (val && orgValue) { -+ DBGLOG("rad", "prop %s has original value", name); -+ if (len == sizeof(uint32_t) && OSDynamicCast(OSNumber, orgValue)) { -+ auto num = *reinterpret_cast(val); -+ auto osnum = OSNumber::withNumber(num, 32); -+ if (osnum) { -+ DBGLOG("rad", "prop %s was merged as number %u", name, num); -+ props->setObject(name, osnum); -+ osnum->release(); -+ } -+ return; -+ } else if (len > 0 && val[len-1] == '\0' && OSDynamicCast(OSString, orgValue)) { -+ auto str = reinterpret_cast(val); -+ auto osstr = OSString::withCString(str); -+ if (osstr) { -+ DBGLOG("rad", "prop %s was merged as string %s", name, str); -+ props->setObject(name, osstr); -+ osstr->release(); -+ } -+ return; -+ } -+ } else { -+ DBGLOG("rad", "prop %s has no original value", name); -+ } -+ } -+ -+ // Default merge as is -+ props->setObject(name, value); -+ DBGLOG("rad", "prop %s was merged", name); - } - --uint32_t RAD::wrapGetConnectorsInfoV2(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { -- uint32_t code = FunctionCast(wrapGetConnectorsInfoV2, callbackRAD->orgGetConnectorsInfoV2)(that, connectors, sz); -- auto props = callbackRAD->currentPropProvider.get(); -+void RAD::mergeProperties(OSDictionary *props, const char *prefix, IOService *provider) { -+ // Should be ok, but in case there are issues switch to dictionaryWithProperties(); -+ auto dict = provider->getPropertyTable(); -+ if (dict) { -+ auto iterator = OSCollectionIterator::withCollection(dict); -+ if (iterator) { -+ OSSymbol *propname; -+ size_t prefixlen = strlen(prefix); -+ while ((propname = OSDynamicCast(OSSymbol, iterator->getNextObject())) != nullptr) { -+ auto name = propname->getCStringNoCopy(); -+ if (name && propname->getLength() > prefixlen && !strncmp(name, prefix, prefixlen)) { -+ auto prop = dict->getObject(propname); -+ if (prop) -+ mergeProperty(props, name + prefixlen, prop); -+ else -+ DBGLOG("rad", "prop %s was not merged due to no value", name); -+ } else { -+ //DBGLOG("rad", "prop %s does not match %s prefix", safeString(name), prefix); -+ } -+ } -+ -+ iterator->release(); -+ } else { -+ SYSLOG("rad", "prop merge failed to iterate over properties"); -+ } -+ } else { -+ SYSLOG("rad", "prop merge failed to get properties"); -+ } -+ -+ if (!strcmp(prefix, "CAIL,")) { -+ for (size_t i = 0; i < arrsize(powerGatingFlags); i++) { -+ if (powerGatingFlags[i] && props->getObject(powerGatingFlags[i])) { -+ DBGLOG("rad", "cail prop merge found %s, replacing", powerGatingFlags[i]); -+ auto num = OSNumber::withNumber(1, 32); -+ if (num) { -+ props->setObject(powerGatingFlags[i], num); -+ num->release(); -+ } -+ } -+ } -+ } -+} - -- if (code == 0 && sz && props && *props) -- callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); -- else -- DBGLOG("rad", "getConnectorsInfoV2 failed %X or undefined %d", code, props == nullptr); -+void RAD::applyPropertyFixes(IOService *service, uint32_t connectorNum) { -+ if (service && getKernelVersion() >= KernelVersion::HighSierra) { -+ // Starting with 10.13.2 this is important to fix sleep issues due to enforced 6 screens -+ if (!service->getProperty("CFG,CFG_FB_LIMIT")) { -+ DBGLOG("rad", "setting fb limit to %u", connectorNum); -+ service->setProperty("CFG_FB_LIMIT", connectorNum, 32); -+ } -+ -+ // In the past we set CFG_USE_AGDC to false, which caused visual glitches and broken multimonitor support. -+ // A better workaround is to disable AGDP just like we do globally. -+ } -+} - -- return code; -+void RAD::updateConnectorsInfo(void *atomutils, t_getAtomObjectTableForType gettable, IOService *ctrl, RADConnectors::Connector *connectors, uint8_t *sz) { -+ if (atomutils) { -+ DBGLOG("rad", "getConnectorsInfo found %u connectors", *sz); -+ RADConnectors::print(connectors, *sz); -+ } -+ -+ // Check if the user wants to override automatically detected connectors -+ auto cons = ctrl->getProperty("connectors"); -+ if (cons) { -+ auto consData = OSDynamicCast(OSData, cons); -+ if (consData) { -+ auto consPtr = consData->getBytesNoCopy(); -+ auto consSize = consData->getLength(); -+ -+ uint32_t consCount; -+ if (WIOKit::getOSDataValue(ctrl, "connector-count", consCount)) { -+ *sz = consCount; -+ DBGLOG("rad", "getConnectorsInfo got size override to %u", *sz); -+ } -+ -+ if (consPtr && consSize > 0 && *sz > 0 && RADConnectors::valid(consSize, *sz)) { -+ RADConnectors::copy(connectors, *sz, static_cast(consPtr), consSize); -+ DBGLOG("rad", "getConnectorsInfo installed %u connectors", *sz); -+ applyPropertyFixes(ctrl, *sz); -+ } else { -+ DBGLOG("rad", "getConnectorsInfo conoverrides have invalid size %u for %u num", consSize, *sz); -+ } -+ } else { -+ DBGLOG("rad", "getConnectorsInfo conoverrides have invalid type"); -+ } -+ } else { -+ if (atomutils) { -+ DBGLOG("rad", "getConnectorsInfo attempting to autofix connectors"); -+ uint8_t sHeader = 0, displayPathNum = 0, connectorObjectNum = 0; -+ auto baseAddr = static_cast(gettable(atomutils, AtomObjectTableType::Common, &sHeader)) - sizeof(uint32_t); -+ auto displayPaths = static_cast(gettable(atomutils, AtomObjectTableType::DisplayPath, &displayPathNum)); -+ auto connectorObjects = static_cast(gettable(atomutils, AtomObjectTableType::ConnectorObject, &connectorObjectNum)); -+ if (displayPathNum == connectorObjectNum) -+ autocorrectConnectors(baseAddr, displayPaths, displayPathNum, connectorObjects, connectorObjectNum, connectors, *sz); -+ else -+ DBGLOG("rad", "getConnectorsInfo found different displaypaths %u and connectors %u", displayPathNum, connectorObjectNum); -+ } -+ -+ applyPropertyFixes(ctrl, *sz); -+ -+ // Prioritise connectors, since it may cause black screen on e.g. R9 370 -+ const uint8_t *senseList = nullptr; -+ uint8_t senseNum = 0; -+ auto priData = OSDynamicCast(OSData, ctrl->getProperty("connector-priority")); -+ if (priData) { -+ senseList = static_cast(priData->getBytesNoCopy()); -+ senseNum = static_cast(priData->getLength()); -+ DBGLOG("rad", "getConnectorInfo found %u senses in connector-priority", senseNum); -+ reprioritiseConnectors(senseList, senseNum, connectors, *sz); -+ } else { -+ DBGLOG("rad", "getConnectorInfo leaving unchaged priority"); -+ } -+ } -+ -+ DBGLOG("rad", "getConnectorsInfo resulting %u connectors follow", *sz); -+ RADConnectors::print(connectors, *sz); - } - --uint32_t RAD::wrapLegacyGetConnectorsInfo(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { -- uint32_t code = FunctionCast(wrapLegacyGetConnectorsInfo, callbackRAD->orgLegacyGetConnectorsInfo)(that, connectors, sz); -- auto props = callbackRAD->currentLegacyPropProvider.get(); -+void RAD::autocorrectConnectors(uint8_t *baseAddr, AtomDisplayObjectPath *displayPaths, uint8_t displayPathNum, AtomConnectorObject *connectorObjects, -+ uint8_t connectorObjectNum, RADConnectors::Connector *connectors, uint8_t sz) { -+ for (uint8_t i = 0; i < displayPathNum; i++) { -+ if (!isEncoder(displayPaths[i].usGraphicObjIds)) { -+ DBGLOG("rad", "autocorrectConnectors not encoder %X at %u", displayPaths[i].usGraphicObjIds, i); -+ continue; -+ } -+ -+ uint8_t txmit = 0, enc = 0; -+ if (!getTxEnc(displayPaths[i].usGraphicObjIds, txmit, enc)) -+ continue; -+ -+ uint8_t sense = getSenseID(baseAddr + connectorObjects[i].usRecordOffset); -+ if (!sense) { -+ DBGLOG("rad", "autocorrectConnectors failed to detect sense for %u connector", i); -+ continue; -+ } -+ -+ DBGLOG("rad", "autocorrectConnectors found txmit %02X enc %02X sense %02X for %u connector", txmit, enc, sense, i); -+ -+ autocorrectConnector(getConnectorID(displayPaths[i].usConnObjectId), sense, txmit, enc, connectors, sz); -+ } -+} - -- if (code == 0 && sz && props && *props) -- callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgLegacyGetAtomObjectTableForType, *props, connectors, sz); -- else -- DBGLOG("rad", "legacy getConnectorsInfo failed %X or undefined %d", code, props == nullptr); -+void RAD::autocorrectConnector(uint8_t connector, uint8_t sense, uint8_t txmit, uint8_t enc, RADConnectors::Connector *connectors, uint8_t sz) { -+ // This function attempts to fix the following issues: -+ // -+ // 1. Incompatible DVI transmitter on 290X, 370 and probably some other models -+ // In this case a correct transmitter is detected by AtiAtomBiosDce60::getPropertiesForEncoderObject, however, later -+ // in AtiAtomBiosDce60::getPropertiesForConnectorObject for DVI DL and TITFP513 this value is conjuncted with 0xCF, -+ // which makes it wrong: 0x10 -> 0, 0x11 -> 1. As a result one gets black screen when connecting multiple displays. -+ // getPropertiesForEncoderObject takes usGraphicObjIds and getPropertiesForConnectorObject takes usConnObjectId -+ -+ if (callbackRAD->dviSingleLink) { -+ if (connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I && -+ connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D && -+ connector != CONNECTOR_OBJECT_ID_LVDS) { -+ DBGLOG("rad", "autocorrectConnector found unsupported connector type %02X", connector); -+ return; -+ } -+ -+ auto fixTransmit = [](auto &con, uint8_t idx, uint8_t sense, uint8_t txmit) { -+ if (con.sense == sense) { -+ if (con.transmitter != txmit && (con.transmitter & 0xCF) == con.transmitter) { -+ DBGLOG("rad", "autocorrectConnector replacing txmit %02X with %02X for %u connector sense %02X", -+ con.transmitter, txmit, idx, sense); -+ con.transmitter = txmit; -+ } -+ return true; -+ } -+ return false; -+ }; -+ -+ bool isModern = RADConnectors::modern(); -+ for (uint8_t j = 0; j < sz; j++) { -+ if (isModern) { -+ auto &con = (&connectors->modern)[j]; -+ if (fixTransmit(con, j, sense, txmit)) -+ break; -+ } else { -+ auto &con = (&connectors->legacy)[j]; -+ if (fixTransmit(con, j, sense, txmit)) -+ break; -+ } -+ } -+ } else { -+ DBGLOG("rad", "autocorrectConnector use -raddvi to enable dvi autocorrection"); -+ } -+} - -- return code; -+void RAD::reprioritiseConnectors(const uint8_t *senseList, uint8_t senseNum, RADConnectors::Connector *connectors, uint8_t sz) { -+ static constexpr uint32_t typeList[] { -+ RADConnectors::ConnectorLVDS, -+ RADConnectors::ConnectorDigitalDVI, -+ RADConnectors::ConnectorHDMI, -+ RADConnectors::ConnectorDP, -+ RADConnectors::ConnectorVGA -+ }; -+ static constexpr uint8_t typeNum {static_cast(arrsize(typeList))}; -+ -+ bool isModern = RADConnectors::modern(); -+ uint16_t priCount = 1; -+ // Automatically detected connectors have equal priority (0), which often results in black screen -+ // This allows to change this firstly by user-defined list, then by type list. -+ //TODO: priority is ignored for 5xxx and 6xxx GPUs, should we manually reorder items? -+ for (uint8_t i = 0; i < senseNum + typeNum + 1; i++) { -+ for (uint8_t j = 0; j < sz; j++) { -+ auto reorder = [&](auto &con) { -+ if (i == senseNum + typeNum) { -+ if (con.priority == 0) -+ con.priority = priCount++; -+ } else if (i < senseNum) { -+ if (con.sense == senseList[i]) { -+ DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by sense", con.sense, priCount); -+ con.priority = priCount++; -+ return true; -+ } -+ } else { -+ if (con.priority == 0 && con.type == typeList[i-senseNum]) { -+ DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by type", con.sense, priCount); -+ con.priority = priCount++; -+ } -+ } -+ return false; -+ }; -+ -+ if ((isModern && reorder((&connectors->modern)[j])) || -+ (!isModern && reorder((&connectors->legacy)[j]))) -+ break; -+ } -+ } - } - --uint32_t RAD::wrapTranslateAtomConnectorInfoV1(void *that, RADConnectors::AtomConnectorInfo *info, RADConnectors::Connector *connector) { -- uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV1, callbackRAD->orgTranslateAtomConnectorInfoV1)(that, info, connector); -- -- if (code == 0 && info && connector) { -- RADConnectors::print(connector, 1); -- -- uint8_t sense = getSenseID(info->i2cRecord); -- if (sense) { -- DBGLOG("rad", "translateAtomConnectorInfoV1 got sense id %02X", sense); -- -- // We need to extract usGraphicObjIds from info->hpdRecord, which is of type ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT: -- // struct ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT { -- // uint8_t ucNumberOfSrc; -- // uint16_t usSrcObjectID[ucNumberOfSrc]; -- // uint8_t ucNumberOfDst; -- // uint16_t usDstObjectID[ucNumberOfDst]; -- // }; -- // The value we need is in usSrcObjectID. The structure is byte-packed. -- -- uint8_t ucNumberOfSrc = info->hpdRecord[0]; -- for (uint8_t i = 0; i < ucNumberOfSrc; i++) { -- auto usSrcObjectID = *reinterpret_cast(info->hpdRecord + sizeof(uint8_t) + i * sizeof(uint16_t)); -- DBGLOG("rad", "translateAtomConnectorInfoV1 checking %04X object id", usSrcObjectID); -- if (((usSrcObjectID & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT) == GRAPH_OBJECT_TYPE_ENCODER) { -- uint8_t txmit = 0, enc = 0; -- if (getTxEnc(usSrcObjectID, txmit, enc)) -- callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); -- break; -- } -- } -+void RAD::setGvaProperties(IOService *accelService) { -+ auto codecStr = OSDynamicCast(OSString, accelService->getProperty("IOGVACodec")); -+ if (codecStr == nullptr) { -+ DBGLOG("rad", "updating X4000 accelerator IOGVACodec to VCE"); -+ accelService->setProperty("IOGVACodec", "VCE"); -+ } else { -+ auto codec = codecStr->getCStringNoCopy(); -+ DBGLOG("rad", "X4000 accelerator IOGVACodec is already set to %s", safeString(codec)); -+ if (codec != nullptr && strncmp(codec, "AMD", strlen("AMD")) == 0) { -+ bool needsDecode = accelService->getProperty("IOGVAHEVCDecode") == nullptr; -+ bool needsEncode = accelService->getProperty("IOGVAHEVCEncode") == nullptr; -+ if (needsDecode) { -+ OSObject *VTMaxDecodeLevel = OSNumber::withNumber(153, 32); -+ OSString *VTMaxDecodeLevelKey = OSString::withCString("VTMaxDecodeLevel"); -+ OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); -+ OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(3); -+ OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); -+ OSString *VTPerProfileDetailsKey2 = OSString::withCString("2"); -+ OSString *VTPerProfileDetailsKey3 = OSString::withCString("3"); -+ -+ OSArray *VTSupportedProfileArray = OSArray::withCapacity(3); -+ OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); -+ OSNumber *VTSupportedProfileArray2 = OSNumber::withNumber(2, 32); -+ OSNumber *VTSupportedProfileArray3 = OSNumber::withNumber(3, 32); -+ -+ OSDictionary *IOGVAHEVCDecodeCapabilities = OSDictionary::withCapacity(2); -+ OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); -+ OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); -+ -+ if (VTMaxDecodeLevel != nullptr && VTMaxDecodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && -+ VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTPerProfileDetailsKey2 != nullptr && -+ VTPerProfileDetailsKey3 != nullptr && VTSupportedProfileArrayKey != nullptr && VTSupportedProfileArray1 != nullptr && -+ VTSupportedProfileArray2 != nullptr && VTSupportedProfileArray3 != nullptr && VTSupportedProfileArray != nullptr && -+ VTPerProfileDetailsKey != nullptr && IOGVAHEVCDecodeCapabilities != nullptr) { -+ VTPerProfileDetailsInner->setObject(VTMaxDecodeLevelKey, VTMaxDecodeLevel); -+ VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); -+ VTPerProfileDetails->setObject(VTPerProfileDetailsKey2, VTPerProfileDetailsInner); -+ VTPerProfileDetails->setObject(VTPerProfileDetailsKey3, VTPerProfileDetailsInner); -+ -+ VTSupportedProfileArray->setObject(VTSupportedProfileArray1); -+ VTSupportedProfileArray->setObject(VTSupportedProfileArray2); -+ VTSupportedProfileArray->setObject(VTSupportedProfileArray3); -+ -+ IOGVAHEVCDecodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); -+ IOGVAHEVCDecodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); -+ -+ accelService->setProperty("IOGVAHEVCDecode", "1"); -+ accelService->setProperty("IOGVAHEVCDecodeCapabilities", IOGVAHEVCDecodeCapabilities); -+ -+ DBGLOG("rad", "recovering IOGVAHEVCDecode"); -+ } else { -+ SYSLOG("rad", "allocation failure in IOGVAHEVCDecode"); -+ } -+ -+ OSSafeReleaseNULL(VTMaxDecodeLevel); -+ OSSafeReleaseNULL(VTMaxDecodeLevelKey); -+ OSSafeReleaseNULL(VTPerProfileDetailsInner); -+ OSSafeReleaseNULL(VTPerProfileDetails); -+ OSSafeReleaseNULL(VTPerProfileDetailsKey1); -+ OSSafeReleaseNULL(VTPerProfileDetailsKey2); -+ OSSafeReleaseNULL(VTPerProfileDetailsKey3); -+ OSSafeReleaseNULL(VTSupportedProfileArrayKey); -+ OSSafeReleaseNULL(VTSupportedProfileArray1); -+ OSSafeReleaseNULL(VTSupportedProfileArray2); -+ OSSafeReleaseNULL(VTSupportedProfileArray3); -+ OSSafeReleaseNULL(VTSupportedProfileArray); -+ OSSafeReleaseNULL(VTPerProfileDetailsKey); -+ OSSafeReleaseNULL(IOGVAHEVCDecodeCapabilities); -+ } -+ -+ if (needsEncode) { -+ OSObject *VTMaxEncodeLevel = OSNumber::withNumber(153, 32); -+ OSString *VTMaxEncodeLevelKey = OSString::withCString("VTMaxEncodeLevel"); -+ -+ OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); -+ OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(1); -+ OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); -+ -+ OSArray *VTSupportedProfileArray = OSArray::withCapacity(1); -+ OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); -+ -+ OSDictionary *IOGVAHEVCEncodeCapabilities = OSDictionary::withCapacity(4); -+ OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); -+ OSString *VTQualityRatingKey = OSString::withCString("VTQualityRating"); -+ OSNumber *VTQualityRating = OSNumber::withNumber(50, 32); -+ OSString *VTRatingKey = OSString::withCString("VTRating"); -+ OSNumber *VTRating = OSNumber::withNumber(350, 32); -+ OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); -+ -+ if (VTMaxEncodeLevel != nullptr && VTMaxEncodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && -+ VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTSupportedProfileArrayKey != nullptr && -+ VTSupportedProfileArray1 != nullptr && VTSupportedProfileArray != nullptr && VTPerProfileDetailsKey != nullptr && -+ VTQualityRatingKey != nullptr && VTQualityRating != nullptr && VTRatingKey != nullptr && VTRating != nullptr && -+ IOGVAHEVCEncodeCapabilities != nullptr) { -+ -+ VTPerProfileDetailsInner->setObject(VTMaxEncodeLevelKey, VTMaxEncodeLevel); -+ VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); -+ VTSupportedProfileArray->setObject(VTSupportedProfileArray1); -+ -+ IOGVAHEVCEncodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); -+ IOGVAHEVCEncodeCapabilities->setObject(VTQualityRatingKey, VTQualityRating); -+ IOGVAHEVCEncodeCapabilities->setObject(VTRatingKey, VTRating); -+ IOGVAHEVCEncodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); -+ -+ accelService->setProperty("IOGVAHEVCEncode", "1"); -+ accelService->setProperty("IOGVAHEVCEncodeCapabilities", IOGVAHEVCEncodeCapabilities); -+ -+ DBGLOG("rad", "recovering IOGVAHEVCEncode"); -+ } else { -+ SYSLOG("rad", "allocation failure in IOGVAHEVCEncode"); -+ } -+ -+ OSSafeReleaseNULL(VTMaxEncodeLevel); -+ OSSafeReleaseNULL(VTMaxEncodeLevelKey); -+ OSSafeReleaseNULL(VTPerProfileDetailsInner); -+ OSSafeReleaseNULL(VTPerProfileDetails); -+ OSSafeReleaseNULL(VTPerProfileDetailsKey1); -+ OSSafeReleaseNULL(VTSupportedProfileArrayKey); -+ OSSafeReleaseNULL(VTSupportedProfileArray1); -+ OSSafeReleaseNULL(VTSupportedProfileArray); -+ OSSafeReleaseNULL(VTPerProfileDetailsKey); -+ OSSafeReleaseNULL(VTQualityRatingKey); -+ OSSafeReleaseNULL(VTQualityRating); -+ OSSafeReleaseNULL(VTRatingKey); -+ OSSafeReleaseNULL(VTRating); -+ OSSafeReleaseNULL(IOGVAHEVCEncodeCapabilities); -+ } -+ } -+ } -+} - -+void RAD::updateAccelConfig(size_t hwIndex, IOService *accelService, const char **accelConfig) { -+ if (accelService && accelConfig) { -+ if (fixConfigName) { -+ auto gpuService = accelService->getParentEntry(gIOServicePlane); -+ -+ if (gpuService) { -+ auto model = OSDynamicCast(OSData, gpuService->getProperty("model")); -+ if (model) { -+ auto modelStr = static_cast(model->getBytesNoCopy()); -+ if (modelStr) { -+ if (modelStr[0] == 'A' && ((modelStr[1] == 'M' && modelStr[2] == 'D') || -+ (modelStr[1] == 'T' && modelStr[2] == 'I')) && modelStr[3] == ' ') { -+ modelStr += 4; -+ } -+ -+ DBGLOG("rad", "updateAccelConfig found gpu model %s", modelStr); -+ *accelConfig = modelStr; -+ } else { -+ DBGLOG("rad", "updateAccelConfig found null gpu model"); -+ } -+ } else { -+ DBGLOG("rad", "updateAccelConfig failed to find gpu model"); -+ } -+ -+ } else { -+ DBGLOG("rad", "updateAccelConfig failed to find accelerator parent"); -+ } -+ } -+ -+ if (enableGvaSupport && hwIndex == IndexRadeonHardwareX4000) { -+ setGvaProperties(accelService); -+ } -+ } -+} - -- } else { -- DBGLOG("rad", "translateAtomConnectorInfoV1 failed to detect sense for translated connector"); -- } -- } -+bool RAD::wrapSetProperty(IORegistryEntry *that, const char *aKey, void *bytes, unsigned length) { -+ if (length > 10 && aKey && reinterpret_cast(aKey)[0] == 'edom' && reinterpret_cast(aKey)[2] == 'l') { -+ DBGLOG("rad", "SetProperty caught model %u (%.*s)", length, length, static_cast(bytes)); -+ if (*static_cast(bytes) == ' DMA' || *static_cast(bytes) == ' ITA' || *static_cast(bytes) == 'edaR') { -+ if (FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey)) { -+ DBGLOG("rad", "SetProperty ignored setting %s to %s", aKey, static_cast(bytes)); -+ return true; -+ } -+ DBGLOG("rad", "SetProperty missing %s, fallback to %s", aKey, static_cast(bytes)); -+ } -+ } -+ -+ return FunctionCast(wrapSetProperty, callbackRAD->orgSetProperty)(that, aKey, bytes, length); -+} - -- return code; -+OSObject *RAD::wrapGetProperty(IORegistryEntry *that, const char *aKey) { -+ auto obj = FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey); -+ auto props = OSDynamicCast(OSDictionary, obj); -+ -+ if (props && aKey) { -+ const char *prefix {nullptr}; -+ auto provider = OSDynamicCast(IOService, that->getParentEntry(gIOServicePlane)); -+ if (provider) { -+ if (aKey[0] == 'a') { -+ if (!strcmp(aKey, "aty_config")) -+ prefix = "CFG,"; -+ else if (!strcmp(aKey, "aty_properties")) -+ prefix = "PP,"; -+ } else if (aKey[0] == 'c' && !strcmp(aKey, "cail_properties")) { -+ prefix = "CAIL,"; -+ } -+ -+ if (prefix) { -+ DBGLOG("rad", "GetProperty discovered property merge request for %s", aKey); -+ auto rawProps = props->copyCollection(); -+ if (rawProps) { -+ auto newProps = OSDynamicCast(OSDictionary, rawProps); -+ if (newProps) { -+ callbackRAD->mergeProperties(newProps, prefix, provider); -+ that->setProperty(aKey, newProps); -+ obj = newProps; -+ } -+ rawProps->release(); -+ } -+ -+ } -+ } -+ } -+ -+ return obj; - } - --uint32_t RAD::wrapTranslateAtomConnectorInfoV2(void *that, RADConnectors::AtomConnectorInfo *info, RADConnectors::Connector *connector) { -- uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV2, callbackRAD->orgTranslateAtomConnectorInfoV2)(that, info, connector); -+uint32_t RAD::wrapGetConnectorsInfoV1(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { -+ uint32_t code = FunctionCast(wrapGetConnectorsInfoV1, callbackRAD->orgGetConnectorsInfoV1)(that, connectors, sz); -+ auto props = callbackRAD->currentPropProvider.get(); -+ -+ if (code == 0 && sz && props && *props) { -+ if (getKernelVersion() >= KernelVersion::HighSierra) -+ callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); -+ else -+ callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgGetAtomObjectTableForType, *props, connectors, sz); -+ } else { -+ DBGLOG("rad", "getConnectorsInfoV1 failed %X or undefined %d", code, props == nullptr); -+ } -+ -+ return code; -+} - -- if (code == 0 && info && connector) { -- RADConnectors::print(connector, 1); -+uint32_t RAD::wrapGetConnectorsInfoV2(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { -+ uint32_t code = FunctionCast(wrapGetConnectorsInfoV2, callbackRAD->orgGetConnectorsInfoV2)(that, connectors, sz); -+ auto props = callbackRAD->currentPropProvider.get(); - -- uint8_t sense = getSenseID(info->i2cRecord); -- if (sense) { -- DBGLOG("rad", "translateAtomConnectorInfoV2 got sense id %02X", sense); -- uint8_t txmit = 0, enc = 0; -- if (getTxEnc(info->usGraphicObjIds, txmit, enc)) -- callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); -- } else { -- DBGLOG("rad", "translateAtomConnectorInfoV2 failed to detect sense for translated connector"); -- } -- } -+ if (code == 0 && sz && props && *props) -+ callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); -+ else -+ DBGLOG("rad", "getConnectorsInfoV2 failed %X or undefined %d", code, props == nullptr); - -- return code; -+ return code; - } - --bool RAD::wrapATIControllerStart(IOService *ctrl, IOService *provider) { -- DBGLOG("rad", "starting controller " PRIKADDR, CASTKADDR(current_thread())); -- if (callbackRAD->forceVesaMode) { -- DBGLOG("rad", "disabling video acceleration on request"); -- return false; -- } -+uint32_t RAD::wrapLegacyGetConnectorsInfo(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { -+ uint32_t code = FunctionCast(wrapLegacyGetConnectorsInfo, callbackRAD->orgLegacyGetConnectorsInfo)(that, connectors, sz); -+ auto props = callbackRAD->currentLegacyPropProvider.get(); - -- callbackRAD->currentPropProvider.set(provider); -- bool r = FunctionCast(wrapATIControllerStart, callbackRAD->orgATIControllerStart)(ctrl, provider); -- DBGLOG("rad", "starting controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); -- callbackRAD->currentPropProvider.erase(); -+ if (code == 0 && sz && props && *props) -+ callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgLegacyGetAtomObjectTableForType, *props, connectors, sz); -+ else -+ DBGLOG("rad", "legacy getConnectorsInfo failed %X or undefined %d", code, props == nullptr); - -- return r; -+ return code; - } - --bool RAD::wrapLegacyATIControllerStart(IOService *ctrl, IOService *provider) { -- DBGLOG("rad", "starting legacy controller " PRIKADDR, CASTKADDR(current_thread())); -- if (callbackRAD->forceVesaMode) { -- DBGLOG("rad", "disabling legacy video acceleration on request"); -- return false; -- } -+uint32_t RAD::wrapTranslateAtomConnectorInfoV1(void *that, RADConnectors::AtomConnectorInfo *info, RADConnectors::Connector *connector) { -+ uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV1, callbackRAD->orgTranslateAtomConnectorInfoV1)(that, info, connector); -+ -+ if (code == 0 && info && connector) { -+ RADConnectors::print(connector, 1); -+ -+ uint8_t sense = getSenseID(info->i2cRecord); -+ if (sense) { -+ DBGLOG("rad", "translateAtomConnectorInfoV1 got sense id %02X", sense); -+ -+ // We need to extract usGraphicObjIds from info->hpdRecord, which is of type ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT: -+ // struct ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT { -+ // uint8_t ucNumberOfSrc; -+ // uint16_t usSrcObjectID[ucNumberOfSrc]; -+ // uint8_t ucNumberOfDst; -+ // uint16_t usDstObjectID[ucNumberOfDst]; -+ // }; -+ // The value we need is in usSrcObjectID. The structure is byte-packed. -+ -+ uint8_t ucNumberOfSrc = info->hpdRecord[0]; -+ for (uint8_t i = 0; i < ucNumberOfSrc; i++) { -+ auto usSrcObjectID = *reinterpret_cast(info->hpdRecord + sizeof(uint8_t) + i * sizeof(uint16_t)); -+ DBGLOG("rad", "translateAtomConnectorInfoV1 checking %04X object id", usSrcObjectID); -+ if (((usSrcObjectID & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT) == GRAPH_OBJECT_TYPE_ENCODER) { -+ uint8_t txmit = 0, enc = 0; -+ if (getTxEnc(usSrcObjectID, txmit, enc)) -+ callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); -+ break; -+ } -+ } -+ -+ -+ } else { -+ DBGLOG("rad", "translateAtomConnectorInfoV1 failed to detect sense for translated connector"); -+ } -+ } -+ -+ return code; -+} - -- callbackRAD->currentLegacyPropProvider.set(provider); -- bool r = FunctionCast(wrapLegacyATIControllerStart, callbackRAD->orgLegacyATIControllerStart)(ctrl, provider); -- DBGLOG("rad", "starting legacy legacy controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); -- callbackRAD->currentLegacyPropProvider.erase(); -+uint32_t RAD::wrapTranslateAtomConnectorInfoV2(void *that, RADConnectors::AtomConnectorInfo *info, RADConnectors::Connector *connector) { -+ uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV2, callbackRAD->orgTranslateAtomConnectorInfoV2)(that, info, connector); -+ -+ if (code == 0 && info && connector) { -+ RADConnectors::print(connector, 1); -+ -+ uint8_t sense = getSenseID(info->i2cRecord); -+ if (sense) { -+ DBGLOG("rad", "translateAtomConnectorInfoV2 got sense id %02X", sense); -+ uint8_t txmit = 0, enc = 0; -+ if (getTxEnc(info->usGraphicObjIds, txmit, enc)) -+ callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); -+ } else { -+ DBGLOG("rad", "translateAtomConnectorInfoV2 failed to detect sense for translated connector"); -+ } -+ } -+ -+ return code; -+} - -- return r; -+bool RAD::wrapATIControllerStart(IOService *ctrl, IOService *provider) { -+ DBGLOG("rad", "starting controller " PRIKADDR, CASTKADDR(current_thread())); -+ if (callbackRAD->forceVesaMode) { -+ DBGLOG("rad", "disabling video acceleration on request"); -+ return false; -+ } -+ -+ callbackRAD->currentPropProvider.set(provider); -+ bool r = FunctionCast(wrapATIControllerStart, callbackRAD->orgATIControllerStart)(ctrl, provider); -+ DBGLOG("rad", "starting controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); -+ callbackRAD->currentPropProvider.erase(); -+ -+ return r; -+} -+ -+bool RAD::wrapLegacyATIControllerStart(IOService *ctrl, IOService *provider) { -+ DBGLOG("rad", "starting legacy controller " PRIKADDR, CASTKADDR(current_thread())); -+ if (callbackRAD->forceVesaMode) { -+ DBGLOG("rad", "disabling legacy video acceleration on request"); -+ return false; -+ } -+ -+ callbackRAD->currentLegacyPropProvider.set(provider); -+ bool r = FunctionCast(wrapLegacyATIControllerStart, callbackRAD->orgLegacyATIControllerStart)(ctrl, provider); -+ DBGLOG("rad", "starting legacy legacy controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); -+ callbackRAD->currentLegacyPropProvider.erase(); -+ -+ return r; - } - - IOReturn RAD::findProjectByPartNumber(IOService *ctrl, void *properties) { +10a11 +> #include +193,236c194,254 +< OSDictionary * matching = IOService::serviceMatching("AppleBacklightDisplay"); +< if (matching == nullptr) { +< DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null AppleBacklightDisplay"); +< return; +< } +< +< OSIterator *iter = IOService::getMatchingServices(matching); +< if (iter == nullptr) { +< DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null matching"); +< matching->release(); +< return; +< } +< +< IORegistryEntry* display = OSDynamicCast(IORegistryEntry, iter->getNextObject()); +< if (display == nullptr) { +< DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null display"); +< iter->release(); +< matching->release(); +< return; +< } +< +< OSDictionary* iodispparm = OSDynamicCast(OSDictionary, display->getProperty("IODisplayParameters")); +< if (iodispparm == nullptr) { +< DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null IODisplayParameters"); +< iter->release(); +< matching->release(); +< return; +< } +< +< OSDictionary* linearbri = OSDynamicCast(OSDictionary, iodispparm->getObject("linear-brightness")); +< if (linearbri == nullptr) { +< DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null linear-brightness"); +< iter->release(); +< matching->release(); +< return; +< } +< +< OSNumber* maxbri = OSDynamicCast(OSNumber, linearbri->getObject("max")); +< if (maxbri == nullptr) { +< DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null max"); +< iter->release(); +< matching->release(); +< return; +< } +--- +> OSDictionary * matching = IOService::serviceMatching("AppleBacklightDisplay"); +> if (matching == nullptr) { +> DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null AppleBacklightDisplay"); +> return; +> } +> +> OSIterator *iter = IOService::getMatchingServices(matching); +> if (iter == nullptr) { +> DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null matching"); +> matching->release(); +> return; +> } +> +> IORegistryEntry* display = OSDynamicCast(IORegistryEntry, iter->getNextObject()); +> if (display == nullptr) { +> DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null display"); +> iter->release(); +> matching->release(); +> return; +> } +> +> OSDictionary* iodispparm = OSDynamicCast(OSDictionary, display->getProperty("IODisplayParameters")); +> if (iodispparm == nullptr) { +> DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null IODisplayParameters"); +> iter->release(); +> matching->release(); +> return; +> } +> +> OSDictionary* linearbri = OSDynamicCast(OSDictionary, iodispparm->getObject("linear-brightness")); +> if (linearbri == nullptr) { +> DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null linear-brightness"); +> iter->release(); +> matching->release(); +> return; +> } +> +> OSNumber* maxbri = OSDynamicCast(OSNumber, linearbri->getObject("max")); +> if (maxbri == nullptr) { +> DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null max"); +> iter->release(); +> matching->release(); +> return; +> } +> +> callbackRAD->maxPwmBacklightLvl = maxbri->unsigned32BitValue(); +> DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay get max brightness: 0x%x", callbackRAD->maxPwmBacklightLvl); +> +> iter->release(); +> matching->release(); +> } +> +> typedef int64_t __int64; +> typedef int64_t _QWORD; +> typedef int32_t _DWORD; +> typedef int8_t __int8; +> +> struct dc_stream_state { +> void *sink; +> void *link; +> }; +238,239c256 +< callbackRAD->maxPwmBacklightLvl = maxbri->unsigned32BitValue(); +< DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay get max brightness: 0x%x", callbackRAD->maxPwmBacklightLvl); +--- +> #define MAX_SINKS_PER_LINK 4 +241,243c258,264 +< iter->release(); +< matching->release(); +< } +--- +> struct dc_link_settings { +> uint32_t lane_count; +> uint32_t link_rate; +> uint32_t link_spread; +> bool use_link_rate_set; +> uint8_t link_rate_set; +> }; +245,250c266,270 +< uint32_t RAD::wrapDcePanelCntlHwInit(void *panel_cntl) { +< callbackRAD->panelCntlPtr = panel_cntl; +< callbackRAD->updatePwmMaxBrightnessFromInternalDisplay(); // read max brightness value from IOReg +< uint32_t ret = FunctionCast(wrapDcePanelCntlHwInit, callbackRAD->orgDcePanelCntlHwInit)(panel_cntl); +< return ret; +< } +--- +> struct dc_lane_settings { +> uint32_t VOLTAGE_SWING; +> uint32_t PRE_EMPHASIS; +> uint32_t POST_CURSOR2; +> }; +252,289c272,287 +< IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t value) { +< IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute)(framebuffer, connectIndex, attribute, value); +< if (attribute != (UInt32)'bklt') { +< return ret; +< } +< +< if (callbackRAD->maxPwmBacklightLvl == 0) { +< DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute zero maxPwmBacklightLvl"); +< return 0; +< } +< +< if (callbackRAD->panelCntlPtr == nullptr) { +< DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null panel cntl"); +< return 0; +< } +< +< if (callbackRAD->orgDceDriverSetBacklight == nullptr) { +< DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null orgDcLinkSetBacklightLevel"); +< return 0; +< } +< +< // set the backlight of AMD navi10 driver +< callbackRAD->curPwmBacklightLvl = (uint32_t)value; +< uint32_t btlper = callbackRAD->curPwmBacklightLvl * 100 / callbackRAD->maxPwmBacklightLvl; +< uint32_t pwmval = 0; +< if (btlper >= 100) { +< // This is from the dmcu_set_backlight_level function of Linux source +< // ... +< // if (backlight_pwm_u16_16 & 0x10000) +< // backlight_8_bit = 0xFF; +< // else +< // backlight_8_bit = (backlight_pwm_u16_16 >> 8) & 0xFF; +< // ... +< // The max brightness should have 0x10000 bit set +< pwmval = 0x1FF00; +< } else { +< pwmval = ((btlper * 0xFF) / 100) << 8U; +< } +--- +> struct dc_link_training_overrides { +> uint32_t *voltage_swing; +> uint32_t *pre_emphasis; +> uint32_t *post_cursor2; +> +> uint16_t *cr_pattern_time; +> uint16_t *eq_pattern_time; +> uint32_t *pattern_for_cr; +> uint32_t *pattern_for_eq; +> +> uint32_t *downspread; +> bool *alternate_scrambler_reset; +> bool *enhanced_framing; +> bool *mst_enable; +> bool *fec_enable; +> }; +291,293c289,292 +< callbackRAD->orgDceDriverSetBacklight(callbackRAD->panelCntlPtr, pwmval); +< return 0; +< } +--- +> struct dp_audio_test_data_flags { +> uint8_t test_requested :1; +> uint8_t disable_video :1; +> }; +295,303c294 +< IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t * value) { +< IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute)(framebuffer, connectIndex, attribute, value); +< if (attribute == (UInt32)'bklt') { +< // enable the backlight feature of AMD navi10 driver +< *value = callbackRAD->curPwmBacklightLvl; +< ret = 0; +< } +< return ret; +< } +--- +> struct dp_audio_test_data { +305,311c296,301 +< bool RAD::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { +< if (kextRadeonX6000Framebuffer.loadIndex == index) { +< KernelPatcher::RouteRequest requests[] = { +< {"_dce_panel_cntl_hw_init", wrapDcePanelCntlHwInit, orgDcePanelCntlHwInit}, +< {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25setAttributeForConnectionEijm", wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute}, +< {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25getAttributeForConnectionEijPm", wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute}, +< }; +--- +> struct dp_audio_test_data_flags flags; +> uint8_t sampling_rate; +> uint8_t channel_count; +> uint8_t pattern_type; +> uint8_t pattern_period[8]; +> }; +313,327c303,309 +< if (!patcher.routeMultiple(index, requests, address, size, true, true)) +< SYSLOG("igfx", "Failed to route redeon x6000 gpu tracing."); +< +< orgDceDriverSetBacklight = reinterpret_cast(patcher.solveSymbol(index, "_dce_driver_set_backlight")); +< if (patcher.getError() != KernelPatcher::Error::NoError) { +< SYSLOG("igfx", "failed to resolve _dce_driver_set_backlight"); +< patcher.clearError(); +< } +< } +< +< if (kextRadeonFramebuffer.loadIndex == index) { +< if (force24BppMode) +< process24BitOutput(patcher, kextRadeonFramebuffer, address, size); +< return true; +< } +--- +> union compliance_test_state { +> struct { +> unsigned char STEREO_3D_RUNNING : 1; +> unsigned char RESERVED : 7; +> } bits; +> unsigned char raw; +> }; +329,333c311,316 +< if (kextRadeonLegacyFramebuffer.loadIndex == index) { +< if (force24BppMode) +< process24BitOutput(patcher, kextRadeonLegacyFramebuffer, address, size); +< return true; +< } +--- +> struct graphics_object_id { +> uint32_t id:8; +> uint32_t enum_id:4; +> uint32_t type:4; +> uint32_t reserved:16; /* for padding. total size should be u32 */ +> }; +335,336c318,326 +< if (kextRadeonSupport.loadIndex == index) { +< processConnectorOverrides(patcher, address, size, true); +--- +> union ddi_channel_mapping { +> struct mapping { +> uint8_t lane0:2; /* Mapping for lane 0 */ +> uint8_t lane1:2; /* Mapping for lane 1 */ +> uint8_t lane2:2; /* Mapping for lane 2 */ +> uint8_t lane3:2; /* Mapping for lane 3 */ +> } mapping; +> uint8_t raw; +> }; +338,342c328,332 +< if (getKernelVersion() > KernelVersion::Mojave || +< (getKernelVersion() == KernelVersion::Mojave && getKernelMinorVersion() >= 5)) { +< KernelPatcher::RouteRequest request("__ZN13ATIController8TestVRAME13PCI_REG_INDEXb", doNotTestVram); +< patcher.routeMultiple(index, &request, 1, address, size); +< } +--- +> struct device_id { +> uint32_t device_type:16; +> uint32_t enum_id:16; /* 1 based enum */ +> uint16_t raw_device_tag; +> }; +344,347c334,337 +< if (useCustomAgdpDecision) { +< KernelPatcher::RouteRequest request("__ZN16AtiDeviceControl16notifyLinkChangeE31kAGDCRegisterLinkControlEvent_tmj", wrapNotifyLinkChange, orgNotifyLinkChange); +< patcher.routeMultiple(index, &request, 1, address, size); +< } +--- +> struct connector_device_tag_info { +> uint32_t acpi_device; +> struct device_id dev_id; +> }; +349,350c339,345 +< return true; +< } +--- +> union dpcd_rev { +> struct { +> uint8_t MINOR:4; +> uint8_t MAJOR:4; +> } bits; +> uint8_t raw; +> }; +352,355c347,355 +< if (kextRadeonLegacySupport.loadIndex == index) { +< processConnectorOverrides(patcher, address, size, false); +< return true; +< } +--- +> union max_lane_count { +> struct { +> uint8_t MAX_LANE_COUNT:5; +> uint8_t POST_LT_ADJ_REQ_SUPPORTED:1; +> uint8_t TPS3_SUPPORTED:1; +> uint8_t ENHANCED_FRAME_CAP:1; +> } bits; +> uint8_t raw; +> }; +357,360c357,365 +< if (kextPolarisController.loadIndex == index) { +< KernelPatcher::RouteRequest request("__ZN17AMD9500Controller23findProjectByPartNumberEP20ControllerProperties", findProjectByPartNumber); +< patcher.routeMultiple(index, &request, 1, address, size); +< } +--- +> union max_down_spread { +> struct { +> uint8_t MAX_DOWN_SPREAD:1; +> uint8_t RESERVED:5; +> uint8_t NO_AUX_HANDSHAKE_LINK_TRAINING:1; +> uint8_t TPS4_SUPPORTED:1; +> } bits; +> uint8_t raw; +> }; +362,367c367,379 +< for (size_t i = 0; i < maxHardwareKexts; i++) { +< if (kextRadeonHardware[i].loadIndex == index) { +< processHardwareKext(patcher, i, address, size); +< return true; +< } +< } +--- +> union dprx_feature { +> struct { +> uint8_t GTC_CAP:1; // bit 0: DP 1.3+ +> uint8_t SST_SPLIT_SDP_CAP:1; // bit 1: DP 1.4 +> uint8_t AV_SYNC_CAP:1; // bit 2: DP 1.3+ +> uint8_t VSC_SDP_COLORIMETRY_SUPPORTED:1; // bit 3: DP 1.3+ +> uint8_t VSC_EXT_VESA_SDP_SUPPORTED:1; // bit 4: DP 1.4 +> uint8_t VSC_EXT_VESA_SDP_CHAINING_SUPPORTED:1; // bit 5: DP 1.4 +> uint8_t VSC_EXT_CEA_SDP_SUPPORTED:1; // bit 6: DP 1.4 +> uint8_t VSC_EXT_CEA_SDP_CHAINING_SUPPORTED:1; // bit 7: DP 1.4 +> } bits; +> uint8_t raw; +> }; +369,370c381,388 +< return false; +< } +--- +> union sink_count { +> struct { +> uint8_t SINK_COUNT:6; +> uint8_t CPREADY:1; +> uint8_t RESERVED:1; +> } bits; +> uint8_t raw; +> }; +372,395c390,403 +< void RAD::initHardwareKextMods() { +< // Decide on kext amount present for optimal performance. +< // 10.15+ has X4000, X5000, and X6000 +< // 10.14+ has X4000 and X5000 +< // 10.13.4+ has X3000, X4000, and X5000 +< if (getKernelVersion() >= KernelVersion::Catalina) +< maxHardwareKexts = MaxRadeonHardwareCatalina; +< else if (getKernelVersion() >= KernelVersion::Mojave) +< maxHardwareKexts = MaxRadeonHardwareMojave; +< else if (getKernelVersion() == KernelVersion::HighSierra && getKernelMinorVersion() >= 5) +< maxHardwareKexts = MaxRadeonHardwareModernHighSierra; +< +< // 10.13.4 fixed black screen issues +< if (maxHardwareKexts != MaxRadeonHardware) { +< for (size_t i = 0; i < MaxGetFrameBufferProcs; i++) +< getFrameBufferProcNames[IndexRadeonHardwareX4000][i] = nullptr; +< +< // We have nothing to do for these kexts on recent systems +< if (!fixConfigName && !forceOpenGL && !forceCodecInfo) { +< // X4000 kext is not included in this list as we need to fix GVA properties for most of its GPUs +< kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); +< kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); +< } +< } +--- +> struct dc_dongle_caps { +> /* dongle type (DP converter, CV smart dongle) */ +> uint32_t dongle_type; +> bool extendedCapValid; +> /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, +> indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ +> bool is_dp_hdmi_s3d_converter; +> bool is_dp_hdmi_ycbcr422_pass_through; +> bool is_dp_hdmi_ycbcr420_pass_through; +> bool is_dp_hdmi_ycbcr422_converter; +> bool is_dp_hdmi_ycbcr420_converter; +> uint32_t dp_hdmi_max_bpc; +> uint32_t dp_hdmi_max_pixel_clk_in_khz; +> }; +397,399c405,414 +< if (getKernelVersion() < KernelVersion::Catalina) { +< kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); +< } +--- +> union dpcd_fec_capability { +> struct { +> uint8_t FEC_CAPABLE:1; +> uint8_t UNCORRECTED_BLOCK_ERROR_COUNT_CAPABLE:1; +> uint8_t CORRECTED_BLOCK_ERROR_COUNT_CAPABLE:1; +> uint8_t BIT_ERROR_COUNT_CAPABLE:1; +> uint8_t RESERVED:4; +> } bits; +> uint8_t raw; +> }; +401,415c416,423 +< if (getKernelVersion() < KernelVersion::HighSierra) { +< // Versions before 10.13 do not support X4250 and X5000 +< kextRadeonHardware[IndexRadeonHardwareX4250].switchOff(); +< kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); +< +< // Versions before 10.13 have legacy X3000 and X4000 IDs +< kextRadeonHardware[IndexRadeonHardwareX3000].id = idRadeonX3000Old; +< kextRadeonHardware[IndexRadeonHardwareX4000].id = idRadeonX4000Old; +< +< bool preSierra = getKernelVersion() < KernelVersion::Sierra; +< +< if (preSierra) { +< // Versions before 10.12 do not support X4100 +< kextRadeonHardware[IndexRadeonHardwareX4100].switchOff(); +< } +--- +> union dpcd_dsc_branch_decoder_capabilities { +> struct { +> uint8_t BRANCH_OVERALL_THROUGHPUT_0; +> uint8_t BRANCH_OVERALL_THROUGHPUT_1; +> uint8_t BRANCH_MAX_LINE_WIDTH; +> } fields; +> uint8_t raw[3]; +> }; +417,422c425,429 +< if (preSierra || (getKernelVersion() == KernelVersion::Sierra && getKernelMinorVersion() < 7)) { +< // Versions before 10.12.6 do not support X4150, X4200 +< kextRadeonHardware[IndexRadeonHardwareX4150].switchOff(); +< kextRadeonHardware[IndexRadeonHardwareX4200].switchOff(); +< } +< } +--- +> struct dpcd_dsc_support { +> uint8_t DSC_SUPPORT :1; +> uint8_t DSC_PASSTHROUGH_SUPPORT :1; +> uint8_t RESERVED :6; +> }; +424,425c431,434 +< lilu.onKextLoadForce(kextRadeonHardware, maxHardwareKexts); +< } +--- +> struct dpcd_dsc_algorithm_revision { +> uint8_t DSC_VERSION_MAJOR :4; +> uint8_t DSC_VERSION_MINOR :4; +> }; +427,446c436,439 +< void RAD::process24BitOutput(KernelPatcher &patcher, KernelPatcher::KextInfo &info, mach_vm_address_t address, size_t size) { +< auto bitsPerComponent = patcher.solveSymbol(info.loadIndex, "__ZL18BITS_PER_COMPONENT", address, size); +< if (bitsPerComponent) { +< while (bitsPerComponent && *bitsPerComponent) { +< if (*bitsPerComponent == 10) { +< auto ret = MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock); +< if (ret == KERN_SUCCESS) { +< DBGLOG("rad", "fixing BITS_PER_COMPONENT"); +< *bitsPerComponent = 8; +< MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock); +< } else { +< SYSLOG("rad", "failed to disable write protection for BITS_PER_COMPONENT"); +< } +< } +< bitsPerComponent++; +< } +< } else { +< SYSLOG("rad", "failed to find BITS_PER_COMPONENT"); +< patcher.clearError(); +< } +--- +> struct dpcd_dsc_rc_buffer_block_size { +> uint8_t RC_BLOCK_BUFFER_SIZE :2; +> uint8_t RESERVED :6; +> }; +448c441,450 +< DBGLOG("rad", "fixing pixel types"); +--- +> struct dpcd_dsc_slice_capability1 { +> uint8_t ONE_SLICE_PER_DP_DSC_SINK_DEVICE :1; +> uint8_t TWO_SLICES_PER_DP_DSC_SINK_DEVICE :1; +> uint8_t RESERVED :1; +> uint8_t FOUR_SLICES_PER_DP_DSC_SINK_DEVICE :1; +> uint8_t SIX_SLICES_PER_DP_DSC_SINK_DEVICE :1; +> uint8_t EIGHT_SLICES_PER_DP_DSC_SINK_DEVICE :1; +> uint8_t TEN_SLICES_PER_DP_DSC_SINK_DEVICE :1; +> uint8_t TWELVE_SLICES_PER_DP_DSC_SINK_DEVICE :1; +> }; +450,462c452,455 +< KernelPatcher::LookupPatch pixelPatch { +< &info, +< reinterpret_cast("--RRRRRRRRRRGGGGGGGGGGBBBBBBBBBB"), +< reinterpret_cast("--------RRRRRRRRGGGGGGGGBBBBBBBB"), +< 32, 2 +< }; +< +< patcher.applyLookupPatch(&pixelPatch); +< if (patcher.getError() != KernelPatcher::Error::NoError) { +< SYSLOG("rad", "failed to patch RGB mask for 24-bit output"); +< patcher.clearError(); +< } +< } +--- +> struct dpcd_dsc_line_buffer_bit_depth { +> uint8_t LINE_BUFFER_BIT_DEPTH :4; +> uint8_t RESERVED :4; +> }; +464,496c457,460 +< void RAD::processConnectorOverrides(KernelPatcher &patcher, mach_vm_address_t address, size_t size, bool modern) { +< if (modern) { +< if (getKernelVersion() >= KernelVersion::HighSierra) { +< KernelPatcher::RouteRequest requests[] { +< KernelPatcher::RouteRequest("__ZN14AtiBiosParser116getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), +< KernelPatcher::RouteRequest("__ZN14AtiBiosParser216getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV2, orgGetConnectorsInfoV2), +< KernelPatcher::RouteRequest("__ZN14AtiBiosParser126translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V117AtomConnectorInfoER13ConnectorInfo", +< wrapTranslateAtomConnectorInfoV1, orgTranslateAtomConnectorInfoV1), +< KernelPatcher::RouteRequest("__ZN14AtiBiosParser226translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V217AtomConnectorInfoER13ConnectorInfo", +< wrapTranslateAtomConnectorInfoV2, orgTranslateAtomConnectorInfoV2), +< KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart) +< }; +< patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); +< } else { +< KernelPatcher::RouteRequest requests[] { +< KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), +< KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart), +< }; +< patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); +< +< orgGetAtomObjectTableForType = reinterpret_cast(patcher.solveSymbol(kextRadeonSupport.loadIndex, +< "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size)); +< if (!orgGetAtomObjectTableForType) { +< SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); +< patcher.clearError(); +< } +< } +< } else { +< KernelPatcher::RouteRequest requests[] { +< KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapLegacyGetConnectorsInfo, orgLegacyGetConnectorsInfo), +< KernelPatcher::RouteRequest("__ZN19AMDLegacyController5startEP9IOService", wrapLegacyATIControllerStart, orgLegacyATIControllerStart), +< }; +< patcher.routeMultiple(kextRadeonLegacySupport.loadIndex, requests, address, size); +--- +> struct dpcd_dsc_block_prediction_support { +> uint8_t BLOCK_PREDICTION_SUPPORT:1; +> uint8_t RESERVED :7; +> }; +498,505c462,466 +< orgLegacyGetAtomObjectTableForType = patcher.solveSymbol(kextRadeonLegacySupport.loadIndex, +< "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size); +< if (!orgLegacyGetAtomObjectTableForType) { +< SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); +< patcher.clearError(); +< } +< } +< } +--- +> struct dpcd_maximum_bits_per_pixel_supported_by_the_decompressor { +> uint8_t MAXIMUM_BITS_PER_PIXEL_SUPPORTED_BY_THE_DECOMPRESSOR_LOW :7; +> uint8_t MAXIMUM_BITS_PER_PIXEL_SUPPORTED_BY_THE_DECOMPRESSOR_HIGH :7; +> uint8_t RESERVED :2; +> }; +507,509c468,475 +< void RAD::processHardwareKext(KernelPatcher &patcher, size_t hwIndex, mach_vm_address_t address, size_t size) { +< auto getFrame = getFrameBufferProcNames[hwIndex]; +< auto &hardware = kextRadeonHardware[hwIndex]; +--- +> struct dpcd_dsc_decoder_color_format_capabilities { +> uint8_t RGB_SUPPORT :1; +> uint8_t Y_CB_CR_444_SUPPORT :1; +> uint8_t Y_CB_CR_SIMPLE_422_SUPPORT :1; +> uint8_t Y_CB_CR_NATIVE_422_SUPPORT :1; +> uint8_t Y_CB_CR_NATIVE_420_SUPPORT :1; +> uint8_t RESERVED :3; +> }; +511,535c477,483 +< // Fix boot and wake to black screen +< for (size_t j = 0; j < MaxGetFrameBufferProcs && getFrame[j] != nullptr; j++) { +< auto getFB = patcher.solveSymbol(hardware.loadIndex, getFrame[j], address, size); +< if (getFB) { +< // Initially it was discovered that the only problematic register is PRIMARY_SURFACE_ADDRESS_HIGH (0x1A07). +< // This register must be nulled to solve most of the issues. +< // Depending on the amount of connected screens PRIMARY_SURFACE_ADDRESS (0x1A04) may not be null. +< // However, as of AMD Vega drivers in 10.13 DP1 both of these registers are now ignored. +< // Furthermore, there are no (extra) issues from just returning 0 in framebuffer base address. +< +< // xor rax, rax +< // ret +< uint8_t ret[] {0x48, 0x31, 0xC0, 0xC3}; +< patcher.routeBlock(getFB, ret, sizeof(ret)); +< if (patcher.getError() == KernelPatcher::Error::NoError) { +< DBGLOG("rad", "patched %s", getFrame[j]); +< } else { +< SYSLOG("rad", "failed to patch %s code %d", getFrame[j], patcher.getError()); +< patcher.clearError(); +< } +< } else { +< SYSLOG("rad", "failed to find %s code %d", getFrame[j], patcher.getError()); +< patcher.clearError(); +< } +< } +--- +> struct dpcd_dsc_decoder_color_depth_capabilities { +> uint8_t RESERVED0 :1; +> uint8_t EIGHT_BITS_PER_COLOR_SUPPORT :1; +> uint8_t TEN_BITS_PER_COLOR_SUPPORT :1; +> uint8_t TWELVE_BITS_PER_COLOR_SUPPORT :1; +> uint8_t RESERVED1 :4; +> }; +537,542c485,488 +< // Fix reported Accelerator name to support WhateverName.app +< // Also fix GVA properties for X4000. +< if (fixConfigName || hwIndex == IndexRadeonHardwareX4000) { +< KernelPatcher::RouteRequest request(populateAccelConfigProcNames[hwIndex], wrapPopulateAccelConfig[hwIndex], orgPopulateAccelConfig[hwIndex]); +< patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); +< } +--- +> struct dpcd_peak_dsc_throughput_dsc_sink { +> uint8_t THROUGHPUT_MODE_0:4; +> uint8_t THROUGHPUT_MODE_1:4; +> }; +544,555c490,495 +< // Enforce OpenGL support if requested +< if (forceOpenGL) { +< DBGLOG("rad", "disabling Metal support"); +< uint8_t find1[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; +< uint8_t find2[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; +< uint8_t repl1[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; +< uint8_t repl2[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; +< +< KernelPatcher::LookupPatch antimetal[] { +< {&hardware, find1, repl1, sizeof(find1), 2}, +< {&hardware, find2, repl2, sizeof(find1), 2} +< }; +--- +> struct dpcd_dsc_slice_capabilities_2 { +> uint8_t SIXTEEN_SLICES_PER_DSC_SINK_DEVICE :1; +> uint8_t TWENTY_SLICES_PER_DSC_SINK_DEVICE :1; +> uint8_t TWENTYFOUR_SLICES_PER_DSC_SINK_DEVICE :1; +> uint8_t RESERVED :5; +> }; +557,561c497,520 +< for (auto &p : antimetal) { +< patcher.applyLookupPatch(&p); +< patcher.clearError(); +< } +< } +--- +> struct dpcd_bits_per_pixel_increment{ +> uint8_t INCREMENT_OF_BITS_PER_PIXEL_SUPPORTED :3; +> uint8_t RESERVED :5; +> }; +> union dpcd_dsc_basic_capabilities { +> struct { +> struct dpcd_dsc_support dsc_support; +> struct dpcd_dsc_algorithm_revision dsc_algorithm_revision; +> struct dpcd_dsc_rc_buffer_block_size dsc_rc_buffer_block_size; +> uint8_t dsc_rc_buffer_size; +> struct dpcd_dsc_slice_capability1 dsc_slice_capabilities_1; +> struct dpcd_dsc_line_buffer_bit_depth dsc_line_buffer_bit_depth; +> struct dpcd_dsc_block_prediction_support dsc_block_prediction_support; +> struct dpcd_maximum_bits_per_pixel_supported_by_the_decompressor maximum_bits_per_pixel_supported_by_the_decompressor; +> struct dpcd_dsc_decoder_color_format_capabilities dsc_decoder_color_format_capabilities; +> struct dpcd_dsc_decoder_color_depth_capabilities dsc_decoder_color_depth_capabilities; +> struct dpcd_peak_dsc_throughput_dsc_sink peak_dsc_throughput_dsc_sink; +> uint8_t dsc_maximum_slice_width; +> struct dpcd_dsc_slice_capabilities_2 dsc_slice_capabilities_2; +> uint8_t reserved; +> struct dpcd_bits_per_pixel_increment bits_per_pixel_increment; +> } fields; +> uint8_t raw[16]; +> }; +563,568c522,525 +< // Patch AppleGVA support for non-supported models +< if (forceCodecInfo && getHWInfoProcNames[hwIndex] != nullptr) { +< KernelPatcher::RouteRequest request(getHWInfoProcNames[hwIndex], wrapGetHWInfo[hwIndex], orgGetHWInfo[hwIndex]); +< patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); +< } +< } +--- +> struct dpcd_dsc_capabilities { +> union dpcd_dsc_basic_capabilities dsc_basic_caps; +> union dpcd_dsc_branch_decoder_capabilities dsc_branch_decoder_caps; +> }; +570,589c527 +< void RAD::mergeProperty(OSDictionary *props, const char *name, OSObject *value) { +< // The only type we could make from device properties is data. +< // To be able to override other types we do a conversion here. +< auto data = OSDynamicCast(OSData, value); +< if (data) { +< // It is hard to make a boolean even from ACPI, so we make a hack here: +< // 1-byte OSData with 0x01 / 0x00 values becomes boolean. +< auto val = static_cast(data->getBytesNoCopy()); +< auto len = data->getLength(); +< if (val && len == sizeof(uint8_t)) { +< if (val[0] == 1) { +< props->setObject(name, kOSBooleanTrue); +< DBGLOG("rad", "prop %s was merged as kOSBooleanTrue", name); +< return; +< } else if (val[0] == 0) { +< props->setObject(name, kOSBooleanFalse); +< DBGLOG("rad", "prop %s was merged as kOSBooleanFalse", name); +< return; +< } +< } +--- +> #define MAX_REPEATER_CNT 8 +591,617c529,782 +< // Consult the original value to make a decision +< auto orgValue = props->getObject(name); +< if (val && orgValue) { +< DBGLOG("rad", "prop %s has original value", name); +< if (len == sizeof(uint32_t) && OSDynamicCast(OSNumber, orgValue)) { +< auto num = *reinterpret_cast(val); +< auto osnum = OSNumber::withNumber(num, 32); +< if (osnum) { +< DBGLOG("rad", "prop %s was merged as number %u", name, num); +< props->setObject(name, osnum); +< osnum->release(); +< } +< return; +< } else if (len > 0 && val[len-1] == '\0' && OSDynamicCast(OSString, orgValue)) { +< auto str = reinterpret_cast(val); +< auto osstr = OSString::withCString(str); +< if (osstr) { +< DBGLOG("rad", "prop %s was merged as string %s", name, str); +< props->setObject(name, osstr); +< osstr->release(); +< } +< return; +< } +< } else { +< DBGLOG("rad", "prop %s has no original value", name); +< } +< } +--- +> struct dc_lttpr_caps { +> union dpcd_rev revision; +> uint8_t mode; +> uint8_t max_lane_count; +> uint8_t max_link_rate; +> uint8_t phy_repeater_cnt; +> uint8_t max_ext_timeout; +> uint8_t aux_rd_interval[MAX_REPEATER_CNT - 1]; +> }; +> +> struct psr_caps { +> unsigned char psr_version; +> unsigned int psr_rfb_setup_time; +> bool psr_exit_link_training_required; +> }; +> +> struct dpcd_caps { +> union dpcd_rev dpcd_rev; +> union max_lane_count max_ln_count; +> union max_down_spread max_down_spread; +> union dprx_feature dprx_feature; +> +> /* valid only for eDP v1.4 or higher*/ +> uint8_t edp_supported_link_rates_count; +> uint32_t edp_supported_link_rates[8]; +> +> /* dongle type (DP converter, CV smart dongle) */ +> uint32_t dongle_type; +> /* branch device or sink device */ +> bool is_branch_dev; +> /* Dongle's downstream count. */ +> union sink_count sink_count; +> /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, +> indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ +> struct dc_dongle_caps dongle_caps; +> +> uint32_t sink_dev_id; +> int8_t sink_dev_id_str[6]; +> int8_t sink_hw_revision; +> int8_t sink_fw_revision[2]; +> +> uint32_t branch_dev_id; +> int8_t branch_dev_name[6]; +> int8_t branch_hw_revision; +> int8_t branch_fw_revision[2]; +> +> bool allow_invalid_MSA_timing_param; +> bool panel_mode_edp; +> bool dpcd_display_control_capable; +> bool ext_receiver_cap_field_present; +> bool dynamic_backlight_capable_edp; +> union dpcd_fec_capability fec_cap; +> struct dpcd_dsc_capabilities dsc_caps; +> struct dc_lttpr_caps lttpr_caps; +> struct psr_caps psr_caps; +> +> }; +> +> union dpcd_sink_ext_caps { +> struct { +> /* 0 - Sink supports backlight adjust via PWM during SDR/HDR mode +> * 1 - Sink supports backlight adjust via AUX during SDR/HDR mode. +> */ +> uint8_t sdr_aux_backlight_control : 1; +> uint8_t hdr_aux_backlight_control : 1; +> uint8_t reserved_1 : 2; +> uint8_t oled : 1; +> uint8_t reserved : 3; +> } bits; +> uint8_t raw; +> }; +> +> struct dc; +> +> typedef struct { +> void *remote_sinks[MAX_SINKS_PER_LINK]; +> unsigned int sink_count; +> void *local_sink; +> unsigned int link_index; +> uint32_t type; +> uint32_t connector_signal; +> uint32_t irq_source_hpd; +> uint32_t irq_source_hpd_rx;/* aka DP Short Pulse */ +> bool is_hpd_filter_disabled; +> bool dp_ss_off; +> bool link_state_valid; +> bool aux_access_disabled; +> bool sync_lt_in_progress; +> uint32_t lttpr_mode; +> bool is_internal_display; +> +> /* TODO: Rename. Flag an endpoint as having a programmable mapping to a +> * DIG encoder. */ +> bool is_dig_mapping_flexible; +> bool hpd_status; /* HPD status of link without physical HPD pin. */ +> +> bool edp_sink_present; +> +> /* caps is the same as reported_link_cap. link_traing use +> * reported_link_cap. Will clean up. TODO +> */ +> struct dc_link_settings reported_link_cap; +> struct dc_link_settings verified_link_cap; +> struct dc_link_settings cur_link_settings; +> struct dc_lane_settings cur_lane_setting; +> struct dc_link_settings preferred_link_setting; +> struct dc_link_training_overrides preferred_training_settings; +> struct dp_audio_test_data audio_test_data; +> +> uint8_t ddc_hw_inst; +> +> uint8_t hpd_src; +> +> uint8_t link_enc_hw_inst; +> /* DIG link encoder ID. Used as index in link encoder resource pool. +> * For links with fixed mapping to DIG, this is not changed after dc_link +> * object creation. +> */ +> uint32_t eng_id; +> +> bool test_pattern_enabled; +> union compliance_test_state compliance_test_state; +> +> void *priv; +> +> void *ddc; +> +> bool aux_mode; +> +> /* Private to DC core */ +> +> struct dc *dc; +> +> void *ctx; +> +> void *panel_cntl; +> void *link_enc; +> struct graphics_object_id link_id; +> /* Endpoint type distinguishes display endpoints which do not have entries +> * in the BIOS connector table from those that do. Helps when tracking link +> * encoder to display endpoint assignments. +> */ +> uint32_t ep_type; +> union ddi_channel_mapping ddi_channel_mapping; +> struct connector_device_tag_info device_tag; +> struct dpcd_caps dpcd_caps; +> uint32_t dongle_max_pix_clk; +> unsigned short chip_caps; +> unsigned int dpcd_sink_count; +> //#if defined(CONFIG_DRM_AMD_DC_HDCP) +> // struct hdcp_caps hdcp_caps; +> //#endif +> uint32_t edp_revision; +> union dpcd_sink_ext_caps dpcd_sink_ext_caps; +> +> /* +> struct psr_settings psr_settings; +> +> // MST record stream using this link +> struct link_flags { +> bool dp_keep_receiver_powered; +> bool dp_skip_DID2; +> bool dp_skip_reset_segment; +> } wa_flags; +> struct link_mst_stream_allocation_table mst_stream_alloc_table; +> +> struct dc_link_status link_status; +> +> struct link_trace link_trace; +> struct gpio *hpd_gpio; +> enum dc_link_fec_state fec_state; +> */ +> } my_dc_link_t; +> +> typedef struct { +> void* plane_state; +> struct dc_stream_state *stream; +> } my_pipe_ctx_t; +> +> struct panel_cntl_backlight_registers { +> unsigned int BL_PWM_CNTL; +> unsigned int BL_PWM_CNTL2; +> unsigned int BL_PWM_PERIOD_CNTL; +> unsigned int LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV; +> }; +> +> struct hw_asic_id { +> uint32_t chip_id; +> uint32_t chip_family; +> uint32_t pci_revision_id; +> uint32_t hw_internal_rev; +> uint32_t vram_type; +> uint32_t vram_width; +> uint32_t feature_flags; +> uint32_t fake_paths_num; +> void *atombios_base_address; +> }; +> +> struct dc_vram_info { +> unsigned int num_chans; +> unsigned int dram_channel_width_bytes; +> }; +> +> struct dc_golden_table { +> uint16_t dc_golden_table_ver; +> uint32_t aux_dphy_rx_control0_val; +> uint32_t aux_dphy_tx_control_val; +> uint32_t aux_dphy_rx_control1_val; +> uint32_t dc_gpio_aux_ctrl_0_val; +> uint32_t dc_gpio_aux_ctrl_1_val; +> uint32_t dc_gpio_aux_ctrl_2_val; +> uint32_t dc_gpio_aux_ctrl_3_val; +> uint32_t dc_gpio_aux_ctrl_4_val; +> uint32_t dc_gpio_aux_ctrl_5_val; +> }; +> +> struct dc_firmware_info { +> struct pll_info { +> uint32_t crystal_frequency; /* in KHz */ +> uint32_t min_input_pxl_clk_pll_frequency; /* in KHz */ +> uint32_t max_input_pxl_clk_pll_frequency; /* in KHz */ +> uint32_t min_output_pxl_clk_pll_frequency; /* in KHz */ +> uint32_t max_output_pxl_clk_pll_frequency; /* in KHz */ +> } pll_info; +> +> struct firmware_feature { +> uint32_t memory_clk_ss_percentage; +> uint32_t engine_clk_ss_percentage; +> } feature; +> +> uint32_t default_display_engine_pll_frequency; /* in KHz */ +> uint32_t external_clock_source_frequency_for_dp; /* in KHz */ +> uint32_t smu_gpu_pll_output_freq; /* in KHz */ +> uint8_t min_allowed_bl_level; +> uint8_t remote_display_config; +> uint32_t default_memory_clk; /* in KHz */ +> uint32_t default_engine_clk; /* in KHz */ +> uint32_t dp_phy_ref_clk; /* in KHz - DCE12 only */ +> uint32_t i2c_engine_ref_clk; /* in KHz - DCE12 only */ +> bool oem_i2c_present; +> uint8_t oem_i2c_obj_id; +> +> }; +> +> #define NUMBER_OF_UCHAR_FOR_GUID 16 +> #define MAX_NUMBER_OF_EXT_DISPLAY_PATH 7 +> #define NUMBER_OF_CSR_M3_ARB 10 +> #define NUMBER_OF_DISP_CLK_VOLTAGE 4 +> #define NUMBER_OF_AVAILABLE_SCLK 5 +> +> struct i2c_reg_info { +> unsigned char i2c_reg_index; +> unsigned char i2c_reg_val; +> }; +619,621c784,1386 +< // Default merge as is +< props->setObject(name, value); +< DBGLOG("rad", "prop %s was merged", name); +--- +> struct edp_info { +> uint16_t edp_backlight_pwm_hz; +> uint16_t edp_ss_percentage; +> uint16_t edp_ss_rate_10hz; +> uint8_t edp_pwr_on_off_delay; +> uint8_t edp_pwr_on_vary_bl_to_blon; +> uint8_t edp_pwr_down_bloff_to_vary_bloff; +> uint8_t edp_panel_bpc; +> uint8_t edp_bootup_bl_level; +> }; +> +> struct integrated_info { +> struct clock_voltage_caps { +> /* The Voltage Index indicated by FUSE, same voltage index +> shared with SCLK DPM fuse table */ +> uint32_t voltage_index; +> /* Maximum clock supported with specified voltage index */ +> uint32_t max_supported_clk; /* in KHz */ +> } disp_clk_voltage[NUMBER_OF_DISP_CLK_VOLTAGE]; +> +> struct display_connection_info { +> struct external_display_path { +> /* A bit vector to show what devices are supported */ +> uint32_t device_tag; +> /* 16bit device ACPI id. */ +> uint32_t device_acpi_enum; +> /* A physical connector for displays to plug in, +> using object connector definitions */ +> struct graphics_object_id device_connector_id; +> /* An index into external AUX/DDC channel LUT */ +> uint8_t ext_aux_ddc_lut_index; +> /* An index into external HPD pin LUT */ +> uint8_t ext_hpd_pin_lut_index; +> /* external encoder object id */ +> struct graphics_object_id ext_encoder_obj_id; +> /* XBAR mapping of the PHY channels */ +> union ddi_channel_mapping channel_mapping; +> +> unsigned short caps; +> } path[MAX_NUMBER_OF_EXT_DISPLAY_PATH]; +> +> uint8_t gu_id[NUMBER_OF_UCHAR_FOR_GUID]; +> uint8_t checksum; +> } ext_disp_conn_info; /* exiting long long time */ +> +> struct available_s_clk_list { +> /* Maximum clock supported with specified voltage index */ +> uint32_t supported_s_clk; /* in KHz */ +> /* The Voltage Index indicated by FUSE for specified SCLK */ +> uint32_t voltage_index; +> /* The Voltage ID indicated by FUSE for specified SCLK */ +> uint32_t voltage_id; +> } avail_s_clk[NUMBER_OF_AVAILABLE_SCLK]; +> +> uint8_t memory_type; +> uint8_t ma_channel_number; +> uint32_t boot_up_engine_clock; /* in KHz */ +> uint32_t dentist_vco_freq; /* in KHz */ +> uint32_t boot_up_uma_clock; /* in KHz */ +> uint32_t boot_up_req_display_vector; +> uint32_t other_display_misc; +> uint32_t gpu_cap_info; +> uint32_t sb_mmio_base_addr; +> uint32_t system_config; +> uint32_t cpu_cap_info; +> uint32_t max_nb_voltage; +> uint32_t min_nb_voltage; +> uint32_t boot_up_nb_voltage; +> uint32_t ext_disp_conn_info_offset; +> uint32_t csr_m3_arb_cntl_default[NUMBER_OF_CSR_M3_ARB]; +> uint32_t csr_m3_arb_cntl_uvd[NUMBER_OF_CSR_M3_ARB]; +> uint32_t csr_m3_arb_cntl_fs3d[NUMBER_OF_CSR_M3_ARB]; +> uint32_t gmc_restore_reset_time; +> uint32_t minimum_n_clk; +> uint32_t idle_n_clk; +> uint32_t ddr_dll_power_up_time; +> uint32_t ddr_pll_power_up_time; +> /* start for V6 */ +> uint32_t pcie_clk_ss_type; +> uint32_t lvds_ss_percentage; +> uint32_t lvds_sspread_rate_in_10hz; +> uint32_t hdmi_ss_percentage; +> uint32_t hdmi_sspread_rate_in_10hz; +> uint32_t dvi_ss_percentage; +> uint32_t dvi_sspread_rate_in_10_hz; +> uint32_t sclk_dpm_boost_margin; +> uint32_t sclk_dpm_throttle_margin; +> uint32_t sclk_dpm_tdp_limit_pg; +> uint32_t sclk_dpm_tdp_limit_boost; +> uint32_t boost_engine_clock; +> uint32_t boost_vid_2bit; +> uint32_t enable_boost; +> uint32_t gnb_tdp_limit; +> /* Start from V7 */ +> uint32_t max_lvds_pclk_freq_in_single_link; +> uint32_t lvds_misc; +> uint32_t lvds_pwr_on_seq_dig_on_to_de_in_4ms; +> uint32_t lvds_pwr_on_seq_de_to_vary_bl_in_4ms; +> uint32_t lvds_pwr_off_seq_vary_bl_to_de_in4ms; +> uint32_t lvds_pwr_off_seq_de_to_dig_on_in4ms; +> uint32_t lvds_off_to_on_delay_in_4ms; +> uint32_t lvds_pwr_on_seq_vary_bl_to_blon_in_4ms; +> uint32_t lvds_pwr_off_seq_blon_to_vary_bl_in_4ms; +> uint32_t lvds_reserved1; +> uint32_t lvds_bit_depth_control_val; +> //Start from V9 +> unsigned char dp0_ext_hdmi_slv_addr; +> unsigned char dp0_ext_hdmi_reg_num; +> struct i2c_reg_info dp0_ext_hdmi_reg_settings[9]; +> unsigned char dp0_ext_hdmi_6g_reg_num; +> struct i2c_reg_info dp0_ext_hdmi_6g_reg_settings[3]; +> unsigned char dp1_ext_hdmi_slv_addr; +> unsigned char dp1_ext_hdmi_reg_num; +> struct i2c_reg_info dp1_ext_hdmi_reg_settings[9]; +> unsigned char dp1_ext_hdmi_6g_reg_num; +> struct i2c_reg_info dp1_ext_hdmi_6g_reg_settings[3]; +> unsigned char dp2_ext_hdmi_slv_addr; +> unsigned char dp2_ext_hdmi_reg_num; +> struct i2c_reg_info dp2_ext_hdmi_reg_settings[9]; +> unsigned char dp2_ext_hdmi_6g_reg_num; +> struct i2c_reg_info dp2_ext_hdmi_6g_reg_settings[3]; +> unsigned char dp3_ext_hdmi_slv_addr; +> unsigned char dp3_ext_hdmi_reg_num; +> struct i2c_reg_info dp3_ext_hdmi_reg_settings[9]; +> unsigned char dp3_ext_hdmi_6g_reg_num; +> struct i2c_reg_info dp3_ext_hdmi_6g_reg_settings[3]; +> /* V11 */ +> uint32_t dp_ss_control; +> /* V2.1 */ +> struct edp_info edp1_info; +> struct edp_info edp2_info; +> }; +> +> struct dc_bios { +> void *funcs; +> +> uint8_t *bios; +> uint32_t bios_size; +> +> uint8_t *bios_local_image; +> +> void *ctx; +> void *regs; +> struct integrated_info *integrated_info; +> struct dc_firmware_info fw_info; +> bool fw_info_valid; +> struct dc_vram_info vram_info; +> struct dc_golden_table golden_table; +> }; +> +> struct dmcu_version { +> unsigned int interface_version; +> unsigned int abm_version; +> unsigned int psr_version; +> unsigned int build_version; +> }; +> +> struct dc_versions { +> const char *dc_ver; +> struct dmcu_version dmcu_version; +> }; +> +> #define MAX_PLANES 6 +> +> struct dc_plane_cap { +> uint32_t type; +> uint32_t blends_with_above : 1; +> uint32_t blends_with_below : 1; +> uint32_t per_pixel_alpha : 1; +> struct { +> uint32_t argb8888 : 1; +> uint32_t nv12 : 1; +> uint32_t fp16 : 1; +> uint32_t p010 : 1; +> uint32_t ayuv : 1; +> } pixel_format_support; +> // max upscaling factor x1000 +> // upscaling factors are always >= 1 +> // for example, 1080p -> 8K is 4.0, or 4000 raw value +> struct { +> uint32_t argb8888; +> uint32_t nv12; +> uint32_t fp16; +> } max_upscale_factor; +> // max downscale factor x1000 +> // downscale factors are always <= 1 +> // for example, 8K -> 1080p is 0.25, or 250 raw value +> struct { +> uint32_t argb8888; +> uint32_t nv12; +> uint32_t fp16; +> } max_downscale_factor; +> // minimal width/height +> uint32_t min_width; +> uint32_t min_height; +> }; +> +> struct rom_curve_caps { +> uint16_t srgb : 1; +> uint16_t bt2020 : 1; +> uint16_t gamma2_2 : 1; +> uint16_t pq : 1; +> uint16_t hlg : 1; +> }; +> +> struct dpp_color_caps { +> uint16_t dcn_arch : 1; // all DCE generations treated the same +> // input lut is different than most LUTs, just plain 256-entry lookup +> uint16_t input_lut_shared : 1; // shared with DGAM +> uint16_t icsc : 1; +> uint16_t dgam_ram : 1; +> uint16_t post_csc : 1; // before gamut remap +> uint16_t gamma_corr : 1; +> +> // hdr_mult and gamut remap always available in DPP (in that order) +> // 3d lut implies shaper LUT, +> // it may be shared with MPC - check MPC:shared_3d_lut flag +> uint16_t hw_3d_lut : 1; +> uint16_t ogam_ram : 1; // blnd gam +> uint16_t ocsc : 1; +> uint16_t dgam_rom_for_yuv : 1; +> struct rom_curve_caps dgam_rom_caps; +> struct rom_curve_caps ogam_rom_caps; +> }; +> +> struct mpc_color_caps { +> uint16_t gamut_remap : 1; +> uint16_t ogam_ram : 1; +> uint16_t ocsc : 1; +> uint16_t num_3dluts : 3; //3d lut always assumes a preceding shaper LUT +> uint16_t shared_3d_lut:1; //can be in either DPP or MPC, but single instance +> +> struct rom_curve_caps ogam_rom_caps; +> }; +> +> struct dc_color_caps { +> struct dpp_color_caps dpp; +> struct mpc_color_caps mpc; +> }; +> +> struct dc_caps { +> uint32_t max_streams; +> uint32_t max_links; +> uint32_t max_audios; +> uint32_t max_slave_planes; +> uint32_t max_slave_yuv_planes; +> uint32_t max_slave_rgb_planes; +> uint32_t max_planes; +> uint32_t max_downscale_ratio; +> uint32_t i2c_speed_in_khz; +> uint32_t i2c_speed_in_khz_hdcp; +> uint32_t dmdata_alloc_size; +> unsigned int max_cursor_size; +> unsigned int max_video_width; +> unsigned int min_horizontal_blanking_period; +> int linear_pitch_alignment; +> bool dcc_const_color; +> bool dynamic_audio; +> bool is_apu; +> bool dual_link_dvi; +> bool post_blend_color_processing; +> bool force_dp_tps4_for_cp2520; +> bool disable_dp_clk_share; +> bool psp_setup_panel_mode; +> bool extended_aux_timeout_support; +> bool dmcub_support; +> uint32_t num_of_internal_disp; +> uint32_t max_dp_protocol_version; +> unsigned int mall_size_per_mem_channel; +> unsigned int mall_size_total; +> unsigned int cursor_cache_size; +> struct dc_plane_cap planes[MAX_PLANES]; +> struct dc_color_caps color; +> bool vbios_lttpr_aware; +> bool vbios_lttpr_enable; +> }; +> +> struct dc_cap_funcs { +> bool (*get_dcc_compression_cap)(const struct dc *dc, +> const struct dc_dcc_surface_param *input, +> struct dc_surface_dcc_cap *output); +> }; +> +> struct dc_config { +> bool gpu_vm_support; +> bool disable_disp_pll_sharing; +> bool fbc_support; +> bool disable_fractional_pwm; +> bool allow_seamless_boot_optimization; +> bool power_down_display_on_boot; +> bool edp_not_connected; +> bool edp_no_power_sequencing; +> bool force_enum_edp; +> bool forced_clocks; +> bool allow_lttpr_non_transparent_mode; +> bool multi_mon_pp_mclk_switch; +> bool disable_dmcu; +> bool enable_4to1MPC; +> bool allow_edp_hotplug_detection; +> //#if defined(CONFIG_DRM_AMD_DC_DCN) +> // bool clamp_min_dcfclk; +> //#endif +> uint64_t vblank_alignment_dto_params; +> uint8_t vblank_alignment_max_frame_time_diff; +> bool is_asymmetric_memory; +> bool is_single_rank_dimm; +> }; +> +> struct dc_bw_validation_profile { +> bool enable; +> +> unsigned long long total_ticks; +> unsigned long long voltage_level_ticks; +> unsigned long long watermark_ticks; +> unsigned long long rq_dlg_ticks; +> +> unsigned long long total_count; +> unsigned long long skip_fast_count; +> unsigned long long skip_pass_count; +> unsigned long long skip_fail_count; +> }; +> +> union mem_low_power_enable_options { +> struct { +> bool vga: 1; +> bool i2c: 1; +> bool dmcu: 1; +> bool dscl: 1; +> bool cm: 1; +> bool mpc: 1; +> bool optc: 1; +> } bits; +> uint32_t u32All; +> }; +> +> struct dc_debug_options { +> uint32_t visual_confirm; +> bool sanity_checks; +> bool max_disp_clk; +> bool surface_trace; +> bool timing_trace; +> bool clock_trace; +> bool validation_trace; +> bool bandwidth_calcs_trace; +> int max_downscale_src_width; +> +> /* stutter efficiency related */ +> bool disable_stutter; +> bool use_max_lb; +> uint32_t disable_dcc; +> uint32_t pipe_split_policy; +> bool force_single_disp_pipe_split; +> bool voltage_align_fclk; +> bool disable_min_fclk; +> +> bool disable_dfs_bypass; +> bool disable_dpp_power_gate; +> bool disable_hubp_power_gate; +> bool disable_dsc_power_gate; +> int dsc_min_slice_height_override; +> int dsc_bpp_increment_div; +> bool native422_support; +> bool disable_pplib_wm_range; +> uint32_t pplib_wm_report_mode; +> unsigned int min_disp_clk_khz; +> unsigned int min_dpp_clk_khz; +> int sr_exit_time_dpm0_ns; +> int sr_enter_plus_exit_time_dpm0_ns; +> int sr_exit_time_ns; +> int sr_enter_plus_exit_time_ns; +> int urgent_latency_ns; +> uint32_t underflow_assert_delay_us; +> int percent_of_ideal_drambw; +> int dram_clock_change_latency_ns; +> bool optimized_watermark; +> int always_scale; +> bool disable_pplib_clock_request; +> bool disable_clock_gate; +> bool disable_mem_low_power; +> //#if defined(CONFIG_DRM_AMD_DC_DCN) +> // bool pstate_enabled; +> //#endif +> bool disable_dmcu; +> bool disable_psr; +> bool force_abm_enable; +> bool disable_stereo_support; +> bool vsr_support; +> bool performance_trace; +> bool az_endpoint_mute_only; +> bool always_use_regamma; +> bool recovery_enabled; +> bool avoid_vbios_exec_table; +> bool scl_reset_length10; +> bool hdmi20_disable; +> bool skip_detection_link_training; +> uint32_t edid_read_retry_times; +> bool remove_disconnect_edp; +> unsigned int force_odm_combine; //bit vector based on otg inst +> //#if defined(CONFIG_DRM_AMD_DC_DCN) +> // unsigned int force_odm_combine_4to1; //bit vector based on otg inst +> // bool disable_z9_mpc; +> //#endif +> unsigned int force_fclk_khz; +> bool enable_tri_buf; +> bool dmub_offload_enabled; +> bool dmcub_emulation; +> //#if defined(CONFIG_DRM_AMD_DC_DCN) +> // bool disable_idle_power_optimizations; +> // unsigned int mall_size_override; +> // unsigned int mall_additional_timer_percent; +> // bool mall_error_as_fatal; +> //#endif +> bool dmub_command_table; /* for testing only */ +> struct dc_bw_validation_profile bw_val_profile; +> bool disable_fec; +> bool disable_48mhz_pwrdwn; +> /* This forces a hard min on the DCFCLK requested to SMU/PP +> * watermarks are not affected. +> */ +> unsigned int force_min_dcfclk_mhz; +> //#if defined(CONFIG_DRM_AMD_DC_DCN) +> // int dwb_fi_phase; +> //#endif +> bool disable_timing_sync; +> bool cm_in_bypass; +> int force_clock_mode;/*every mode change.*/ +> +> bool disable_dram_clock_change_vactive_support; +> bool validate_dml_output; +> bool enable_dmcub_surface_flip; +> bool usbc_combo_phy_reset_wa; +> bool disable_dsc; +> bool enable_dram_clock_change_one_display_vactive; +> union mem_low_power_enable_options enable_mem_low_power; +> bool force_vblank_alignment; +> +> /* Enable dmub aux for legacy ddc */ +> bool enable_dmub_aux_for_legacy_ddc; +> bool optimize_edp_link_rate; /* eDP ILR */ +> /* force enable edp FEC */ +> bool force_enable_edp_fec; +> /* FEC/PSR1 sequence enable delay in 100us */ +> uint8_t fec_enable_delay_in100us; +> //#if defined(CONFIG_DRM_AMD_DC_DCN) +> // bool disable_z10; +> // bool enable_sw_cntl_psr; +> //#endif +> }; +> +> struct dc_bounding_box_overrides { +> int sr_exit_time_ns; +> int sr_enter_plus_exit_time_ns; +> int urgent_latency_ns; +> int percent_of_ideal_drambw; +> int dram_clock_change_latency_ns; +> int dummy_clock_change_latency_ns; +> /* This forces a hard min on the DCFCLK we use +> * for DML. Unlike the debug option for forcing +> * DCFCLK, this override affects watermark calculations +> */ +> int min_dcfclk_mhz; +> }; +> +> struct dc_bug_wa { +> bool no_connect_phy_config; +> bool dedcn20_305_wa; +> bool skip_clock_update; +> bool lt_early_cr_pattern; +> }; +> +> struct dc { +> struct dc_versions versions; +> struct dc_caps caps; +> struct dc_cap_funcs cap_funcs; +> struct dc_config config; +> struct dc_debug_options debug; +> struct dc_bounding_box_overrides bb_overrides; +> struct dc_bug_wa work_arounds; +> /* +> struct dc_context *ctx; +> struct dc_phy_addr_space_config vm_pa_config; +> +> uint8_t link_count; +> struct dc_link *links[MAX_PIPES * 2]; +> +> struct dc_state *current_state; +> struct resource_pool *res_pool; +> +> struct clk_mgr *clk_mgr; +> +> // Display Engine Clock levels +> struct dm_pp_clock_levels sclk_lvls; +> +> // Inputs into BW and WM calculations. +> struct bw_calcs_dceip *bw_dceip; +> struct bw_calcs_vbios *bw_vbios; +> #ifdef CONFIG_DRM_AMD_DC_DCN +> struct dcn_soc_bounding_box *dcn_soc; +> struct dcn_ip_params *dcn_ip; +> struct display_mode_lib dml; +> #endif +> +> // HW functions +> struct hw_sequencer_funcs hwss; +> struct dce_hwseq *hwseq; +> +> // Require to optimize clocks and bandwidth for added/removed planes +> bool optimized_required; +> bool wm_optimized_required; +> #if defined(CONFIG_DRM_AMD_DC_DCN) +> bool idle_optimizations_allowed; +> #endif +> +> // Require to maintain clocks and bandwidth for UEFI enabled HW +> +> // FBC compressor +> struct compressor *fbc_compressor; +> +> struct dc_debug_data debug_data; +> struct dpcd_vendor_signature vendor_signature; +> +> const char *build_id; +> struct vm_helper *vm_helper; +> */ +> }; +> +> struct dc_context { +> struct dc *dc; +> +> void *driver_context; /* e.g. amdgpu_device */ +> void *perf_trace; +> void *cgs_device; +> +> uint32_t dce_environment; +> struct hw_asic_id asic_id; +> +> /* todo: below should probably move to dc. to facilitate removal +> * of AS we will store these here +> */ +> uint32_t dce_version; +> struct dc_bios *dc_bios; +> bool created_bios; +> void *gpio_service; +> uint32_t dc_sink_id_count; +> uint32_t dc_stream_id_count; +> uint32_t dc_edp_id_count; +> uint64_t fbc_gpu_addr; +> void *dmub_srv; +> }; +> +> struct panel_cntl_funcs { +> void (*destroy)(struct panel_cntl **panel_cntl); +> uint32_t (*hw_init)(struct panel_cntl *panel_cntl); +> bool (*is_panel_backlight_on)(struct panel_cntl *panel_cntl); +> bool (*is_panel_powered_on)(struct panel_cntl *panel_cntl); +> void (*store_backlight_level)(struct panel_cntl *panel_cntl); +> void (*driver_set_backlight)(struct panel_cntl *panel_cntl, +> uint32_t backlight_pwm_u16_16); +> uint32_t (*get_current_backlight)(struct panel_cntl *panel_cntl); +> }; +> struct panel_cntl { +> const struct panel_cntl_funcs *funcs; +> struct dc_context *ctx; +> uint32_t inst; +> /* registers setting needs to be saved and restored at InitBacklight */ +> struct panel_cntl_backlight_registers stored_backlight_registers; +> }; +> bool g_is_sleep = false; +> uint64_t g_last_sleep_time = 0; +> static my_dc_link_t* g_dc_link_ptr = NULL; +> static mach_vm_address_t orig_dce110_edp_backlight_control; +> +> static mach_vm_address_t orig_dce110_edp_power_control; +> static void wrap_dce110_edp_power_control(my_dc_link_t *dc_link, bool power_up) { +> g_dc_link_ptr = dc_link; +> if (orig_dce110_edp_power_control) { +> FunctionCast(wrap_dce110_edp_power_control, orig_dce110_edp_power_control)(dc_link, power_up); +> } +> SYSLOG("igfx", "wrap_dce110_edp_power_control %d end - %p", power_up, g_dc_link_ptr); +> } +> +> static char wrap_dce110_edp_backlight_control(my_dc_link_t *that, uint8_t a2) { +> //SYSLOG("igfx", "wrap_dce110_edp_backlight_control start %p:%lu,%lu,%lu,%lu,%lu", that, on_or_off, a3, a4, a5, a6); +> g_dc_link_ptr = that; +> //if (g_dc_link_ptr && g_dc_link_ptr->dc) { +> // SYSLOG("igfx", "dce110_edp_backlight_control disable_fractional_pwm: %u", g_dc_link_ptr->dc->config.disable_fractional_pwm); +> //} +> char ret = FunctionCast(wrap_dce110_edp_backlight_control, orig_dce110_edp_backlight_control)(that, a2); +> SYSLOG("igfx", "wrap_dce110_edp_backlight_control end - %p, %d", g_dc_link_ptr, ret); +> +> if (g_is_sleep) { +> g_is_sleep = false; +> +> if (g_dc_link_ptr) { +> wrap_dce110_edp_backlight_control(g_dc_link_ptr, false); +> wrap_dce110_edp_power_control(g_dc_link_ptr, false); +> IOSleep(200); +> wrap_dce110_edp_power_control(g_dc_link_ptr, true); +> wrap_dce110_edp_backlight_control(g_dc_link_ptr, true); +> } +> } +> +> return ret; +624,643c1389,1463 +< void RAD::mergeProperties(OSDictionary *props, const char *prefix, IOService *provider) { +< // Should be ok, but in case there are issues switch to dictionaryWithProperties(); +< auto dict = provider->getPropertyTable(); +< if (dict) { +< auto iterator = OSCollectionIterator::withCollection(dict); +< if (iterator) { +< OSSymbol *propname; +< size_t prefixlen = strlen(prefix); +< while ((propname = OSDynamicCast(OSSymbol, iterator->getNextObject())) != nullptr) { +< auto name = propname->getCStringNoCopy(); +< if (name && propname->getLength() > prefixlen && !strncmp(name, prefix, prefixlen)) { +< auto prop = dict->getObject(propname); +< if (prop) +< mergeProperty(props, name + prefixlen, prop); +< else +< DBGLOG("rad", "prop %s was not merged due to no value", name); +< } else { +< //DBGLOG("rad", "prop %s does not match %s prefix", safeString(name), prefix); +< } +< } +--- +> IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t value) { +> IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute)(framebuffer, connectIndex, attribute, value); +> if (attribute != (UInt32)'bklt') { +> return ret; +> } +> +> if (callbackRAD->maxPwmBacklightLvl == 0) { +> DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute zero maxPwmBacklightLvl"); +> return 0; +> } +> +> if (callbackRAD->panelCntlPtr == nullptr) { +> DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null panel cntl"); +> return 0; +> } +> +> if (callbackRAD->orgDceDriverSetBacklight == nullptr) { +> DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null orgDcLinkSetBacklightLevel"); +> return 0; +> } +> +> // set the backlight of AMD navi10 driver +> callbackRAD->curPwmBacklightLvl = (uint32_t)value; +> uint32_t btlper = callbackRAD->curPwmBacklightLvl*100.0 / callbackRAD->maxPwmBacklightLvl; +> if (btlper < 0) { +> btlper = 0; +> } else if (btlper > 100) { +> btlper = 100; +> } +> +> int pwmval = (int)((btlper / 100.0) * 0xFF) << 8; +> if (pwmval >= 0xFF00) { +> // This is from the dmcu_set_backlight_level function of Linux source +> // ... +> // if (backlight_pwm_u16_16 & 0x10000) +> // backlight_8_bit = 0xFF; +> // else +> // backlight_8_bit = (backlight_pwm_u16_16 >> 8) & 0xFF; +> // ... +> // The max brightness should have 0x10000 bit set +> pwmval = 0x1FF00; +> } +> +> /*if (g_dc_link_ptr) { +> __int64 v10 = 0; +> __int64 v9 = *(_QWORD *)(*(_QWORD *)((__int64)g_dc_link_ptr + 304) + 944LL); +> while ( true ) { +> __int64 stream = *(_QWORD *)(v9 + v10 + 496); +> if ( stream ) { +> if (*(_QWORD *)(stream + 8) == (__int64)g_dc_link_ptr) { +> __int64 pipe_ctx = (v9 + v10 + 488); +> //SYSLOG("igfx", "my_set_backlight_lvl %p:%p, %d, %d", g_dc_link_ptr, pipe_ctx, backlight_pwm_u16_16, ramp); +> _QWORD *a1 = (_QWORD *)pipe_ctx; +> __int64 v5 = *(_QWORD *)(a1[1] + 8LL); +> __int64 v6 = *(_QWORD *)(v5 + 320); +> struct panel_cntl *panel_cntl = (struct panel_cntl *)v6; +> SYSLOG("igfx", "panel_cntl: %p", panel_cntl); +> break; +> } else { +> SYSLOG("igfx", "steam link %lu != %p", *(_QWORD *)(stream + 8), g_dc_link_ptr); +> } +> } +> v10 += 1280LL; +> if ( v10 >= 7680 ) +> break; +> } +> } else { +> DBGLOG("igfx", "null g_dc_link_ptr"); +> }*/ +> +> struct panel_cntl* ppc = (struct panel_cntl*)callbackRAD->panelCntlPtr; +> DBGLOG("igfx", "fb 0x%p, idx: %d, set brightness: 0x%p -> 0x%x, st: %d", framebuffer, connectIndex, callbackRAD->panelCntlPtr, pwmval, ppc->funcs->is_panel_powered_on(ppc)); +> callbackRAD->orgDceDriverSetBacklight(callbackRAD->panelCntlPtr, pwmval); +> return 0; +> } +645,651c1465,1478 +< iterator->release(); +< } else { +< SYSLOG("rad", "prop merge failed to iterate over properties"); +< } +< } else { +< SYSLOG("rad", "prop merge failed to get properties"); +< } +--- +> uint32_t RAD::wrapDcePanelCntlHwInit(void *panel_cntl) { +> callbackRAD->panelCntlPtr = panel_cntl; +> callbackRAD->updatePwmMaxBrightnessFromInternalDisplay(); // read max brightness value from IOReg +> struct panel_cntl* ppc = (struct panel_cntl*)callbackRAD->panelCntlPtr; +> DBGLOG("igfx", "wrapDcePanelCntlHwInit: %p, bl: %d, pw: %d, bl: %d", panel_cntl, ppc->funcs->is_panel_backlight_on(ppc), ppc->funcs->is_panel_powered_on(ppc), ppc->funcs->get_current_backlight(ppc)); +> uint32_t ret = FunctionCast(wrapDcePanelCntlHwInit, callbackRAD->orgDcePanelCntlHwInit)(panel_cntl); +> //if (ppc->funcs->is_panel_backlight_on(ppc) == 0 && ppc->funcs->is_panel_powered_on(ppc) == 1 && g_dc_link_ptr) { +> //wrap_dce110_edp_backlight_control(g_dc_link_ptr, 0); +> //wrap_dce110_edp_power_control(g_dc_link_ptr, false); +> // g_is_sleep = true; +> //} +> DBGLOG("igfx", "wrapDcePanelCntlHwInit: %p - %u, st: %d", panel_cntl, ret, ppc->funcs->is_panel_powered_on(ppc)); +> return ret; +> } +653,664c1480,1520 +< if (!strcmp(prefix, "CAIL,")) { +< for (size_t i = 0; i < arrsize(powerGatingFlags); i++) { +< if (powerGatingFlags[i] && props->getObject(powerGatingFlags[i])) { +< DBGLOG("rad", "cail prop merge found %s, replacing", powerGatingFlags[i]); +< auto num = OSNumber::withNumber(1, 32); +< if (num) { +< props->setObject(powerGatingFlags[i], num); +< num->release(); +< } +< } +< } +< } +--- +> IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t * value) { +> IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute)(framebuffer, connectIndex, attribute, value); +> if (attribute == (UInt32)'bklt') { +> // enable the backlight feature of AMD navi10 driver +> *value = callbackRAD->curPwmBacklightLvl; +> ret = 0; +> } +> return ret; +> } +> +> static mach_vm_address_t Orig_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts; +> static int64_t Wrap_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts(__int64 a1, __int64 a2) { +> SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdInterruptManager_sleepInterrupts"" start " "%lu:%lu", a1, a2); +> uint64_t currNs = getCurrentTimeNs(); +> if (currNs - g_last_sleep_time >= 1000000000) { +> g_is_sleep = true; +> g_last_sleep_time = getCurrentTimeNs(); +> } +> int64_t ret = FunctionCast(Wrap_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts, Orig_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts)(a1, a2); +> SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdInterruptManager_sleepInterrupts"" end - " "%lu", ret); +> return ret; +> } +> +> static mach_vm_address_t Orig_AMDRadeonX6000_AmdLogger_setDebugLevel; +> static int64_t Wrap_AMDRadeonX6000_AmdLogger_setDebugLevel(__int64 a1, int a2) { +> SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdLogger_setDebugLevel"" start " "%lu:%d", a1, a2); +> a2 = 9999; +> int64_t ret = FunctionCast(Wrap_AMDRadeonX6000_AmdLogger_setDebugLevel, Orig_AMDRadeonX6000_AmdLogger_setDebugLevel)(a1, a2); +> SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdLogger_setDebugLevel"" end - " "%lu", ret); +> return ret; +> } +> +> static mach_vm_address_t Orig_my_dm_logger_write; +> static char g_buffer[81920] = {0}; +> static int64_t Wrap_my_dm_logger_write(int64_t a1, unsigned int a2, const char *a3, ...) { +> va_list args; +> va_start(args, a3); +> vsnprintf(g_buffer, 8192, a3, args); +> SYSLOG("igfx", "dm log, type: %d, msg: %s", a2, g_buffer); +> va_end(args); +> return 0; +667,673c1523,1752 +< void RAD::applyPropertyFixes(IOService *service, uint32_t connectorNum) { +< if (service && getKernelVersion() >= KernelVersion::HighSierra) { +< // Starting with 10.13.2 this is important to fix sleep issues due to enforced 6 screens +< if (!service->getProperty("CFG,CFG_FB_LIMIT")) { +< DBGLOG("rad", "setting fb limit to %u", connectorNum); +< service->setProperty("CFG_FB_LIMIT", connectorNum, 32); +< } +--- +> bool RAD::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { +> if (kextRadeonX6000Framebuffer.loadIndex == index) { +> SYSLOG("igfx", "RAD::processKext, index: %lu, address: %p, size: %lu", index, address, size); +> if (getKernelVersion() >= KernelVersion::Monterey) { +> KernelPatcher::RouteRequest requests[] = { +> {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25setAttributeForConnectionEijm", wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute}, +> {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25getAttributeForConnectionEijPm", wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute}, +> //{"__ZN24AMDRadeonX6000_AmdLogger13setDebugLevelE11LogSeverity", Wrap_AMDRadeonX6000_AmdLogger_setDebugLevel, Orig_AMDRadeonX6000_AmdLogger_setDebugLevel}, +> //{"__ZN34AMDRadeonX6000_AmdInterruptManager15sleepInterruptsEv", Wrap_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts, Orig_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts}, +> }; +> +> if (!patcher.routeMultiple(index, requests, address, size, true, true)) +> SYSLOG("igfx", "Failed to route redeon x6000 gpu tracing."); +> +> SYSLOG("igfx", "route _dce_panel_cntl_hw_init"); +> mach_vm_address_t dpchi_cal = address + 0x12CB94; // For Monterey +> //if (getKernelVersion() >= KernelVersion::Ventura) { +> // dpchi_cal = address + 0x12EC5F; // For Ventura +> //} +> +> // Verify the expect code of the destinate address, otherwise will not call to prevent kernel panic +> const uint8_t dpchi_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x50, 0x49, 0x89, 0xFD, 0x4C, 0x8D, 0x45 }; +> unsigned char* dpchi_cal_charp = (unsigned char*)dpchi_cal; +> bool expect_match = true; +> for (int i = 0; i < 20; i++) { +> //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); +> if (dpchi_cal_charp[i] != dpchi_expect[i]) { +> SYSLOG("igfx", "_dce_panel_cntl_hw_init dismatch: %d => %x != %x", i, dpchi_cal_charp[i], dpchi_expect[i]); +> expect_match = false; +> break; +> } +> } +> +> if (expect_match) { +> orgDcePanelCntlHwInit = patcher.routeFunction(dpchi_cal, reinterpret_cast(wrapDcePanelCntlHwInit), true); +> if (patcher.getError() == KernelPatcher::Error::NoError) { +> DBGLOG("igfx", "routed _dce_panel_cntl_hw_init"); +> } else { +> SYSLOG("igfx", "failed to route _dce_panel_cntl_hw_init %d", patcher.getError()); +> patcher.clearError(); +> } +> +> mach_vm_address_t ddsb_cal = address + 0x12CFC9; +> //if (getKernelVersion() >= KernelVersion::Ventura) { +> // ddsb_cal = address + 0x12F094; +> //} +> SYSLOG("igfx", "got Monterey _dce_driver_set_backlight address: %p", ddsb_cal); +> const uint8_t ddsb_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x50, 0x41, 0x89, 0xF7, 0x49, 0x89, 0xFE }; +> unsigned char* ddsb_cal_charp = (unsigned char*)ddsb_cal; +> expect_match = true; +> for (int i = 0; i < 20; i++) { +> //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); +> if (ddsb_cal_charp[i] != ddsb_expect[i]) { +> SYSLOG("igfx", "_dce_driver_set_backlight dismatch: %d => %x != %x", i, ddsb_cal_charp[i], ddsb_expect[i]); +> expect_match = false; +> break; +> } +> } +> +> //for (int i = 0; i < 0x200000; i++) { +> // ddsb_cal_charp = (unsigned char*)(address + i); +> // int j = 0; +> // for (j = 0; j < 20; j++) { +> // //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); +> // if (ddsb_cal_charp[j] != ddsb_expect[j]) { +> // //SYSLOG("igfx", "_dce_panel_cntl_hw_init dismatch: %d => %x != %x", i, dpchi_cal_charp[i], dpchi_expect[i]); +> // //expect_match = false; +> // break; +> // } +> // } +> // if (j == 20) { +> // SYSLOG("igfx", "found address %u", i); +> // } +> //} +> +> if (expect_match) { +> orgDceDriverSetBacklight = reinterpret_cast(ddsb_cal); +> } +> } +> +> /*SYSLOG("igfx", "route _dm_logger_write"); +> mach_vm_address_t dlw_cal = address + 0x169117; +> const uint8_t dlw_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x48, 0x81, 0xEC, 0x88, 0x04, 0x00, 0x00 }; +> unsigned char* dlw_cal_charp = (unsigned char*)dlw_cal; +> expect_match = true; +> for (int i = 0; i < 20; i++) { +> //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); +> if (dlw_cal_charp[i] != dlw_expect[i]) { +> SYSLOG("igfx", "_dm_logger_write dismatch: %d => %x != %x", i, dlw_cal_charp[i], dlw_expect[i]); +> expect_match = false; +> break; +> } +> } +> if (expect_match) { +> Orig_my_dm_logger_write = patcher.routeFunction(dlw_cal, reinterpret_cast(Wrap_my_dm_logger_write), true); +> if (patcher.getError() == KernelPatcher::Error::NoError) { +> DBGLOG("igfx", "routed _dm_logger_write"); +> } else { +> SYSLOG("igfx", "failed to route _dm_logger_write %d", patcher.getError()); +> patcher.clearError(); +> } +> }*/ +> +> /* +> SYSLOG("igfx", "route _dce110_edp_power_control"); +> mach_vm_address_t depc_cal = address + 0x1B04B9; +> const uint8_t depc_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x48, 0x83, 0xEC, 0x48, 0x4C, 0x8B, 0xB7 }; +> unsigned char* depc_cal_charp = (unsigned char*)depc_cal; +> expect_match = true; +> for (int i = 0; i < 20; i++) { +> //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); +> if (depc_cal_charp[i] != depc_expect[i]) { +> SYSLOG("igfx", "_dce110_edp_power_control dismatch: %d => %x != %x", i, depc_cal_charp[i], depc_expect[i]); +> expect_match = false; +> break; +> } +> } +> if (expect_match) { +> orig_dce110_edp_power_control = patcher.routeFunction(depc_cal, reinterpret_cast(wrap_dce110_edp_power_control), true); +> if (patcher.getError() == KernelPatcher::Error::NoError) { +> DBGLOG("igfx", "routed _dce110_edp_power_control"); +> } else { +> SYSLOG("igfx", "failed to route _dce110_edp_power_control %d", patcher.getError()); +> patcher.clearError(); +> } +> } +> +> SYSLOG("igfx", "route _dce110_edp_backlight_control"); +> mach_vm_address_t debc_cal = address + 0x1B0926; +> const uint8_t debc_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x48, 0x83, 0xEC, 0x48, 0x4C, 0x8B, 0xAF }; +> unsigned char* debc_cal_charp = (unsigned char*)debc_cal; +> expect_match = true; +> for (int i = 0; i < 20; i++) { +> //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); +> if (debc_cal_charp[i] != debc_expect[i]) { +> SYSLOG("igfx", "_dce110_edp_backlight_control dismatch: %d => %x != %x", i, debc_cal_charp[i], debc_expect[i]); +> expect_match = false; +> break; +> } +> } +> if (expect_match) { +> orig_dce110_edp_backlight_control = patcher.routeFunction(debc_cal, reinterpret_cast(wrap_dce110_edp_backlight_control), true); +> if (patcher.getError() == KernelPatcher::Error::NoError) { +> DBGLOG("igfx", "routed _dce110_edp_backlight_control"); +> } else { +> SYSLOG("igfx", "failed to route _dce110_edp_backlight_control %d", patcher.getError()); +> patcher.clearError(); +> } +> }*/ +> } else { +> KernelPatcher::RouteRequest requests[] = { +> {"_dce_panel_cntl_hw_init", wrapDcePanelCntlHwInit, orgDcePanelCntlHwInit}, +> {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25setAttributeForConnectionEijm", wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute}, +> {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25getAttributeForConnectionEijPm", wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute}, +> //{"_dc_link_bandwidth_kbps", wrapDcLinkBandwidthKbps, orgDcLinkBandwidthKbps}, +> }; +> +> mach_vm_address_t dpchi_cal = address + 0x124F56; +> unsigned char* dpchi_cal_charp = (unsigned char*)dpchi_cal; +> for (int i = 0; i < 20; i++) { +> SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); +> } +> mach_vm_address_t ddsb_cal = address + 0x124B21; +> unsigned char* ddsb_cal_charp = (unsigned char*)ddsb_cal; +> for (int i = 0; i < 20; i++) { +> SYSLOG("igfx", "%d => %x", i, ddsb_cal_charp[i]); +> } +> +> if (!patcher.routeMultiple(index, requests, address, size, true, true)) +> SYSLOG("igfx", "Failed to route redeon x6000 gpu tracing."); +> +> mach_vm_address_t ddsb = patcher.solveSymbol(index, "_dce_driver_set_backlight"); +> SYSLOG("igfx", "got Bigsur _dce_driver_set_backlight address: %p", ddsb); +> orgDceDriverSetBacklight = reinterpret_cast(ddsb); +> } +> //mach_vm_address_t ddsb_cal = address + 0x124F56; +> //SYSLOG("igfx", "got _dce_driver_set_backlight address: %p, %p", ddsb, ddsb_cal); +> //orgDceDriverSetBacklight = reinterpret_cast(ddsb); +> if (patcher.getError() != KernelPatcher::Error::NoError) { +> SYSLOG("igfx", "failed to resolve _dce_driver_set_backlight"); +> patcher.clearError(); +> return false; +> } +> } +> +> if (kextRadeonFramebuffer.loadIndex == index) { +> if (force24BppMode) +> process24BitOutput(patcher, kextRadeonFramebuffer, address, size); +> return true; +> } +> +> if (kextRadeonLegacyFramebuffer.loadIndex == index) { +> if (force24BppMode) +> process24BitOutput(patcher, kextRadeonLegacyFramebuffer, address, size); +> return true; +> } +> +> if (kextRadeonSupport.loadIndex == index) { +> processConnectorOverrides(patcher, address, size, true); +> +> if (getKernelVersion() > KernelVersion::Mojave || +> (getKernelVersion() == KernelVersion::Mojave && getKernelMinorVersion() >= 5)) { +> KernelPatcher::RouteRequest request("__ZN13ATIController8TestVRAME13PCI_REG_INDEXb", doNotTestVram); +> patcher.routeMultiple(index, &request, 1, address, size); +> } +> +> if (useCustomAgdpDecision) { +> KernelPatcher::RouteRequest request("__ZN16AtiDeviceControl16notifyLinkChangeE31kAGDCRegisterLinkControlEvent_tmj", wrapNotifyLinkChange, orgNotifyLinkChange); +> patcher.routeMultiple(index, &request, 1, address, size); +> } +> +> return true; +> } +> +> if (kextRadeonLegacySupport.loadIndex == index) { +> processConnectorOverrides(patcher, address, size, false); +> return true; +> } +> +> if (kextPolarisController.loadIndex == index) { +> KernelPatcher::RouteRequest request("__ZN17AMD9500Controller23findProjectByPartNumberEP20ControllerProperties", findProjectByPartNumber); +> patcher.routeMultiple(index, &request, 1, address, size); +> } +> +> for (size_t i = 0; i < maxHardwareKexts; i++) { +> if (kextRadeonHardware[i].loadIndex == index) { +> processHardwareKext(patcher, i, address, size); +> return true; +> } +> } +675,677c1754 +< // In the past we set CFG_USE_AGDC to false, which caused visual glitches and broken multimonitor support. +< // A better workaround is to disable AGDP just like we do globally. +< } +--- +> return false; +680,684c1757,1807 +< void RAD::updateConnectorsInfo(void *atomutils, t_getAtomObjectTableForType gettable, IOService *ctrl, RADConnectors::Connector *connectors, uint8_t *sz) { +< if (atomutils) { +< DBGLOG("rad", "getConnectorsInfo found %u connectors", *sz); +< RADConnectors::print(connectors, *sz); +< } +--- +> void RAD::initHardwareKextMods() { +> // Decide on kext amount present for optimal performance. +> // 10.15+ has X4000, X5000, and X6000 +> // 10.14+ has X4000 and X5000 +> // 10.13.4+ has X3000, X4000, and X5000 +> if (getKernelVersion() >= KernelVersion::Catalina) +> maxHardwareKexts = MaxRadeonHardwareCatalina; +> else if (getKernelVersion() >= KernelVersion::Mojave) +> maxHardwareKexts = MaxRadeonHardwareMojave; +> else if (getKernelVersion() == KernelVersion::HighSierra && getKernelMinorVersion() >= 5) +> maxHardwareKexts = MaxRadeonHardwareModernHighSierra; +> +> // 10.13.4 fixed black screen issues +> if (maxHardwareKexts != MaxRadeonHardware) { +> for (size_t i = 0; i < MaxGetFrameBufferProcs; i++) +> getFrameBufferProcNames[IndexRadeonHardwareX4000][i] = nullptr; +> +> // We have nothing to do for these kexts on recent systems +> if (!fixConfigName && !forceOpenGL && !forceCodecInfo) { +> // X4000 kext is not included in this list as we need to fix GVA properties for most of its GPUs +> kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); +> kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); +> } +> } +> +> if (getKernelVersion() < KernelVersion::Catalina) { +> kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); +> } +> +> if (getKernelVersion() < KernelVersion::HighSierra) { +> // Versions before 10.13 do not support X4250 and X5000 +> kextRadeonHardware[IndexRadeonHardwareX4250].switchOff(); +> kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); +> +> // Versions before 10.13 have legacy X3000 and X4000 IDs +> kextRadeonHardware[IndexRadeonHardwareX3000].id = idRadeonX3000Old; +> kextRadeonHardware[IndexRadeonHardwareX4000].id = idRadeonX4000Old; +> +> bool preSierra = getKernelVersion() < KernelVersion::Sierra; +> +> if (preSierra) { +> // Versions before 10.12 do not support X4100 +> kextRadeonHardware[IndexRadeonHardwareX4100].switchOff(); +> } +> +> if (preSierra || (getKernelVersion() == KernelVersion::Sierra && getKernelMinorVersion() < 7)) { +> // Versions before 10.12.6 do not support X4150, X4200 +> kextRadeonHardware[IndexRadeonHardwareX4150].switchOff(); +> kextRadeonHardware[IndexRadeonHardwareX4200].switchOff(); +> } +> } +686,698c1809,1810 +< // Check if the user wants to override automatically detected connectors +< auto cons = ctrl->getProperty("connectors"); +< if (cons) { +< auto consData = OSDynamicCast(OSData, cons); +< if (consData) { +< auto consPtr = consData->getBytesNoCopy(); +< auto consSize = consData->getLength(); +< +< uint32_t consCount; +< if (WIOKit::getOSDataValue(ctrl, "connector-count", consCount)) { +< *sz = consCount; +< DBGLOG("rad", "getConnectorsInfo got size override to %u", *sz); +< } +--- +> lilu.onKextLoadForce(kextRadeonHardware, maxHardwareKexts); +> } +700,721c1812,1847 +< if (consPtr && consSize > 0 && *sz > 0 && RADConnectors::valid(consSize, *sz)) { +< RADConnectors::copy(connectors, *sz, static_cast(consPtr), consSize); +< DBGLOG("rad", "getConnectorsInfo installed %u connectors", *sz); +< applyPropertyFixes(ctrl, *sz); +< } else { +< DBGLOG("rad", "getConnectorsInfo conoverrides have invalid size %u for %u num", consSize, *sz); +< } +< } else { +< DBGLOG("rad", "getConnectorsInfo conoverrides have invalid type"); +< } +< } else { +< if (atomutils) { +< DBGLOG("rad", "getConnectorsInfo attempting to autofix connectors"); +< uint8_t sHeader = 0, displayPathNum = 0, connectorObjectNum = 0; +< auto baseAddr = static_cast(gettable(atomutils, AtomObjectTableType::Common, &sHeader)) - sizeof(uint32_t); +< auto displayPaths = static_cast(gettable(atomutils, AtomObjectTableType::DisplayPath, &displayPathNum)); +< auto connectorObjects = static_cast(gettable(atomutils, AtomObjectTableType::ConnectorObject, &connectorObjectNum)); +< if (displayPathNum == connectorObjectNum) +< autocorrectConnectors(baseAddr, displayPaths, displayPathNum, connectorObjects, connectorObjectNum, connectors, *sz); +< else +< DBGLOG("rad", "getConnectorsInfo found different displaypaths %u and connectors %u", displayPathNum, connectorObjectNum); +< } +--- +> void RAD::process24BitOutput(KernelPatcher &patcher, KernelPatcher::KextInfo &info, mach_vm_address_t address, size_t size) { +> auto bitsPerComponent = patcher.solveSymbol(info.loadIndex, "__ZL18BITS_PER_COMPONENT", address, size); +> if (bitsPerComponent) { +> while (bitsPerComponent && *bitsPerComponent) { +> if (*bitsPerComponent == 10) { +> auto ret = MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock); +> if (ret == KERN_SUCCESS) { +> DBGLOG("rad", "fixing BITS_PER_COMPONENT"); +> *bitsPerComponent = 8; +> MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock); +> } else { +> SYSLOG("rad", "failed to disable write protection for BITS_PER_COMPONENT"); +> } +> } +> bitsPerComponent++; +> } +> } else { +> SYSLOG("rad", "failed to find BITS_PER_COMPONENT"); +> patcher.clearError(); +> } +> +> DBGLOG("rad", "fixing pixel types"); +> +> KernelPatcher::LookupPatch pixelPatch { +> &info, +> reinterpret_cast("--RRRRRRRRRRGGGGGGGGGGBBBBBBBBBB"), +> reinterpret_cast("--------RRRRRRRRGGGGGGGGBBBBBBBB"), +> 32, 2 +> }; +> +> patcher.applyLookupPatch(&pixelPatch); +> if (patcher.getError() != KernelPatcher::Error::NoError) { +> SYSLOG("rad", "failed to patch RGB mask for 24-bit output"); +> patcher.clearError(); +> } +> } +723c1849,1890 +< applyPropertyFixes(ctrl, *sz); +--- +> void RAD::processConnectorOverrides(KernelPatcher &patcher, mach_vm_address_t address, size_t size, bool modern) { +> if (modern) { +> if (getKernelVersion() >= KernelVersion::HighSierra) { +> KernelPatcher::RouteRequest requests[] { +> KernelPatcher::RouteRequest("__ZN14AtiBiosParser116getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), +> KernelPatcher::RouteRequest("__ZN14AtiBiosParser216getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV2, orgGetConnectorsInfoV2), +> KernelPatcher::RouteRequest("__ZN14AtiBiosParser126translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V117AtomConnectorInfoER13ConnectorInfo", +> wrapTranslateAtomConnectorInfoV1, orgTranslateAtomConnectorInfoV1), +> KernelPatcher::RouteRequest("__ZN14AtiBiosParser226translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V217AtomConnectorInfoER13ConnectorInfo", +> wrapTranslateAtomConnectorInfoV2, orgTranslateAtomConnectorInfoV2), +> KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart) +> }; +> patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); +> } else { +> KernelPatcher::RouteRequest requests[] { +> KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), +> KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart), +> }; +> patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); +> +> orgGetAtomObjectTableForType = reinterpret_cast(patcher.solveSymbol(kextRadeonSupport.loadIndex, +> "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size)); +> if (!orgGetAtomObjectTableForType) { +> SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); +> patcher.clearError(); +> } +> } +> } else { +> KernelPatcher::RouteRequest requests[] { +> KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapLegacyGetConnectorsInfo, orgLegacyGetConnectorsInfo), +> KernelPatcher::RouteRequest("__ZN19AMDLegacyController5startEP9IOService", wrapLegacyATIControllerStart, orgLegacyATIControllerStart), +> }; +> patcher.routeMultiple(kextRadeonLegacySupport.loadIndex, requests, address, size); +> +> orgLegacyGetAtomObjectTableForType = patcher.solveSymbol(kextRadeonLegacySupport.loadIndex, +> "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size); +> if (!orgLegacyGetAtomObjectTableForType) { +> SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); +> patcher.clearError(); +> } +> } +> } +725,737c1892,1894 +< // Prioritise connectors, since it may cause black screen on e.g. R9 370 +< const uint8_t *senseList = nullptr; +< uint8_t senseNum = 0; +< auto priData = OSDynamicCast(OSData, ctrl->getProperty("connector-priority")); +< if (priData) { +< senseList = static_cast(priData->getBytesNoCopy()); +< senseNum = static_cast(priData->getLength()); +< DBGLOG("rad", "getConnectorInfo found %u senses in connector-priority", senseNum); +< reprioritiseConnectors(senseList, senseNum, connectors, *sz); +< } else { +< DBGLOG("rad", "getConnectorInfo leaving unchaged priority"); +< } +< } +--- +> void RAD::processHardwareKext(KernelPatcher &patcher, size_t hwIndex, mach_vm_address_t address, size_t size) { +> auto getFrame = getFrameBufferProcNames[hwIndex]; +> auto &hardware = kextRadeonHardware[hwIndex]; +739,740c1896,1952 +< DBGLOG("rad", "getConnectorsInfo resulting %u connectors follow", *sz); +< RADConnectors::print(connectors, *sz); +--- +> // Fix boot and wake to black screen +> for (size_t j = 0; j < MaxGetFrameBufferProcs && getFrame[j] != nullptr; j++) { +> auto getFB = patcher.solveSymbol(hardware.loadIndex, getFrame[j], address, size); +> if (getFB) { +> // Initially it was discovered that the only problematic register is PRIMARY_SURFACE_ADDRESS_HIGH (0x1A07). +> // This register must be nulled to solve most of the issues. +> // Depending on the amount of connected screens PRIMARY_SURFACE_ADDRESS (0x1A04) may not be null. +> // However, as of AMD Vega drivers in 10.13 DP1 both of these registers are now ignored. +> // Furthermore, there are no (extra) issues from just returning 0 in framebuffer base address. +> +> // xor rax, rax +> // ret +> uint8_t ret[] {0x48, 0x31, 0xC0, 0xC3}; +> patcher.routeBlock(getFB, ret, sizeof(ret)); +> if (patcher.getError() == KernelPatcher::Error::NoError) { +> DBGLOG("rad", "patched %s", getFrame[j]); +> } else { +> SYSLOG("rad", "failed to patch %s code %d", getFrame[j], patcher.getError()); +> patcher.clearError(); +> } +> } else { +> SYSLOG("rad", "failed to find %s code %d", getFrame[j], patcher.getError()); +> patcher.clearError(); +> } +> } +> +> // Fix reported Accelerator name to support WhateverName.app +> // Also fix GVA properties for X4000. +> if (fixConfigName || hwIndex == IndexRadeonHardwareX4000) { +> KernelPatcher::RouteRequest request(populateAccelConfigProcNames[hwIndex], wrapPopulateAccelConfig[hwIndex], orgPopulateAccelConfig[hwIndex]); +> patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); +> } +> +> // Enforce OpenGL support if requested +> if (forceOpenGL) { +> DBGLOG("rad", "disabling Metal support"); +> uint8_t find1[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; +> uint8_t find2[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; +> uint8_t repl1[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; +> uint8_t repl2[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; +> +> KernelPatcher::LookupPatch antimetal[] { +> {&hardware, find1, repl1, sizeof(find1), 2}, +> {&hardware, find2, repl2, sizeof(find1), 2} +> }; +> +> for (auto &p : antimetal) { +> patcher.applyLookupPatch(&p); +> patcher.clearError(); +> } +> } +> +> // Patch AppleGVA support for non-supported models +> if (forceCodecInfo && getHWInfoProcNames[hwIndex] != nullptr) { +> KernelPatcher::RouteRequest request(getHWInfoProcNames[hwIndex], wrapGetHWInfo[hwIndex], orgGetHWInfo[hwIndex]); +> patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); +> } +743,749c1955,2007 +< void RAD::autocorrectConnectors(uint8_t *baseAddr, AtomDisplayObjectPath *displayPaths, uint8_t displayPathNum, AtomConnectorObject *connectorObjects, +< uint8_t connectorObjectNum, RADConnectors::Connector *connectors, uint8_t sz) { +< for (uint8_t i = 0; i < displayPathNum; i++) { +< if (!isEncoder(displayPaths[i].usGraphicObjIds)) { +< DBGLOG("rad", "autocorrectConnectors not encoder %X at %u", displayPaths[i].usGraphicObjIds, i); +< continue; +< } +--- +> void RAD::mergeProperty(OSDictionary *props, const char *name, OSObject *value) { +> // The only type we could make from device properties is data. +> // To be able to override other types we do a conversion here. +> auto data = OSDynamicCast(OSData, value); +> if (data) { +> // It is hard to make a boolean even from ACPI, so we make a hack here: +> // 1-byte OSData with 0x01 / 0x00 values becomes boolean. +> auto val = static_cast(data->getBytesNoCopy()); +> auto len = data->getLength(); +> if (val && len == sizeof(uint8_t)) { +> if (val[0] == 1) { +> props->setObject(name, kOSBooleanTrue); +> DBGLOG("rad", "prop %s was merged as kOSBooleanTrue", name); +> return; +> } else if (val[0] == 0) { +> props->setObject(name, kOSBooleanFalse); +> DBGLOG("rad", "prop %s was merged as kOSBooleanFalse", name); +> return; +> } +> } +> +> // Consult the original value to make a decision +> auto orgValue = props->getObject(name); +> if (val && orgValue) { +> DBGLOG("rad", "prop %s has original value", name); +> if (len == sizeof(uint32_t) && OSDynamicCast(OSNumber, orgValue)) { +> auto num = *reinterpret_cast(val); +> auto osnum = OSNumber::withNumber(num, 32); +> if (osnum) { +> DBGLOG("rad", "prop %s was merged as number %u", name, num); +> props->setObject(name, osnum); +> osnum->release(); +> } +> return; +> } else if (len > 0 && val[len-1] == '\0' && OSDynamicCast(OSString, orgValue)) { +> auto str = reinterpret_cast(val); +> auto osstr = OSString::withCString(str); +> if (osstr) { +> DBGLOG("rad", "prop %s was merged as string %s", name, str); +> props->setObject(name, osstr); +> osstr->release(); +> } +> return; +> } +> } else { +> DBGLOG("rad", "prop %s has no original value", name); +> } +> } +> +> // Default merge as is +> props->setObject(name, value); +> DBGLOG("rad", "prop %s was merged", name); +> } +751,759c2009,2063 +< uint8_t txmit = 0, enc = 0; +< if (!getTxEnc(displayPaths[i].usGraphicObjIds, txmit, enc)) +< continue; +< +< uint8_t sense = getSenseID(baseAddr + connectorObjects[i].usRecordOffset); +< if (!sense) { +< DBGLOG("rad", "autocorrectConnectors failed to detect sense for %u connector", i); +< continue; +< } +--- +> void RAD::mergeProperties(OSDictionary *props, const char *prefix, IOService *provider) { +> // Should be ok, but in case there are issues switch to dictionaryWithProperties(); +> auto dict = provider->getPropertyTable(); +> if (dict) { +> auto iterator = OSCollectionIterator::withCollection(dict); +> if (iterator) { +> OSSymbol *propname; +> size_t prefixlen = strlen(prefix); +> while ((propname = OSDynamicCast(OSSymbol, iterator->getNextObject())) != nullptr) { +> auto name = propname->getCStringNoCopy(); +> if (name && propname->getLength() > prefixlen && !strncmp(name, prefix, prefixlen)) { +> auto prop = dict->getObject(propname); +> if (prop) +> mergeProperty(props, name + prefixlen, prop); +> else +> DBGLOG("rad", "prop %s was not merged due to no value", name); +> } else { +> //DBGLOG("rad", "prop %s does not match %s prefix", safeString(name), prefix); +> } +> } +> +> iterator->release(); +> } else { +> SYSLOG("rad", "prop merge failed to iterate over properties"); +> } +> } else { +> SYSLOG("rad", "prop merge failed to get properties"); +> } +> +> if (!strcmp(prefix, "CAIL,")) { +> for (size_t i = 0; i < arrsize(powerGatingFlags); i++) { +> if (powerGatingFlags[i] && props->getObject(powerGatingFlags[i])) { +> DBGLOG("rad", "cail prop merge found %s, replacing", powerGatingFlags[i]); +> auto num = OSNumber::withNumber(1, 32); +> if (num) { +> props->setObject(powerGatingFlags[i], num); +> num->release(); +> } +> } +> } +> } +> } +> +> void RAD::applyPropertyFixes(IOService *service, uint32_t connectorNum) { +> if (service && getKernelVersion() >= KernelVersion::HighSierra) { +> // Starting with 10.13.2 this is important to fix sleep issues due to enforced 6 screens +> if (!service->getProperty("CFG,CFG_FB_LIMIT")) { +> DBGLOG("rad", "setting fb limit to %u", connectorNum); +> service->setProperty("CFG_FB_LIMIT", connectorNum, 32); +> } +> +> // In the past we set CFG_USE_AGDC to false, which caused visual glitches and broken multimonitor support. +> // A better workaround is to disable AGDP just like we do globally. +> } +> } +761c2065,2122 +< DBGLOG("rad", "autocorrectConnectors found txmit %02X enc %02X sense %02X for %u connector", txmit, enc, sense, i); +--- +> void RAD::updateConnectorsInfo(void *atomutils, t_getAtomObjectTableForType gettable, IOService *ctrl, RADConnectors::Connector *connectors, uint8_t *sz) { +> if (atomutils) { +> DBGLOG("rad", "getConnectorsInfo found %u connectors", *sz); +> RADConnectors::print(connectors, *sz); +> } +> +> // Check if the user wants to override automatically detected connectors +> auto cons = ctrl->getProperty("connectors"); +> if (cons) { +> auto consData = OSDynamicCast(OSData, cons); +> if (consData) { +> auto consPtr = consData->getBytesNoCopy(); +> auto consSize = consData->getLength(); +> +> uint32_t consCount; +> if (WIOKit::getOSDataValue(ctrl, "connector-count", consCount)) { +> *sz = consCount; +> DBGLOG("rad", "getConnectorsInfo got size override to %u", *sz); +> } +> +> if (consPtr && consSize > 0 && *sz > 0 && RADConnectors::valid(consSize, *sz)) { +> RADConnectors::copy(connectors, *sz, static_cast(consPtr), consSize); +> DBGLOG("rad", "getConnectorsInfo installed %u connectors", *sz); +> applyPropertyFixes(ctrl, *sz); +> } else { +> DBGLOG("rad", "getConnectorsInfo conoverrides have invalid size %u for %u num", consSize, *sz); +> } +> } else { +> DBGLOG("rad", "getConnectorsInfo conoverrides have invalid type"); +> } +> } else { +> if (atomutils) { +> DBGLOG("rad", "getConnectorsInfo attempting to autofix connectors"); +> uint8_t sHeader = 0, displayPathNum = 0, connectorObjectNum = 0; +> auto baseAddr = static_cast(gettable(atomutils, AtomObjectTableType::Common, &sHeader)) - sizeof(uint32_t); +> auto displayPaths = static_cast(gettable(atomutils, AtomObjectTableType::DisplayPath, &displayPathNum)); +> auto connectorObjects = static_cast(gettable(atomutils, AtomObjectTableType::ConnectorObject, &connectorObjectNum)); +> if (displayPathNum == connectorObjectNum) +> autocorrectConnectors(baseAddr, displayPaths, displayPathNum, connectorObjects, connectorObjectNum, connectors, *sz); +> else +> DBGLOG("rad", "getConnectorsInfo found different displaypaths %u and connectors %u", displayPathNum, connectorObjectNum); +> } +> +> applyPropertyFixes(ctrl, *sz); +> +> // Prioritise connectors, since it may cause black screen on e.g. R9 370 +> const uint8_t *senseList = nullptr; +> uint8_t senseNum = 0; +> auto priData = OSDynamicCast(OSData, ctrl->getProperty("connector-priority")); +> if (priData) { +> senseList = static_cast(priData->getBytesNoCopy()); +> senseNum = static_cast(priData->getLength()); +> DBGLOG("rad", "getConnectorInfo found %u senses in connector-priority", senseNum); +> reprioritiseConnectors(senseList, senseNum, connectors, *sz); +> } else { +> DBGLOG("rad", "getConnectorInfo leaving unchaged priority"); +> } +> } +763,764c2124,2125 +< autocorrectConnector(getConnectorID(displayPaths[i].usConnObjectId), sense, txmit, enc, connectors, sz); +< } +--- +> DBGLOG("rad", "getConnectorsInfo resulting %u connectors follow", *sz); +> RADConnectors::print(connectors, *sz); +767,782c2128,2144 +< void RAD::autocorrectConnector(uint8_t connector, uint8_t sense, uint8_t txmit, uint8_t enc, RADConnectors::Connector *connectors, uint8_t sz) { +< // This function attempts to fix the following issues: +< // +< // 1. Incompatible DVI transmitter on 290X, 370 and probably some other models +< // In this case a correct transmitter is detected by AtiAtomBiosDce60::getPropertiesForEncoderObject, however, later +< // in AtiAtomBiosDce60::getPropertiesForConnectorObject for DVI DL and TITFP513 this value is conjuncted with 0xCF, +< // which makes it wrong: 0x10 -> 0, 0x11 -> 1. As a result one gets black screen when connecting multiple displays. +< // getPropertiesForEncoderObject takes usGraphicObjIds and getPropertiesForConnectorObject takes usConnObjectId +< +< if (callbackRAD->dviSingleLink) { +< if (connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I && +< connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D && +< connector != CONNECTOR_OBJECT_ID_LVDS) { +< DBGLOG("rad", "autocorrectConnector found unsupported connector type %02X", connector); +< return; +< } +--- +> void RAD::autocorrectConnectors(uint8_t *baseAddr, AtomDisplayObjectPath *displayPaths, uint8_t displayPathNum, AtomConnectorObject *connectorObjects, +> uint8_t connectorObjectNum, RADConnectors::Connector *connectors, uint8_t sz) { +> for (uint8_t i = 0; i < displayPathNum; i++) { +> if (!isEncoder(displayPaths[i].usGraphicObjIds)) { +> DBGLOG("rad", "autocorrectConnectors not encoder %X at %u", displayPaths[i].usGraphicObjIds, i); +> continue; +> } +> +> uint8_t txmit = 0, enc = 0; +> if (!getTxEnc(displayPaths[i].usGraphicObjIds, txmit, enc)) +> continue; +> +> uint8_t sense = getSenseID(baseAddr + connectorObjects[i].usRecordOffset); +> if (!sense) { +> DBGLOG("rad", "autocorrectConnectors failed to detect sense for %u connector", i); +> continue; +> } +784,794c2146 +< auto fixTransmit = [](auto &con, uint8_t idx, uint8_t sense, uint8_t txmit) { +< if (con.sense == sense) { +< if (con.transmitter != txmit && (con.transmitter & 0xCF) == con.transmitter) { +< DBGLOG("rad", "autocorrectConnector replacing txmit %02X with %02X for %u connector sense %02X", +< con.transmitter, txmit, idx, sense); +< con.transmitter = txmit; +< } +< return true; +< } +< return false; +< }; +--- +> DBGLOG("rad", "autocorrectConnectors found txmit %02X enc %02X sense %02X for %u connector", txmit, enc, sense, i); +796,810c2148,2195 +< bool isModern = RADConnectors::modern(); +< for (uint8_t j = 0; j < sz; j++) { +< if (isModern) { +< auto &con = (&connectors->modern)[j]; +< if (fixTransmit(con, j, sense, txmit)) +< break; +< } else { +< auto &con = (&connectors->legacy)[j]; +< if (fixTransmit(con, j, sense, txmit)) +< break; +< } +< } +< } else { +< DBGLOG("rad", "autocorrectConnector use -raddvi to enable dvi autocorrection"); +< } +--- +> autocorrectConnector(getConnectorID(displayPaths[i].usConnObjectId), sense, txmit, enc, connectors, sz); +> } +> } +> +> void RAD::autocorrectConnector(uint8_t connector, uint8_t sense, uint8_t txmit, uint8_t enc, RADConnectors::Connector *connectors, uint8_t sz) { +> // This function attempts to fix the following issues: +> // +> // 1. Incompatible DVI transmitter on 290X, 370 and probably some other models +> // In this case a correct transmitter is detected by AtiAtomBiosDce60::getPropertiesForEncoderObject, however, later +> // in AtiAtomBiosDce60::getPropertiesForConnectorObject for DVI DL and TITFP513 this value is conjuncted with 0xCF, +> // which makes it wrong: 0x10 -> 0, 0x11 -> 1. As a result one gets black screen when connecting multiple displays. +> // getPropertiesForEncoderObject takes usGraphicObjIds and getPropertiesForConnectorObject takes usConnObjectId +> +> if (callbackRAD->dviSingleLink) { +> if (connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I && +> connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D && +> connector != CONNECTOR_OBJECT_ID_LVDS) { +> DBGLOG("rad", "autocorrectConnector found unsupported connector type %02X", connector); +> return; +> } +> +> auto fixTransmit = [](auto &con, uint8_t idx, uint8_t sense, uint8_t txmit) { +> if (con.sense == sense) { +> if (con.transmitter != txmit && (con.transmitter & 0xCF) == con.transmitter) { +> DBGLOG("rad", "autocorrectConnector replacing txmit %02X with %02X for %u connector sense %02X", +> con.transmitter, txmit, idx, sense); +> con.transmitter = txmit; +> } +> return true; +> } +> return false; +> }; +> +> bool isModern = RADConnectors::modern(); +> for (uint8_t j = 0; j < sz; j++) { +> if (isModern) { +> auto &con = (&connectors->modern)[j]; +> if (fixTransmit(con, j, sense, txmit)) +> break; +> } else { +> auto &con = (&connectors->legacy)[j]; +> if (fixTransmit(con, j, sense, txmit)) +> break; +> } +> } +> } else { +> DBGLOG("rad", "autocorrectConnector use -raddvi to enable dvi autocorrection"); +> } +814,853c2199,2238 +< static constexpr uint32_t typeList[] { +< RADConnectors::ConnectorLVDS, +< RADConnectors::ConnectorDigitalDVI, +< RADConnectors::ConnectorHDMI, +< RADConnectors::ConnectorDP, +< RADConnectors::ConnectorVGA +< }; +< static constexpr uint8_t typeNum {static_cast(arrsize(typeList))}; +< +< bool isModern = RADConnectors::modern(); +< uint16_t priCount = 1; +< // Automatically detected connectors have equal priority (0), which often results in black screen +< // This allows to change this firstly by user-defined list, then by type list. +< //TODO: priority is ignored for 5xxx and 6xxx GPUs, should we manually reorder items? +< for (uint8_t i = 0; i < senseNum + typeNum + 1; i++) { +< for (uint8_t j = 0; j < sz; j++) { +< auto reorder = [&](auto &con) { +< if (i == senseNum + typeNum) { +< if (con.priority == 0) +< con.priority = priCount++; +< } else if (i < senseNum) { +< if (con.sense == senseList[i]) { +< DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by sense", con.sense, priCount); +< con.priority = priCount++; +< return true; +< } +< } else { +< if (con.priority == 0 && con.type == typeList[i-senseNum]) { +< DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by type", con.sense, priCount); +< con.priority = priCount++; +< } +< } +< return false; +< }; +< +< if ((isModern && reorder((&connectors->modern)[j])) || +< (!isModern && reorder((&connectors->legacy)[j]))) +< break; +< } +< } +--- +> static constexpr uint32_t typeList[] { +> RADConnectors::ConnectorLVDS, +> RADConnectors::ConnectorDigitalDVI, +> RADConnectors::ConnectorHDMI, +> RADConnectors::ConnectorDP, +> RADConnectors::ConnectorVGA +> }; +> static constexpr uint8_t typeNum {static_cast(arrsize(typeList))}; +> +> bool isModern = RADConnectors::modern(); +> uint16_t priCount = 1; +> // Automatically detected connectors have equal priority (0), which often results in black screen +> // This allows to change this firstly by user-defined list, then by type list. +> //TODO: priority is ignored for 5xxx and 6xxx GPUs, should we manually reorder items? +> for (uint8_t i = 0; i < senseNum + typeNum + 1; i++) { +> for (uint8_t j = 0; j < sz; j++) { +> auto reorder = [&](auto &con) { +> if (i == senseNum + typeNum) { +> if (con.priority == 0) +> con.priority = priCount++; +> } else if (i < senseNum) { +> if (con.sense == senseList[i]) { +> DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by sense", con.sense, priCount); +> con.priority = priCount++; +> return true; +> } +> } else { +> if (con.priority == 0 && con.type == typeList[i-senseNum]) { +> DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by type", con.sense, priCount); +> con.priority = priCount++; +> } +> } +> return false; +> }; +> +> if ((isModern && reorder((&connectors->modern)[j])) || +> (!isModern && reorder((&connectors->legacy)[j]))) +> break; +> } +> } +857,984c2242,2369 +< auto codecStr = OSDynamicCast(OSString, accelService->getProperty("IOGVACodec")); +< if (codecStr == nullptr) { +< DBGLOG("rad", "updating X4000 accelerator IOGVACodec to VCE"); +< accelService->setProperty("IOGVACodec", "VCE"); +< } else { +< auto codec = codecStr->getCStringNoCopy(); +< DBGLOG("rad", "X4000 accelerator IOGVACodec is already set to %s", safeString(codec)); +< if (codec != nullptr && strncmp(codec, "AMD", strlen("AMD")) == 0) { +< bool needsDecode = accelService->getProperty("IOGVAHEVCDecode") == nullptr; +< bool needsEncode = accelService->getProperty("IOGVAHEVCEncode") == nullptr; +< if (needsDecode) { +< OSObject *VTMaxDecodeLevel = OSNumber::withNumber(153, 32); +< OSString *VTMaxDecodeLevelKey = OSString::withCString("VTMaxDecodeLevel"); +< OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); +< OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(3); +< OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); +< OSString *VTPerProfileDetailsKey2 = OSString::withCString("2"); +< OSString *VTPerProfileDetailsKey3 = OSString::withCString("3"); +< +< OSArray *VTSupportedProfileArray = OSArray::withCapacity(3); +< OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); +< OSNumber *VTSupportedProfileArray2 = OSNumber::withNumber(2, 32); +< OSNumber *VTSupportedProfileArray3 = OSNumber::withNumber(3, 32); +< +< OSDictionary *IOGVAHEVCDecodeCapabilities = OSDictionary::withCapacity(2); +< OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); +< OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); +< +< if (VTMaxDecodeLevel != nullptr && VTMaxDecodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && +< VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTPerProfileDetailsKey2 != nullptr && +< VTPerProfileDetailsKey3 != nullptr && VTSupportedProfileArrayKey != nullptr && VTSupportedProfileArray1 != nullptr && +< VTSupportedProfileArray2 != nullptr && VTSupportedProfileArray3 != nullptr && VTSupportedProfileArray != nullptr && +< VTPerProfileDetailsKey != nullptr && IOGVAHEVCDecodeCapabilities != nullptr) { +< VTPerProfileDetailsInner->setObject(VTMaxDecodeLevelKey, VTMaxDecodeLevel); +< VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); +< VTPerProfileDetails->setObject(VTPerProfileDetailsKey2, VTPerProfileDetailsInner); +< VTPerProfileDetails->setObject(VTPerProfileDetailsKey3, VTPerProfileDetailsInner); +< +< VTSupportedProfileArray->setObject(VTSupportedProfileArray1); +< VTSupportedProfileArray->setObject(VTSupportedProfileArray2); +< VTSupportedProfileArray->setObject(VTSupportedProfileArray3); +< +< IOGVAHEVCDecodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); +< IOGVAHEVCDecodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); +< +< accelService->setProperty("IOGVAHEVCDecode", "1"); +< accelService->setProperty("IOGVAHEVCDecodeCapabilities", IOGVAHEVCDecodeCapabilities); +< +< DBGLOG("rad", "recovering IOGVAHEVCDecode"); +< } else { +< SYSLOG("rad", "allocation failure in IOGVAHEVCDecode"); +< } +< +< OSSafeReleaseNULL(VTMaxDecodeLevel); +< OSSafeReleaseNULL(VTMaxDecodeLevelKey); +< OSSafeReleaseNULL(VTPerProfileDetailsInner); +< OSSafeReleaseNULL(VTPerProfileDetails); +< OSSafeReleaseNULL(VTPerProfileDetailsKey1); +< OSSafeReleaseNULL(VTPerProfileDetailsKey2); +< OSSafeReleaseNULL(VTPerProfileDetailsKey3); +< OSSafeReleaseNULL(VTSupportedProfileArrayKey); +< OSSafeReleaseNULL(VTSupportedProfileArray1); +< OSSafeReleaseNULL(VTSupportedProfileArray2); +< OSSafeReleaseNULL(VTSupportedProfileArray3); +< OSSafeReleaseNULL(VTSupportedProfileArray); +< OSSafeReleaseNULL(VTPerProfileDetailsKey); +< OSSafeReleaseNULL(IOGVAHEVCDecodeCapabilities); +< } +< +< if (needsEncode) { +< OSObject *VTMaxEncodeLevel = OSNumber::withNumber(153, 32); +< OSString *VTMaxEncodeLevelKey = OSString::withCString("VTMaxEncodeLevel"); +< +< OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); +< OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(1); +< OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); +< +< OSArray *VTSupportedProfileArray = OSArray::withCapacity(1); +< OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); +< +< OSDictionary *IOGVAHEVCEncodeCapabilities = OSDictionary::withCapacity(4); +< OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); +< OSString *VTQualityRatingKey = OSString::withCString("VTQualityRating"); +< OSNumber *VTQualityRating = OSNumber::withNumber(50, 32); +< OSString *VTRatingKey = OSString::withCString("VTRating"); +< OSNumber *VTRating = OSNumber::withNumber(350, 32); +< OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); +< +< if (VTMaxEncodeLevel != nullptr && VTMaxEncodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && +< VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTSupportedProfileArrayKey != nullptr && +< VTSupportedProfileArray1 != nullptr && VTSupportedProfileArray != nullptr && VTPerProfileDetailsKey != nullptr && +< VTQualityRatingKey != nullptr && VTQualityRating != nullptr && VTRatingKey != nullptr && VTRating != nullptr && +< IOGVAHEVCEncodeCapabilities != nullptr) { +< +< VTPerProfileDetailsInner->setObject(VTMaxEncodeLevelKey, VTMaxEncodeLevel); +< VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); +< VTSupportedProfileArray->setObject(VTSupportedProfileArray1); +< +< IOGVAHEVCEncodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); +< IOGVAHEVCEncodeCapabilities->setObject(VTQualityRatingKey, VTQualityRating); +< IOGVAHEVCEncodeCapabilities->setObject(VTRatingKey, VTRating); +< IOGVAHEVCEncodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); +< +< accelService->setProperty("IOGVAHEVCEncode", "1"); +< accelService->setProperty("IOGVAHEVCEncodeCapabilities", IOGVAHEVCEncodeCapabilities); +< +< DBGLOG("rad", "recovering IOGVAHEVCEncode"); +< } else { +< SYSLOG("rad", "allocation failure in IOGVAHEVCEncode"); +< } +< +< OSSafeReleaseNULL(VTMaxEncodeLevel); +< OSSafeReleaseNULL(VTMaxEncodeLevelKey); +< OSSafeReleaseNULL(VTPerProfileDetailsInner); +< OSSafeReleaseNULL(VTPerProfileDetails); +< OSSafeReleaseNULL(VTPerProfileDetailsKey1); +< OSSafeReleaseNULL(VTSupportedProfileArrayKey); +< OSSafeReleaseNULL(VTSupportedProfileArray1); +< OSSafeReleaseNULL(VTSupportedProfileArray); +< OSSafeReleaseNULL(VTPerProfileDetailsKey); +< OSSafeReleaseNULL(VTQualityRatingKey); +< OSSafeReleaseNULL(VTQualityRating); +< OSSafeReleaseNULL(VTRatingKey); +< OSSafeReleaseNULL(VTRating); +< OSSafeReleaseNULL(IOGVAHEVCEncodeCapabilities); +< } +< } +< } +--- +> auto codecStr = OSDynamicCast(OSString, accelService->getProperty("IOGVACodec")); +> if (codecStr == nullptr) { +> DBGLOG("rad", "updating X4000 accelerator IOGVACodec to VCE"); +> accelService->setProperty("IOGVACodec", "VCE"); +> } else { +> auto codec = codecStr->getCStringNoCopy(); +> DBGLOG("rad", "X4000 accelerator IOGVACodec is already set to %s", safeString(codec)); +> if (codec != nullptr && strncmp(codec, "AMD", strlen("AMD")) == 0) { +> bool needsDecode = accelService->getProperty("IOGVAHEVCDecode") == nullptr; +> bool needsEncode = accelService->getProperty("IOGVAHEVCEncode") == nullptr; +> if (needsDecode) { +> OSObject *VTMaxDecodeLevel = OSNumber::withNumber(153, 32); +> OSString *VTMaxDecodeLevelKey = OSString::withCString("VTMaxDecodeLevel"); +> OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); +> OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(3); +> OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); +> OSString *VTPerProfileDetailsKey2 = OSString::withCString("2"); +> OSString *VTPerProfileDetailsKey3 = OSString::withCString("3"); +> +> OSArray *VTSupportedProfileArray = OSArray::withCapacity(3); +> OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); +> OSNumber *VTSupportedProfileArray2 = OSNumber::withNumber(2, 32); +> OSNumber *VTSupportedProfileArray3 = OSNumber::withNumber(3, 32); +> +> OSDictionary *IOGVAHEVCDecodeCapabilities = OSDictionary::withCapacity(2); +> OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); +> OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); +> +> if (VTMaxDecodeLevel != nullptr && VTMaxDecodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && +> VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTPerProfileDetailsKey2 != nullptr && +> VTPerProfileDetailsKey3 != nullptr && VTSupportedProfileArrayKey != nullptr && VTSupportedProfileArray1 != nullptr && +> VTSupportedProfileArray2 != nullptr && VTSupportedProfileArray3 != nullptr && VTSupportedProfileArray != nullptr && +> VTPerProfileDetailsKey != nullptr && IOGVAHEVCDecodeCapabilities != nullptr) { +> VTPerProfileDetailsInner->setObject(VTMaxDecodeLevelKey, VTMaxDecodeLevel); +> VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); +> VTPerProfileDetails->setObject(VTPerProfileDetailsKey2, VTPerProfileDetailsInner); +> VTPerProfileDetails->setObject(VTPerProfileDetailsKey3, VTPerProfileDetailsInner); +> +> VTSupportedProfileArray->setObject(VTSupportedProfileArray1); +> VTSupportedProfileArray->setObject(VTSupportedProfileArray2); +> VTSupportedProfileArray->setObject(VTSupportedProfileArray3); +> +> IOGVAHEVCDecodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); +> IOGVAHEVCDecodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); +> +> accelService->setProperty("IOGVAHEVCDecode", "1"); +> accelService->setProperty("IOGVAHEVCDecodeCapabilities", IOGVAHEVCDecodeCapabilities); +> +> DBGLOG("rad", "recovering IOGVAHEVCDecode"); +> } else { +> SYSLOG("rad", "allocation failure in IOGVAHEVCDecode"); +> } +> +> OSSafeReleaseNULL(VTMaxDecodeLevel); +> OSSafeReleaseNULL(VTMaxDecodeLevelKey); +> OSSafeReleaseNULL(VTPerProfileDetailsInner); +> OSSafeReleaseNULL(VTPerProfileDetails); +> OSSafeReleaseNULL(VTPerProfileDetailsKey1); +> OSSafeReleaseNULL(VTPerProfileDetailsKey2); +> OSSafeReleaseNULL(VTPerProfileDetailsKey3); +> OSSafeReleaseNULL(VTSupportedProfileArrayKey); +> OSSafeReleaseNULL(VTSupportedProfileArray1); +> OSSafeReleaseNULL(VTSupportedProfileArray2); +> OSSafeReleaseNULL(VTSupportedProfileArray3); +> OSSafeReleaseNULL(VTSupportedProfileArray); +> OSSafeReleaseNULL(VTPerProfileDetailsKey); +> OSSafeReleaseNULL(IOGVAHEVCDecodeCapabilities); +> } +> +> if (needsEncode) { +> OSObject *VTMaxEncodeLevel = OSNumber::withNumber(153, 32); +> OSString *VTMaxEncodeLevelKey = OSString::withCString("VTMaxEncodeLevel"); +> +> OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); +> OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(1); +> OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); +> +> OSArray *VTSupportedProfileArray = OSArray::withCapacity(1); +> OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); +> +> OSDictionary *IOGVAHEVCEncodeCapabilities = OSDictionary::withCapacity(4); +> OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); +> OSString *VTQualityRatingKey = OSString::withCString("VTQualityRating"); +> OSNumber *VTQualityRating = OSNumber::withNumber(50, 32); +> OSString *VTRatingKey = OSString::withCString("VTRating"); +> OSNumber *VTRating = OSNumber::withNumber(350, 32); +> OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); +> +> if (VTMaxEncodeLevel != nullptr && VTMaxEncodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && +> VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTSupportedProfileArrayKey != nullptr && +> VTSupportedProfileArray1 != nullptr && VTSupportedProfileArray != nullptr && VTPerProfileDetailsKey != nullptr && +> VTQualityRatingKey != nullptr && VTQualityRating != nullptr && VTRatingKey != nullptr && VTRating != nullptr && +> IOGVAHEVCEncodeCapabilities != nullptr) { +> +> VTPerProfileDetailsInner->setObject(VTMaxEncodeLevelKey, VTMaxEncodeLevel); +> VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); +> VTSupportedProfileArray->setObject(VTSupportedProfileArray1); +> +> IOGVAHEVCEncodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); +> IOGVAHEVCEncodeCapabilities->setObject(VTQualityRatingKey, VTQualityRating); +> IOGVAHEVCEncodeCapabilities->setObject(VTRatingKey, VTRating); +> IOGVAHEVCEncodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); +> +> accelService->setProperty("IOGVAHEVCEncode", "1"); +> accelService->setProperty("IOGVAHEVCEncodeCapabilities", IOGVAHEVCEncodeCapabilities); +> +> DBGLOG("rad", "recovering IOGVAHEVCEncode"); +> } else { +> SYSLOG("rad", "allocation failure in IOGVAHEVCEncode"); +> } +> +> OSSafeReleaseNULL(VTMaxEncodeLevel); +> OSSafeReleaseNULL(VTMaxEncodeLevelKey); +> OSSafeReleaseNULL(VTPerProfileDetailsInner); +> OSSafeReleaseNULL(VTPerProfileDetails); +> OSSafeReleaseNULL(VTPerProfileDetailsKey1); +> OSSafeReleaseNULL(VTSupportedProfileArrayKey); +> OSSafeReleaseNULL(VTSupportedProfileArray1); +> OSSafeReleaseNULL(VTSupportedProfileArray); +> OSSafeReleaseNULL(VTPerProfileDetailsKey); +> OSSafeReleaseNULL(VTQualityRatingKey); +> OSSafeReleaseNULL(VTQualityRating); +> OSSafeReleaseNULL(VTRatingKey); +> OSSafeReleaseNULL(VTRating); +> OSSafeReleaseNULL(IOGVAHEVCEncodeCapabilities); +> } +> } +> } +988,1019c2373,2404 +< if (accelService && accelConfig) { +< if (fixConfigName) { +< auto gpuService = accelService->getParentEntry(gIOServicePlane); +< +< if (gpuService) { +< auto model = OSDynamicCast(OSData, gpuService->getProperty("model")); +< if (model) { +< auto modelStr = static_cast(model->getBytesNoCopy()); +< if (modelStr) { +< if (modelStr[0] == 'A' && ((modelStr[1] == 'M' && modelStr[2] == 'D') || +< (modelStr[1] == 'T' && modelStr[2] == 'I')) && modelStr[3] == ' ') { +< modelStr += 4; +< } +< +< DBGLOG("rad", "updateAccelConfig found gpu model %s", modelStr); +< *accelConfig = modelStr; +< } else { +< DBGLOG("rad", "updateAccelConfig found null gpu model"); +< } +< } else { +< DBGLOG("rad", "updateAccelConfig failed to find gpu model"); +< } +< +< } else { +< DBGLOG("rad", "updateAccelConfig failed to find accelerator parent"); +< } +< } +< +< if (enableGvaSupport && hwIndex == IndexRadeonHardwareX4000) { +< setGvaProperties(accelService); +< } +< } +--- +> if (accelService && accelConfig) { +> if (fixConfigName) { +> auto gpuService = accelService->getParentEntry(gIOServicePlane); +> +> if (gpuService) { +> auto model = OSDynamicCast(OSData, gpuService->getProperty("model")); +> if (model) { +> auto modelStr = static_cast(model->getBytesNoCopy()); +> if (modelStr) { +> if (modelStr[0] == 'A' && ((modelStr[1] == 'M' && modelStr[2] == 'D') || +> (modelStr[1] == 'T' && modelStr[2] == 'I')) && modelStr[3] == ' ') { +> modelStr += 4; +> } +> +> DBGLOG("rad", "updateAccelConfig found gpu model %s", modelStr); +> *accelConfig = modelStr; +> } else { +> DBGLOG("rad", "updateAccelConfig found null gpu model"); +> } +> } else { +> DBGLOG("rad", "updateAccelConfig failed to find gpu model"); +> } +> +> } else { +> DBGLOG("rad", "updateAccelConfig failed to find accelerator parent"); +> } +> } +> +> if (enableGvaSupport && hwIndex == IndexRadeonHardwareX4000) { +> setGvaProperties(accelService); +> } +> } +1023,1032c2408,2417 +< if (length > 10 && aKey && reinterpret_cast(aKey)[0] == 'edom' && reinterpret_cast(aKey)[2] == 'l') { +< DBGLOG("rad", "SetProperty caught model %u (%.*s)", length, length, static_cast(bytes)); +< if (*static_cast(bytes) == ' DMA' || *static_cast(bytes) == ' ITA' || *static_cast(bytes) == 'edaR') { +< if (FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey)) { +< DBGLOG("rad", "SetProperty ignored setting %s to %s", aKey, static_cast(bytes)); +< return true; +< } +< DBGLOG("rad", "SetProperty missing %s, fallback to %s", aKey, static_cast(bytes)); +< } +< } +--- +> if (length > 10 && aKey && reinterpret_cast(aKey)[0] == 'edom' && reinterpret_cast(aKey)[2] == 'l') { +> DBGLOG("rad", "SetProperty caught model %u (%.*s)", length, length, static_cast(bytes)); +> if (*static_cast(bytes) == ' DMA' || *static_cast(bytes) == ' ITA' || *static_cast(bytes) == 'edaR') { +> if (FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey)) { +> DBGLOG("rad", "SetProperty ignored setting %s to %s", aKey, static_cast(bytes)); +> return true; +> } +> DBGLOG("rad", "SetProperty missing %s, fallback to %s", aKey, static_cast(bytes)); +> } +> } +1034c2419 +< return FunctionCast(wrapSetProperty, callbackRAD->orgSetProperty)(that, aKey, bytes, length); +--- +> return FunctionCast(wrapSetProperty, callbackRAD->orgSetProperty)(that, aKey, bytes, length); +1038,1039c2423,2424 +< auto obj = FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey); +< auto props = OSDynamicCast(OSDictionary, obj); +--- +> auto obj = FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey); +> auto props = OSDynamicCast(OSDictionary, obj); +1041,1052c2426,2454 +< if (props && aKey) { +< const char *prefix {nullptr}; +< auto provider = OSDynamicCast(IOService, that->getParentEntry(gIOServicePlane)); +< if (provider) { +< if (aKey[0] == 'a') { +< if (!strcmp(aKey, "aty_config")) +< prefix = "CFG,"; +< else if (!strcmp(aKey, "aty_properties")) +< prefix = "PP,"; +< } else if (aKey[0] == 'c' && !strcmp(aKey, "cail_properties")) { +< prefix = "CAIL,"; +< } +--- +> if (props && aKey) { +> const char *prefix {nullptr}; +> auto provider = OSDynamicCast(IOService, that->getParentEntry(gIOServicePlane)); +> if (provider) { +> if (aKey[0] == 'a') { +> if (!strcmp(aKey, "aty_config")) +> prefix = "CFG,"; +> else if (!strcmp(aKey, "aty_properties")) +> prefix = "PP,"; +> } else if (aKey[0] == 'c' && !strcmp(aKey, "cail_properties")) { +> prefix = "CAIL,"; +> } +> +> if (prefix) { +> DBGLOG("rad", "GetProperty discovered property merge request for %s", aKey); +> auto rawProps = props->copyCollection(); +> if (rawProps) { +> auto newProps = OSDynamicCast(OSDictionary, rawProps); +> if (newProps) { +> callbackRAD->mergeProperties(newProps, prefix, provider); +> that->setProperty(aKey, newProps); +> obj = newProps; +> } +> rawProps->release(); +> } +> +> } +> } +> } +1054,1071c2456 +< if (prefix) { +< DBGLOG("rad", "GetProperty discovered property merge request for %s", aKey); +< auto rawProps = props->copyCollection(); +< if (rawProps) { +< auto newProps = OSDynamicCast(OSDictionary, rawProps); +< if (newProps) { +< callbackRAD->mergeProperties(newProps, prefix, provider); +< that->setProperty(aKey, newProps); +< obj = newProps; +< } +< rawProps->release(); +< } +< +< } +< } +< } +< +< return obj; +--- +> return obj; +1075,1076c2460,2461 +< uint32_t code = FunctionCast(wrapGetConnectorsInfoV1, callbackRAD->orgGetConnectorsInfoV1)(that, connectors, sz); +< auto props = callbackRAD->currentPropProvider.get(); +--- +> uint32_t code = FunctionCast(wrapGetConnectorsInfoV1, callbackRAD->orgGetConnectorsInfoV1)(that, connectors, sz); +> auto props = callbackRAD->currentPropProvider.get(); +1078,1085c2463,2470 +< if (code == 0 && sz && props && *props) { +< if (getKernelVersion() >= KernelVersion::HighSierra) +< callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); +< else +< callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgGetAtomObjectTableForType, *props, connectors, sz); +< } else { +< DBGLOG("rad", "getConnectorsInfoV1 failed %X or undefined %d", code, props == nullptr); +< } +--- +> if (code == 0 && sz && props && *props) { +> if (getKernelVersion() >= KernelVersion::HighSierra) +> callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); +> else +> callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgGetAtomObjectTableForType, *props, connectors, sz); +> } else { +> DBGLOG("rad", "getConnectorsInfoV1 failed %X or undefined %d", code, props == nullptr); +> } +1087c2472 +< return code; +--- +> return code; +1091,1092c2476,2477 +< uint32_t code = FunctionCast(wrapGetConnectorsInfoV2, callbackRAD->orgGetConnectorsInfoV2)(that, connectors, sz); +< auto props = callbackRAD->currentPropProvider.get(); +--- +> uint32_t code = FunctionCast(wrapGetConnectorsInfoV2, callbackRAD->orgGetConnectorsInfoV2)(that, connectors, sz); +> auto props = callbackRAD->currentPropProvider.get(); +1094,1097c2479,2482 +< if (code == 0 && sz && props && *props) +< callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); +< else +< DBGLOG("rad", "getConnectorsInfoV2 failed %X or undefined %d", code, props == nullptr); +--- +> if (code == 0 && sz && props && *props) +> callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); +> else +> DBGLOG("rad", "getConnectorsInfoV2 failed %X or undefined %d", code, props == nullptr); +1099c2484 +< return code; +--- +> return code; +1103,1104c2488,2489 +< uint32_t code = FunctionCast(wrapLegacyGetConnectorsInfo, callbackRAD->orgLegacyGetConnectorsInfo)(that, connectors, sz); +< auto props = callbackRAD->currentLegacyPropProvider.get(); +--- +> uint32_t code = FunctionCast(wrapLegacyGetConnectorsInfo, callbackRAD->orgLegacyGetConnectorsInfo)(that, connectors, sz); +> auto props = callbackRAD->currentLegacyPropProvider.get(); +1106,1109c2491,2494 +< if (code == 0 && sz && props && *props) +< callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgLegacyGetAtomObjectTableForType, *props, connectors, sz); +< else +< DBGLOG("rad", "legacy getConnectorsInfo failed %X or undefined %d", code, props == nullptr); +--- +> if (code == 0 && sz && props && *props) +> callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgLegacyGetAtomObjectTableForType, *props, connectors, sz); +> else +> DBGLOG("rad", "legacy getConnectorsInfo failed %X or undefined %d", code, props == nullptr); +1111c2496 +< return code; +--- +> return code; +1115c2500 +< uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV1, callbackRAD->orgTranslateAtomConnectorInfoV1)(that, info, connector); +--- +> uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV1, callbackRAD->orgTranslateAtomConnectorInfoV1)(that, info, connector); +1117,1118c2502,2503 +< if (code == 0 && info && connector) { +< RADConnectors::print(connector, 1); +--- +> if (code == 0 && info && connector) { +> RADConnectors::print(connector, 1); +1120,1149c2505,2534 +< uint8_t sense = getSenseID(info->i2cRecord); +< if (sense) { +< DBGLOG("rad", "translateAtomConnectorInfoV1 got sense id %02X", sense); +< +< // We need to extract usGraphicObjIds from info->hpdRecord, which is of type ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT: +< // struct ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT { +< // uint8_t ucNumberOfSrc; +< // uint16_t usSrcObjectID[ucNumberOfSrc]; +< // uint8_t ucNumberOfDst; +< // uint16_t usDstObjectID[ucNumberOfDst]; +< // }; +< // The value we need is in usSrcObjectID. The structure is byte-packed. +< +< uint8_t ucNumberOfSrc = info->hpdRecord[0]; +< for (uint8_t i = 0; i < ucNumberOfSrc; i++) { +< auto usSrcObjectID = *reinterpret_cast(info->hpdRecord + sizeof(uint8_t) + i * sizeof(uint16_t)); +< DBGLOG("rad", "translateAtomConnectorInfoV1 checking %04X object id", usSrcObjectID); +< if (((usSrcObjectID & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT) == GRAPH_OBJECT_TYPE_ENCODER) { +< uint8_t txmit = 0, enc = 0; +< if (getTxEnc(usSrcObjectID, txmit, enc)) +< callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); +< break; +< } +< } +< +< +< } else { +< DBGLOG("rad", "translateAtomConnectorInfoV1 failed to detect sense for translated connector"); +< } +< } +--- +> uint8_t sense = getSenseID(info->i2cRecord); +> if (sense) { +> DBGLOG("rad", "translateAtomConnectorInfoV1 got sense id %02X", sense); +> +> // We need to extract usGraphicObjIds from info->hpdRecord, which is of type ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT: +> // struct ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT { +> // uint8_t ucNumberOfSrc; +> // uint16_t usSrcObjectID[ucNumberOfSrc]; +> // uint8_t ucNumberOfDst; +> // uint16_t usDstObjectID[ucNumberOfDst]; +> // }; +> // The value we need is in usSrcObjectID. The structure is byte-packed. +> +> uint8_t ucNumberOfSrc = info->hpdRecord[0]; +> for (uint8_t i = 0; i < ucNumberOfSrc; i++) { +> auto usSrcObjectID = *reinterpret_cast(info->hpdRecord + sizeof(uint8_t) + i * sizeof(uint16_t)); +> DBGLOG("rad", "translateAtomConnectorInfoV1 checking %04X object id", usSrcObjectID); +> if (((usSrcObjectID & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT) == GRAPH_OBJECT_TYPE_ENCODER) { +> uint8_t txmit = 0, enc = 0; +> if (getTxEnc(usSrcObjectID, txmit, enc)) +> callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); +> break; +> } +> } +> +> +> } else { +> DBGLOG("rad", "translateAtomConnectorInfoV1 failed to detect sense for translated connector"); +> } +> } +1151c2536 +< return code; +--- +> return code; +1155c2540 +< uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV2, callbackRAD->orgTranslateAtomConnectorInfoV2)(that, info, connector); +--- +> uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV2, callbackRAD->orgTranslateAtomConnectorInfoV2)(that, info, connector); +1157,1158c2542,2543 +< if (code == 0 && info && connector) { +< RADConnectors::print(connector, 1); +--- +> if (code == 0 && info && connector) { +> RADConnectors::print(connector, 1); +1160,1169c2545,2554 +< uint8_t sense = getSenseID(info->i2cRecord); +< if (sense) { +< DBGLOG("rad", "translateAtomConnectorInfoV2 got sense id %02X", sense); +< uint8_t txmit = 0, enc = 0; +< if (getTxEnc(info->usGraphicObjIds, txmit, enc)) +< callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); +< } else { +< DBGLOG("rad", "translateAtomConnectorInfoV2 failed to detect sense for translated connector"); +< } +< } +--- +> uint8_t sense = getSenseID(info->i2cRecord); +> if (sense) { +> DBGLOG("rad", "translateAtomConnectorInfoV2 got sense id %02X", sense); +> uint8_t txmit = 0, enc = 0; +> if (getTxEnc(info->usGraphicObjIds, txmit, enc)) +> callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); +> } else { +> DBGLOG("rad", "translateAtomConnectorInfoV2 failed to detect sense for translated connector"); +> } +> } +1171c2556 +< return code; +--- +> return code; +1175,1184c2560,2569 +< DBGLOG("rad", "starting controller " PRIKADDR, CASTKADDR(current_thread())); +< if (callbackRAD->forceVesaMode) { +< DBGLOG("rad", "disabling video acceleration on request"); +< return false; +< } +< +< callbackRAD->currentPropProvider.set(provider); +< bool r = FunctionCast(wrapATIControllerStart, callbackRAD->orgATIControllerStart)(ctrl, provider); +< DBGLOG("rad", "starting controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); +< callbackRAD->currentPropProvider.erase(); +--- +> DBGLOG("rad", "starting controller " PRIKADDR, CASTKADDR(current_thread())); +> if (callbackRAD->forceVesaMode) { +> DBGLOG("rad", "disabling video acceleration on request"); +> return false; +> } +> +> callbackRAD->currentPropProvider.set(provider); +> bool r = FunctionCast(wrapATIControllerStart, callbackRAD->orgATIControllerStart)(ctrl, provider); +> DBGLOG("rad", "starting controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); +> callbackRAD->currentPropProvider.erase(); +1186c2571 +< return r; +--- +> return r; +1190,1199c2575,2584 +< DBGLOG("rad", "starting legacy controller " PRIKADDR, CASTKADDR(current_thread())); +< if (callbackRAD->forceVesaMode) { +< DBGLOG("rad", "disabling legacy video acceleration on request"); +< return false; +< } +< +< callbackRAD->currentLegacyPropProvider.set(provider); +< bool r = FunctionCast(wrapLegacyATIControllerStart, callbackRAD->orgLegacyATIControllerStart)(ctrl, provider); +< DBGLOG("rad", "starting legacy legacy controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); +< callbackRAD->currentLegacyPropProvider.erase(); +--- +> DBGLOG("rad", "starting legacy controller " PRIKADDR, CASTKADDR(current_thread())); +> if (callbackRAD->forceVesaMode) { +> DBGLOG("rad", "disabling legacy video acceleration on request"); +> return false; +> } +> +> callbackRAD->currentLegacyPropProvider.set(provider); +> bool r = FunctionCast(wrapLegacyATIControllerStart, callbackRAD->orgLegacyATIControllerStart)(ctrl, provider); +> DBGLOG("rad", "starting legacy legacy controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); +> callbackRAD->currentLegacyPropProvider.erase(); +1201c2586 +< return r; +--- +> return r;