هناك لعبة في Verbis Virtus مع ميكانيكا غير عادية - تعاويذ المدلى بها باستخدام ميكروفون.
هذا ليس محاكاة لـ Hmayak Hakobyan ، إنه لغز أول شخص له عناصر تحكم غير نمطية.
لهذا ، تستخدم اللعبة مكتبة التعرف على الكلام Sphinx.
تبدو الفكرة مثيرة للاهتمام ، لكن التنفيذ خرج (كثيرًا ما يفتقد التقدير) ، وبصراحة مزعج للإدلاء به بعد أول 20 دقيقة.
حول كيف تبدو من الخارج - صامتة بشكل عام.
لم يترك المطورون ، للأسف ، القدرة على التحكم في التعاويذ من لوحة المفاتيح ، وقررت إصلاحها.
كانت الفكرة الأولى هي إجراء تغييرات على مكتبة Sphinx ، لأنها مفتوحة المصدر. ومع ذلك ، وجدت أن هناك مجموعة من إصدارات هذه المكتبة.
بعد تجربة ثلاثة منهم (تقريبًا تقريبًا وقت إصدار اللعبة) ، ما زلت لا أجد الخيار الصحيح ، لأن كل واحد منهم كان لديه أي اختلافات (على الأقل فيما يتعلق بمجموعة الوظائف التي تم تصديرها).
لذلك قررت أن ألفها على قمة المكتبة الأصلية من اللعبة.
للقيام بذلك ، اتبعت النهج المقترح في مقالة
توليد .DLL Wrappers .
جوهرها هو أنه يمكنك التفاف أي مكتبة دون أي معرفة بالمعلمات وأنواع الوظائف المصدرة ، فقط أسمائهم (والتي يمكن استخراجها حتى مع محرر نصوص) كافية.
يتم إنشاء قائمة التصدير باستخدام ملف def للنموذج:
EXPORTS func1=_func1 @1 func2=_func2 @2
مغلفة وظيفة أنفسهم:
_declspec(naked) void _func1() { __asm jmp dword ptr [procs + 1 * 4]; }
هذا يلغي مشاكل تمرير الوسائط وإرجاع قيم الوظائف الأصلية.
أولا ، كان مطلوبا قليلا من الهندسة العكسية. أنا خلقت المجمع مع الإضافة الوحيدة - تسجيل أسماء الوظائف التي تسمى.
لذلك حددت أين ومتى وكيف يعمل منطق المكتبة الأساسية.
اتضح أنه في البداية تم جمع عدد معين من العينات الخام من الميكروفون بواسطة الدالة ps_process_raw () ، ثم تم اتخاذ القرار نفسه في وظيفة ps_get_hyp ().
في وقت لاحق (بعد فوات الأوان) ، ما زلت أعتقد أنه سيكون من المفيد النظر في وثائق Sphinx أولاً (حيث تم وصفها كلها).
تقرر إضافة وظيفة ps_process_raw () إلى تعريف لحالة المفاتيح التي ستكون مسؤولة عن نوبات.
للقيام بذلك ، تحتاج إلى تعيين هذه المفاتيح. نقوم بذلك في DllMain () ، إلى جانب الحصول على عناوين الوظائف الأصلية. إليك بعض الإعلانات التجارية:
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { HINSTANCE hinst_dll; if (fdwReason == DLL_PROCESS_ATTACH) { hinst_dll = LoadLibraryA("pocketsphinx_orig.dll"); if (!hinst_dll) return 0; for (i = 0; i < 93; i++) procs[i] = GetProcAddress(hinst_dll, import_names[i]); for (i = 0; i < 256; i++) { _itoa(i, &buf[i][0], 10); GetPrivateProfileStringA("main", &buf[i][0], 0, &buf[i][0], MAX_PATH, ".\\settings.ini"); } i = 0; } else if (fdwReason == DLL_PROCESS_DETACH) FreeLibrary(hinst_dll); return 1; }
يحتوي ملف settings.ini على النموذج:
[main] 49=String 1 50=String 2
المجموع ، في صفيف buf سيكون هناك خطوط تقابل تعاويذ. علاوة على ذلك ، فإنهم سوف يكذبون بواسطة مؤشرات المقابلة للمفاتيح اللازمة.
سنحدد حالة المفاتيح كما يلي:
void find_key() { if(!i) { for (i = 0; i < 256; i++) if (buf[i][0]) if (GetAsyncKeyState(i) >> 1) { i = (int)&buf[i][0]; return; } if (i == 256) i = 0; } }
سيظهر مجمّع الدالة ps_process_raw () كما يلي:
_declspec(naked) void _ps_process_raw() { find_key(); __asm jmp dword ptr [procs + 78 * 4]; }
وهذا هو ، إذا كان من الضروري ، في الوقت الذي كان من الضروري فيه الدخول إلى الميكروفون ، ضغط المستخدم على أحد المفاتيح ، وتم حفظ المؤشر إلى السطر المقابل للمفتاح المضغوط في المتغير العام i.
الانتهاء من الاستعدادات ، فقد حان الوقت لتنفيذ الوظائف الأساسية.
من الضروري تحديد ما إذا كان المستخدم قام بالضغط على زر الإملاء ، وإذا كان الأمر كذلك ، فقم بتغيير قيمة الإرجاع في دالة ps_get_hyp ().
سيتطلب ذلك معالجة بسيطة للمكدس:
_declspec(naked) void _ps_get_hyp() { static unsigned int return_address; _asm {
الوظيفة الرئيسية في قطعة مع التعليق "استبدال النتيجة (إذا تم الضغط على المفتاح)".
إذا كان المؤشر في المتغير العام ، فإننا نستبدل النتيجة التي تم إرجاعها ونعيد تعيين المتغير الشامل.
وإذا لم يكن الأمر كذلك ، فإننا نترك كل شيء دون تغيير.
وبالتالي ، يمكنك الاستمرار في بث الميكروفون ، أو يمكنك استخدام الأزرار (لها الأولوية). يتم تحقيق الهدف.
نعم ، هناك نقاط ملتوية في الحل.
على سبيل المثال ، تمرير مؤشر عبر متغير عمومي ، يسمى أيضًا i (قررت استخدامه مرة أخرى بعد التهيئة في DllMain).
تسلق كومة شخص آخر غير مقبول بطريقة أو بأخرى (لم أفكر في كيفية القيام بذلك بطريقة مختلفة).
ومع ذلك ، فإن الحل يعمل تماما. الكود الرئيسي أقل من 100 سطر ، بالنسبة للجزء الأكبر ، كل شيء تافه.
شفرة المصدرملف defملف إعدادات + ثنائيالتثبيت:
- في المجلد \ In Verbis Virtus \ Binaries \ Win32 \ ، أعد تسمية pocketsphinx.dll الأصلي إلى pocketsphinx_orig.dll
- ضع المجمع القريب pocketsphinx.dll
- في المجلد \ In Verbis Virtus \ Binaries \ Win32 \ UserCode ، ضع settings.ini
يتم قبول النقد والاقتراحات.