From 63c597d330f6dddf9fc77ba8d7605a8071b151c7 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Thu, 25 Apr 2013 13:41:51 -0700 Subject: [PATCH] Update path to Widevine MediaDrm engine credentials Use separate directories for unit test-generated credentials vs actual credentials, so the unit test credentials don't interfere with the real ones. related-to-bug: 8620943 Merge of: Update path to where CDM persistently stores data https://widevine-internal-review.googlesource.com/#/c/5300/ Rename Keybox File https://widevine-internal-review.googlesource.com/#/c/5240/ ... from the widevine CDM repo. Change-Id: Idefa484b3a2f71f723238f033460bf431ce4209b --- .../cdm/core/include/device_files.h | 5 +- libwvdrmengine/cdm/core/src/device_files.cpp | 27 +-- .../cdm/core/test/device_files_unittest.cpp | 28 +-- .../cdm/core/test/file_store_unittest.cpp | 167 ++++++++++-------- libwvdrmengine/level3/arm/libwvlevel3.a | Bin 292296 -> 293070 bytes libwvdrmengine/level3/mips/libwvlevel3.a | Bin 311368 -> 312138 bytes libwvdrmengine/level3/x86/libwvlevel3.a | Bin 303800 -> 304836 bytes 7 files changed, 123 insertions(+), 104 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index 5bf3e684..d9294ebd 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -14,10 +14,9 @@ class DeviceFiles { static bool RetrieveCertificate(std::string* certificate, std::string* wrapped_private_key); - static std::string GetPath(const char* dir, const char * filename); + static std::string GetBasePath(const char* dir); static const char* kBasePath; - static const char* kIdmPath; - static const char* kCencPath; + static const char* kPathDelimiter; static const char* kDeviceCertificateFileName; private: diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index dd6b4c05..3e9f8e9c 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -4,7 +4,8 @@ #include #include -#include +#include +#include #include "device_files.pb.h" #include "file_store.h" @@ -14,9 +15,8 @@ namespace wvcdm { // TODO(rfrias): Make this work for non-unix paths -const char* DeviceFiles::kBasePath = "/data/mediadrm"; -const char* DeviceFiles::kIdmPath = "/data/mediadrm/IDM"; -const char* DeviceFiles::kCencPath = "/data/mediadrm/IDM/CENC"; +const char* DeviceFiles::kBasePath = "/data/mediadrm/IDM"; +const char* DeviceFiles::kPathDelimiter = "/"; const char* DeviceFiles::kDeviceCertificateFileName = "cert.bin"; // Protobuf generated classes. @@ -123,12 +123,14 @@ bool DeviceFiles::StoreFile(const char* name, const std::string& data) { if (!name) return false; - if (!File::IsDirectory(kCencPath)) { - if (!File::CreateDirectory(kCencPath)) + std::string path = GetBasePath(kBasePath); + + if (!File::IsDirectory(path)) { + if (!File::CreateDirectory(path)) return false; } - std::string path = GetPath(kCencPath, name); + path += name; File file(path, File::kCreate | File::kTruncate | File::kBinary); if (file.IsBad()) { @@ -152,7 +154,7 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) { if (!data) return false; - std::string path = GetPath(kCencPath, name); + std::string path = GetBasePath(kBasePath) + name; if (!File::Exists(path)) { LOGW("DeviceFiles::RetrieveFile: %s does not exist", path.c_str()); @@ -185,12 +187,11 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) { return true; } -std::string DeviceFiles::GetPath(const char* dir, const char * filename) { +std::string DeviceFiles::GetBasePath(const char* dir) { // TODO(rfrias): Make this work for non-unix paths - std::string path = dir; - path += "/"; - path += filename; - return path; + std::stringstream ss; + ss << dir << getuid() << kPathDelimiter; + return ss.str(); } } diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index d6fdf1dc..8b2ed697 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -7,12 +7,13 @@ namespace wvcdm { TEST(DeviceFilesTest, StoreCertificate) { + std::string device_base_path = + DeviceFiles::GetBasePath(DeviceFiles::kBasePath); std::string device_certificate_path = - DeviceFiles::GetPath(DeviceFiles::kCencPath, - DeviceFiles::kDeviceCertificateFileName); + device_base_path + DeviceFiles::kDeviceCertificateFileName; - if (!File::Exists(DeviceFiles::kCencPath)) - EXPECT_TRUE(File::CreateDirectory(DeviceFiles::kCencPath)); + if (!File::Exists(device_base_path)) + EXPECT_TRUE(File::CreateDirectory(device_base_path)); if (File::Exists(device_certificate_path)) EXPECT_TRUE(File::Remove(device_certificate_path)); @@ -33,12 +34,13 @@ TEST(DeviceFilesTest, StoreCertificate) { } TEST(DeviceFilesTest, StoreCertificateInitial) { + std::string device_base_path = + DeviceFiles::GetBasePath(DeviceFiles::kBasePath); std::string device_certificate_path = - DeviceFiles::GetPath(DeviceFiles::kCencPath, - DeviceFiles::kDeviceCertificateFileName); + device_base_path + DeviceFiles::kDeviceCertificateFileName; - if (File::Exists(DeviceFiles::kCencPath)) - EXPECT_TRUE(File::Remove(DeviceFiles::kIdmPath)); + if (File::Exists(device_base_path)) + EXPECT_TRUE(File::Remove(device_base_path)); char test_buf[1200]; for (size_t i = 0; i < sizeof(test_buf); i++) { @@ -57,12 +59,13 @@ TEST(DeviceFilesTest, StoreCertificateInitial) { } TEST(DeviceFilesTest, RetrieveCertificate) { + std::string device_base_path = + DeviceFiles::GetBasePath(DeviceFiles::kBasePath); std::string device_certificate_path = - DeviceFiles::GetPath(DeviceFiles::kCencPath, - DeviceFiles::kDeviceCertificateFileName); + device_base_path + DeviceFiles::kDeviceCertificateFileName; - if (File::Exists(DeviceFiles::kCencPath)) - EXPECT_TRUE(File::Remove(DeviceFiles::kIdmPath)); + if (File::Exists(device_base_path)) + EXPECT_TRUE(File::Remove(device_base_path)); char test_buf[1200]; for (size_t i = 0; i < sizeof(test_buf); i++) { @@ -87,6 +90,7 @@ TEST(DeviceFilesTest, RetrieveCertificate) { certificate.size()) == 0); EXPECT_TRUE(memcmp(wrapped_private_key.data(), in_wrapped_private_key.data(), wrapped_private_key.size()) == 0); + EXPECT_TRUE(File::Remove(device_base_path)); } } diff --git a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp index 0400bed7..7384d8d9 100644 --- a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp @@ -1,16 +1,11 @@ // Copyright 2013 Google Inc. All Rights Reserved. +#include "device_files.h" #include "file_store.h" #include "gtest/gtest.h" namespace { // TODO(rfrias): Make this work for non-unix paths - const std::string kDataDrmDir = "/data/mediadrm"; - const std::string kIdmTestDir = "/data/mediadrm/IDMtest"; - const std::string kCencTestDir = "/data/mediadrm/IDMtest/CENCtest"; - const std::string kCencTestDirWithSlash = "/data/mediadrm/IDMtest/CENCtest/"; - const std::string kTestFile01 = "/data/mediadrm/IDMtest/CENCtest/file01.txt"; - const std::string kFileExists = "/system/bin/sh"; const std::string kDirExists = "/system/bin"; const std::string kFileDoesNotExist = "/system/bin/shxyxyxy"; @@ -27,102 +22,117 @@ TEST(FileTest, FileExists) { } TEST(FileTest, CreateDirectory) { - if (File::Exists(kCencTestDir)) - EXPECT_TRUE(File::Remove(kIdmTestDir)); - EXPECT_FALSE(File::Exists(kCencTestDir)); - EXPECT_TRUE(File::CreateDirectory(kCencTestDir)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - EXPECT_TRUE(File::Remove(kIdmTestDir)); - EXPECT_TRUE(File::CreateDirectory(kCencTestDirWithSlash)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - EXPECT_TRUE(File::Remove(kIdmTestDir)); + std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string dirWoDelimiter = dir.substr(0, dir.size()-1); + if (File::Exists(dirWoDelimiter)) + EXPECT_TRUE(File::Remove(dirWoDelimiter)); + EXPECT_FALSE(File::Exists(dirWoDelimiter)); + EXPECT_TRUE(File::CreateDirectory(dirWoDelimiter)); + EXPECT_TRUE(File::Exists(dirWoDelimiter)); + EXPECT_TRUE(File::Remove(dirWoDelimiter)); + EXPECT_TRUE(File::CreateDirectory(dir)); + EXPECT_TRUE(File::Exists(dir)); + EXPECT_TRUE(File::Remove(dir)); } TEST(FileTest, RemoveDir) { - if (!File::Exists(kCencTestDir)) - EXPECT_TRUE(File::CreateDirectory(kCencTestDir)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - EXPECT_TRUE(File::Remove(kCencTestDir)); - EXPECT_FALSE(File::Exists(kCencTestDir)); + std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + if (!File::Exists(dir)) + EXPECT_TRUE(File::CreateDirectory(dir)); + EXPECT_TRUE(File::Exists(dir)); + EXPECT_TRUE(File::Remove(dir)); + EXPECT_FALSE(File::Exists(dir)); } TEST(FileTest, OpenFileUsingConstructor) { - if (!File::Exists(kCencTestDir)) - EXPECT_TRUE(File::CreateDirectory(kCencTestDir)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - File::Remove(kTestFile01); - File file(kTestFile01, File::kCreate); + std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string path = dir + DeviceFiles::kDeviceCertificateFileName; + if (!File::Exists(dir)) + EXPECT_TRUE(File::CreateDirectory(dir)); + EXPECT_TRUE(File::Exists(dir)); + File::Remove(path); + File file(path, File::kCreate); EXPECT_TRUE(file.IsOpen()); file.Close(); - EXPECT_TRUE(File::Exists(kTestFile01)); + EXPECT_TRUE(File::Exists(path)); } TEST(FileTest, OpenFile) { - if (!File::Exists(kCencTestDir)) - EXPECT_TRUE(File::CreateDirectory(kCencTestDir)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - File::Remove(kTestFile01); + std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string path = dir + DeviceFiles::kDeviceCertificateFileName; + if (!File::Exists(dir)) + EXPECT_TRUE(File::CreateDirectory(dir)); + EXPECT_TRUE(File::Exists(dir)); + File::Remove(path); File file; - file.Open(kTestFile01, File::kCreate); + file.Open(path, File::kCreate); EXPECT_TRUE(file.IsOpen()); file.Close(); - EXPECT_TRUE(File::Exists(kTestFile01)); + EXPECT_TRUE(File::Exists(path)); } TEST(FileTest, RemoveDirAndFile) { - if (!File::Exists(kCencTestDir)) - EXPECT_TRUE(File::CreateDirectory(kCencTestDir)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - File file(kTestFile01, File::kCreate); + std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string path = dir + DeviceFiles::kDeviceCertificateFileName; + if (!File::Exists(dir)) + EXPECT_TRUE(File::CreateDirectory(dir)); + EXPECT_TRUE(File::Exists(dir)); + File file(path, File::kCreate); EXPECT_TRUE(file.IsOpen()); file.Close(); - EXPECT_TRUE(File::Remove(kTestFile01)); - EXPECT_TRUE(File::Remove(kCencTestDir)); - EXPECT_FALSE(File::Exists(kTestFile01)); - EXPECT_FALSE(File::Exists(kCencTestDir)); + EXPECT_TRUE(File::Remove(path)); + EXPECT_TRUE(File::Remove(dir)); + EXPECT_FALSE(File::Exists(path)); + EXPECT_FALSE(File::Exists(dir)); } TEST(FileTest, IsDir) { - if (!File::Exists(kCencTestDir)) - EXPECT_TRUE(File::CreateDirectory(kCencTestDir)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - File file(kTestFile01, File::kCreate); + std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string path = dir + DeviceFiles::kDeviceCertificateFileName; + if (!File::Exists(dir)) + EXPECT_TRUE(File::CreateDirectory(dir)); + EXPECT_TRUE(File::Exists(dir)); + File file(path, File::kCreate); EXPECT_TRUE(file.IsOpen()); file.Close(); - EXPECT_TRUE(File::Exists(kTestFile01)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - EXPECT_FALSE(File::IsDirectory(kTestFile01)); - EXPECT_TRUE(File::IsDirectory(kCencTestDir)); + EXPECT_TRUE(File::Exists(path)); + EXPECT_TRUE(File::Exists(dir)); + EXPECT_FALSE(File::IsDirectory(path)); + EXPECT_TRUE(File::IsDirectory(dir)); } TEST(FileTest, IsRegularFile) { - if (!File::Exists(kCencTestDir)) - EXPECT_TRUE(File::CreateDirectory(kCencTestDir)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - File file(kTestFile01, File::kCreate); + std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string path = dir + DeviceFiles::kDeviceCertificateFileName; + if (!File::Exists(dir)) + EXPECT_TRUE(File::CreateDirectory(dir)); + EXPECT_TRUE(File::Exists(dir)); + File file(path, File::kCreate); EXPECT_TRUE(file.IsOpen()); file.Close(); - EXPECT_TRUE(File::Exists(kTestFile01)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - EXPECT_TRUE(File::IsRegularFile(kTestFile01)); - EXPECT_FALSE(File::IsRegularFile(kCencTestDir)); + EXPECT_TRUE(File::Exists(path)); + EXPECT_TRUE(File::Exists(dir)); + EXPECT_TRUE(File::IsRegularFile(path)); + EXPECT_FALSE(File::IsRegularFile(dir)); } TEST(FileTest, WriteReadTextFile) { - if (!File::Exists(kCencTestDir)) - EXPECT_TRUE(File::CreateDirectory(kCencTestDir)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - File::Remove(kTestFile01); + std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string path = dir + DeviceFiles::kDeviceCertificateFileName; + if (!File::Exists(dir)) + EXPECT_TRUE(File::CreateDirectory(dir)); + EXPECT_TRUE(File::Exists(dir)); + File::Remove(path); const char* test_string = "This is a test"; - File file1(kTestFile01, File::kCreate); + File file1(path, File::kCreate); EXPECT_TRUE(file1.IsOpen()); EXPECT_TRUE(file1.Write(test_string, strlen(test_string)+1)); file1.Close(); - EXPECT_TRUE(File::Exists(kTestFile01)); + EXPECT_TRUE(File::Exists(path)); char buf[100]; - File file2(kTestFile01, File::kReadOnly); + File file2(path, File::kReadOnly); EXPECT_TRUE(file2.IsOpen()); EXPECT_EQ((ssize_t)strlen(test_string)+1, file2.Read(buf, sizeof(buf))); file2.Close(); @@ -130,23 +140,25 @@ TEST(FileTest, WriteReadTextFile) { } TEST(FileTest, WriteReadBinaryFile) { - if (!File::Exists(kCencTestDir)) - EXPECT_TRUE(File::CreateDirectory(kCencTestDir)); - EXPECT_TRUE(File::Exists(kCencTestDir)); - File::Remove(kTestFile01); + std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string path = dir + DeviceFiles::kDeviceCertificateFileName; + if (!File::Exists(dir)) + EXPECT_TRUE(File::CreateDirectory(dir)); + EXPECT_TRUE(File::Exists(dir)); + File::Remove(path); unsigned char test_buf[600]; for (size_t i = 0; i < sizeof(test_buf); i++) { test_buf[i] = i % 128; } - File file1(kTestFile01, File::kCreate | File::kBinary); + File file1(path, File::kCreate | File::kBinary); EXPECT_TRUE(file1.IsOpen()); EXPECT_TRUE(file1.Write(test_buf, sizeof(test_buf))); file1.Close(); - EXPECT_TRUE(File::Exists(kTestFile01)); + EXPECT_TRUE(File::Exists(path)); char buf[1000]; - File file2(kTestFile01, File::kReadOnly); + File file2(path, File::kReadOnly); EXPECT_TRUE(file2.IsOpen()); EXPECT_EQ((ssize_t)sizeof(test_buf), file2.Read(buf, sizeof(buf))); file2.Close(); @@ -154,21 +166,24 @@ TEST(FileTest, WriteReadBinaryFile) { } TEST(FileTest, FileSize) { - if (!File::Exists(kCencTestDir)) - EXPECT_TRUE(File::CreateDirectory(kCencTestDir)); - File::Remove(kTestFile01); + std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string path = dir + DeviceFiles::kDeviceCertificateFileName; + if (!File::Exists(dir)) + EXPECT_TRUE(File::CreateDirectory(dir)); + File::Remove(path); unsigned char test_buf[600]; for (size_t i = 0; i < sizeof(test_buf); i++) { test_buf[i] = i % 128; } - File file1(kTestFile01, File::kCreate | File::kBinary); + File file1(path, File::kCreate | File::kBinary); EXPECT_TRUE(file1.IsOpen()); EXPECT_TRUE(file1.Write(test_buf, sizeof(test_buf))); file1.Close(); - EXPECT_TRUE(File::Exists(kTestFile01)); + EXPECT_TRUE(File::Exists(path)); - EXPECT_EQ((ssize_t)sizeof(test_buf), File::FileSize(kTestFile01)); + EXPECT_EQ((ssize_t)sizeof(test_buf), File::FileSize(path)); + EXPECT_TRUE(File::Remove(dir)); } } diff --git a/libwvdrmengine/level3/arm/libwvlevel3.a b/libwvdrmengine/level3/arm/libwvlevel3.a index 8dcf6942f31d0ac5e4e3db4050bf1545df8ee0fb..27d7c432be0a277c3641964b9ccf1bdc7c1a1c30 100644 GIT binary patch delta 7947 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 N+%h-nzsOPN{|8Xp7AXJ# delta 7539 zcmcIp3vg7`89w*k&62EnY*NT;WA-K?$z~yBc?ATN1ePTdiK&p5s$kX!k}4WYtyq)` z)CUc(3_)~l*sH-5|*-K;jc7-OF?R?>Wx zn|6f>Te=v#8tusaj8W4};Y#@F1C0GWa8;UiwGvStY5v?RZQ7LvTxEaliVN zRp`~=)6s%xj#~FC%Yu$j;#j5o@S5ACNPoXPYQ%`zk+YTs`(J7A+Bwe|8qzspQANnh zzyIpH?@Mdcf~CR4K%h|#kv^QST<6htuFhGs_0i2MSaEt|>qCw6rxsA3x^K?cD=%gY z1oOfT(s*zA#gj~3H!zv0A?d}0&Cbo_QxGc5_UW0bFXqUBP?;_p7Voynivpo-)1)5V zv#b9wJGvK`iMF7%=n`XHJZ=5E+E=eQLTcDu&=@A~qNbYJtgxmoxB2nl@?b)*8rnI) z+|(nF_k~7>eyZ1z0vAtK&A$1I%MUd+JszZ9qp~l1@3i?n;ieg$lBV`)^2rX5dyLdu zv}l`W^R)3Fl!c73%P&+9I8rDo+*GJFv7%dYogumR{oB8qD~;{RWxh+N{Wmjz=TYZ5 zIobI+OJ+q{Q0e_>e)53Aq_O|ZWn}}Wi~n_Yxf)`{$>&msBoCz4Q#xsTY4~FH!f;`u zr`JyeMZ8 zU(?}{TO@vLS0+Ew;Z9mBQWWu19o{lGwmEZ!OWQ@h=f{9Gwf^6|Fa-jsmFeFT4Zw6d%Vw-~R*t-EMbE75cLU&T{VAi z!Urtjbmp0;o)zKd^aT`}KJAoYj!j0) zTS|!p3Ggfdewa);y$#AnLi47ohRMWTAQH-QUjYfgm$qlaX(%X=hbP*b0r7KZIri-Y629BTY`Ai4MPQm}9p? zGA5MjQz(&H=XLm5@3I~X?i+<4#pMl&zfXtqJ6_0|^o1?D7tzPyO(1L0OpqBO+60;H z)E}lvhB*c@8|kPRg%X({E8%Aa8O77)Mn9FQ6Mhu;lntbcdu{t5c6Ul$R#tM`-4}e*-_Vo8c;y+o79emLxmLF+aXxU)FVbuv({m z9;*1@MOA)+W>&dQLQ_veIl)S`YkY{Omoc4D>_S^Pr`a*gb@Jj4k098vr_B2T8#h7(b;GbpNiH%LH~kWaB3w z85{T?PZenKF;nyy^PHj+D}LFh#K(vz&lA^JC8=`t0P&DRKug^we40Z#16n!3xj;Dp?TlDyXb7GlD zQ1BpG`|%LbRys%uo5!9MDs7??bZY=(^Z3ez1$_Db)JUL4ZjJQT$Z8~TBQ7iG(nA+@ z7B03_B9{We|3K}rBoP_LIR^eWT`;wh{`80eL_P_AaxLZ>&*47>|ALN|#< ze6LMkEc_k1e=S>S>wns&Z!mRQe@eL3Gzk3-n||D;zi-no*mNguCK~YpvJ{G(AV>dLw8A)k@ENC0I%MW$EAc}-6_A@De>Z|*GBr2ltli) ztMBoLoq5SimfRJ7U?E$4-`!0=ZcRhwZe#Ztu*X@y!YsYexm$e8QgCk=dh>3;J!3r3 zE(}F4kMuRAEI!o{M}glG$LzmF z9GxeKV}z%PV?p}BX+h3|(}MJaE0dXiSSVABPn-%~t}fG#q}+gB8qrj6kbk1VV|NCC zs4!6>3|1mP@suYMM>RL`ea2HQ?=tQ<`FY;_dZzUs5FM|N z%(wmnqWj#LI}(JUyZ&tcGK=%mEsfH_mPUE?Xlum5ijDG`kgyy)_K}>C8rOZ{Gg%vA z$#Sse-*VrzR>S&a<*{qb1o$t=g4uBi*4(2^m=R|=^BZMOAkK30$I7YkahB=njuF?# LS=Q7!ru+W`NA*}g diff --git a/libwvdrmengine/level3/mips/libwvlevel3.a b/libwvdrmengine/level3/mips/libwvlevel3.a index cd726b47c51c559e34df60012b8a0cb4b707dd1e..fe57d9d06c78a22c9678ba06b0ebc46f69194d7a 100644 GIT binary patch delta 6368 zcmcgw3vg7`8UF9t%_hr|2!2F zGxvVyf1K|>=RdEzd#o|-#FJ^w+0oLX(!$arO)J#6INzF9TsooHRBLqui0Cj;_G4f1 z6a7C3SDhgGvd@dNiJVW=Dg3Yc(F;Vc4gN}>Xgr}3Iz(UN6a6}aT_slZ%4o%nC+JH( zjW3Y+Z_kD^BsSgu#V3cAyYI1{?(4`EIYh4JZM=1> zVz$KL5=R<)4vJj;Vs^kN%uz<`j~y@5YhTMYj@%#u`tB0}ee&@XqavaNjM^MU(}%yN z8MPx(IdVLubIrpQiYvFCI$wWepQt<4|I<98XJaZI?#QOS9l^#%%BNR5BGlOtru`in z?du3?ZKRU=aylMwpXLg#XZdg}xH(6JnwJZ1AJj;&O`#h`vdu%|0_o&#GPJvtY`x+4 z*+%|mF;GACTle+V2+85v5oQZQwjqQ`i_qM(n%eU;N(d7@9%Jexni^(1kxPF)dVBQN z7S%msBGJ-4Zn}3*5-r;^knY=&Ld&;R(+hbIQg>d$lut3Jsm+C;=A~jy)p-|;c3RN$mep*@-Y}k#=zurQ0OPfOF;eYg3r$0pd%;W?$O;pp4 zyk^>&=dD6l9;CMIW(F|oU_>)Bz+D`odcqs1J?5nrmF)_ortbVCqBI_o zH5?+OAco<`w&=4~4|4%KSFPUe8rSTYlnOa1OOdU5I@D1|2Rmx$Ku0wus0tHQiR>++ zE`8?OAz5YUAJNmuT8o)EqGy0bBZ&^^(Y0C8w?}5sZDz;PDY2=Hx}JEPE<#RiB1&os zX{pM6q^qIIzbFdzZc`}}`e$RpWXQmPM>KIJf{{j{&>t~}!dwm<^~3(#(s{u4;4*1H z&v7hRQidhnj$S<(qbSHyF|pR@e-*YeSSAXQTD##ikg-0WcElpI^D-f?uY6>708I<9 zJqel3gik2aPOE8q{z|G-Z4PV_b0M+JYp_qTVn&G$nv0Xk`uj7m!qy?cdNT;opUsXb z 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 diff --git a/libwvdrmengine/level3/x86/libwvlevel3.a b/libwvdrmengine/level3/x86/libwvlevel3.a index 8ea63a571d98e0f1c0ae035e586487ab0a5ca7ef..afc987a33ab0276a37aa8342805b891e944fbfce 100644 GIT binary patch delta 5229 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 V?WP7(vwyo?nz-@2&@2@#`5VCIK|cTh delta 4200 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 SB!@DU%PSHW9c)x=^Zo`u@WZ|U