
مفكك شيفرات & مفكك تجريبي لسكريبتات TLOU2 DC.
dconstruct هي أداة هندسة عكسية لملفات DC-Script المستخدمة في The Last of Us Part II. تحتوي على مفكك ومترجم عكسي.
يُنتج ملفات .asm تحتوي على الهياكل المفككة والبايت كود، بالإضافة إلى ملفات .dcpl (لغة DC شبه) تحتوي على كود شبيه بلغة C.
يمكنك أيضًا إجراء تعديلات على الملفات عبر سطر الأوامر، بما في ذلك استبدال هياكل كاملة بجهد ضئيل. وهذا يجعل إنشاء التعديلات (mods) التي تغير ببساطة بضع قيم داخل ملفات .bin أمرًا سهلاً للغاية.
-e، وإنشاء ملفات جديدة يمكن استخدامها للتعديلات (mods)أولاً، يُوصى بنقل مجلد dconstruct الذي تم فك ضغطه إلى موقع آمن، مثل C:\Program Files.
لجعل استخدام dconstruct سهلاً قدر الإمكان، يُوصى بإضافة مجلد .\bin داخل مجلد dconstruct إلى PATH. يمكنك معرفة المزيد هنا، أو اتباع هذه الخطوات السريعة:

dconstruct --about. يجب أن ترى بعض المخرجات من البرنامج ولا توجد رسالة خطأ.قم بتشغيل أمر مثل هذا في سطر الأوامر لإنشاء أول ملف مفكك لك:```shell dconstruct my_bin_file.bin
سيؤدي هذا بعد ذلك إلى إخراج ملف باسم `my_bin_file.bin.asm` في نفس الدليل الذي يحتوي على ملف الإدخال الخاص بك. يمكنك بعد ذلك فتح هذا الملف باستخدام محرر نصوص/أكواد. أوصي باستخدام شيء مثل VSCode الذي يوفر ميزات بحث متقدمة وهو جيد في التعامل مع الملفات الكبيرة. لا يُنصح باستخدام برنامج "المفكرة" القياسي في Windows.
لفك ترجمة ملف، أضف العلامة `--decompile` عند تشغيل الأمر.
# وسائط سطر الأوامر
- `-i` - ملف الإدخال أو المجلد. يمكن حذفه إذا مررت مسار الإدخال كوسيطة أولى.
- `-o` - مسار الإخراج. إذا كان مسار الإدخال الخاص بك هو مجلد، فلا يمكن أن يكون هذا ملفًا. إذا لم يتم تحديد إخراج، فسيتم وضع ملف .txt بجوار ملف الإدخال. إذا كان الإدخال مجلدًا ولم يتم تحديد إخراج، فسيقوم البرنامج بإنشاء دليل "output" في دليل العمل الحالي ووضع جميع الملفات فيه.
- يتم تحميل sidbase من `sidbase.bin` الموجود بجوار الملف القابل للتنفيذ.
- `--no_decompile` - لا يصدر كودًا مفكوكًا زائفًا إلى ملف .dcpl. سيتم وضع الملف بجوار ملف .asm. هذا خطأ افتراضيًا.
- `--no_optimize` - لا يحسن وينظف كود dcpl. يتضمن ذلك تضمين استدعاءات الدوال، وإزالة المتغيرات غير المستخدمة، وتحويل حلقات for المتوافقة إلى حلقات foreach، وتحويل بعض سلاسل if-else إلى تعبيرات match.
- `--pascal_case` - يحول أسماء دوال الألعاب إلى حالة Pascal في إخراج dcpl، على سبيل المثال get-boolean -> GetBoolean.
- `--graphs` - يصدر ملفات .svg تحتوي على رسوم بيانية لتدفق التحكم لجميع الدوال المفكوكة. يحصل كل ملف .bin على مجلده الخاص الذي يحتوي على جميع رسومه البيانية. هذا **يبطئ** سرعة فك الترجمة بشكل كبير، لذا لا يُنصح به عند فك ترجمة عدد كبير من الملفات في نفس الوقت.
- `--emit_once` - يمنع إصدار نفس الهيكل مرتين في التفكيك. إذا ظهر هيكل عدة مرات، فسيتم إصدار المثيل الأول فقط بالكامل، وسيتم استبدال جميع المناسبات الأخرى بعلامة `ALREADY_EMITTED`. يمكن أن يقلل هذا من حجم الملف بشكل كبير.
- `-e` - إجراء تعديل. مزيد من المعلومات في القسم أدناه.
- `--edit_file` - تقديم ملف تحرير. يحتوي ملف التحرير على تعديل واحد لكل سطر. يستخدم نفس بناء الجملة مثل العلامة -e.
# ما هو المفكك؟
[المفكك](https://en.wikipedia.org/wiki/Disassembler) هو أداة تقرأ التعليمات الثنائية (المعروفة أيضًا باسم [bytecode](https://en.wikipedia.org/wiki/Bytecode) أو [machine code](https://en.wikipedia.org/wiki/Machine_code)) وتترجم كل منها إلى نسخة قابلة للقراءة البشرية تسمى [mnemonic](https://en.wikipedia.org/wiki/Assembly_language#Mnemonics). بشكل عام، لا تحاول المفككات تفسير الكثير حول معنى هذه التعليمات وتقوم فقط بتحويلها 1-1 إلى إصداراتها القابلة للقراءة. على سبيل المثال، التعليمات:```arm
15 00 00 00
4A 01 01 00
43 31 01 00
1C 00 00 01
يتم تفكيكها إلى الإصدارات القابلة للقراءة البشرية التالية:```arm LookupPointer r0, 0 LoadStaticU64Imm r1, 1 Move r49, r1 CallFf r0, r0, 1
جميع الأرقام في البايت كود مكتوبة بنظام [الست عشري](https://en.wikipedia.org/wiki/hexadecimal). يمثل العمود الأول في كل صف `opcode`، أو نوع التعليمات التي سيتم تنفيذها. العمود التالي هو سجل الوجهة، حيث سيتم تخزين نتيجة العملية. آخر عمودين هما المعاملان 1 و2، وهما إما سجلات أو أرقام حرفية سيتم تنفيذ العملية عليها. ليست كل التعليمات تستخدم جميع البايتات الأربعة، على سبيل المثال، تعليمة `LookupPointer` الأولى تحتاج فقط إلى معامل واحد.
يضيف مفكك dconstruct أيضًا بعض المعلومات الإضافية المصممة لتسهيل قراءة التعليمات بعض الشيء. كما يقوم بإدراج تسميات (مثل `L_0`) لتسهيل تتبع الفروع في الكود.```arm
15 00 00 00 LookupPointer r0, 0 r0 = ST[0] -> <is-player-abby?>
4A 01 01 00 LoadStaticU64Imm r1, 1 r1 = ST[1] -> <player>
43 31 01 00 Move r49, r1 r49 = player
1C 00 00 01 CallFf r0, r0, 1 r0 = is-player-abby?(player)
2F 0D 00 00 BranchIfNot r0, 0xD IF NOT r0 => L_0
هذا مفيد عندما تريد النظر إلى المحتويات الخام للملف دون أن يقوم البرنامج بالكثير من التخمينات. لكن قراءته يمكن أن تكون صعبة للكتل الكبيرة من الكود، حيث لا يوجد هيكل على الإطلاق. وهنا يأتي دور المفكك.
المفكك هو عكس المترجم. المترجم هو برنامج يأخذ الكود المكتوب بواسطة البشر (مثل C، Java، C++، ...) وينتج تعليمات الآلة. في حالة TLOU2 والعديد من ألعاب ND الأخرى، لغة البرمجة النصية المستخدمة تسمى 'DC'، وهي في الأساس مجرد نسخة من لغة البرمجة Racket، و"الآلة" هي اللعبة نفسها التي تنفذ التعليمات أثناء تشغيل اللعبة. بشكل أساسي، يكتب المبرمجون بلغة DC ويستخدمون مترجمًا لتحويل هذا الكود إلى ملفات .bin التي تُشحن مع اللعبة.
المفكك يأخذ الكود المُفكك من الأعلى وينتج ما يُعرف بالشبه شيفرة. الشبه شيفرة هي محاولة لإعادة بناء الكود المصدري الأصلي الذي تم استخدامه لتوليد التعليمات الخام. هذا يهدف إلى جعل فهم الكود أسهل بكثير، ومع ذلك، فإن عملية توليد الشبه شيفرة معقدة للغاية لأن هناك العديد من الإصدارات المختلفة من الكود المصدري التي يمكن أن تولد الكود البايتي النهائي، بالإضافة إلى التحسينات التي تحدث أثناء التجميع.
حاليًا، مخرجات مفكك dconstruct ليست مشابهة من الناحية الصياغية لـ DC الأصلي. DC (أي Racket) هي لغة برمجة وظيفية ذات صياغة فريدة تجعلها غير مقروءة بشدة للمبرمجين الذين ليسوا معتادين عليها. لهذا السبب، اخترت جعل الشبه شيفرة تبدو أشبه بلغة C في الوقت الحالي، والتي يجب أن تكون أسهل في القراءة لمعظم الناس. ومع ذلك، من المخطط إنشاء صياغات إضافية تشمل Racket بالإضافة إلى نسخة Python.
43 00 31 00 15 01 00 00 43 02 00 00 43 31 02 00 1B 01 01 01 43 02 00 00 40 03 01 00 24 02 02 03 2F 0B 02 00 40 02 02 00 2D 0C 00 00 40 02 03 00 43 03 02 00 15 04 04 00 43 05 01 00 43 31 05 00 1C 04 04 01 07 03 03 04 43 01 03 00 00 01 01 00
من هذا، من المستحيل تقريبًا معرفة ما يفعله الكود.
## الكود المفكك مع التسميات وجدول الرموز```arm
sqrt-sign = script-lambda [0x9A8D8] {
[1 args]
0000 0x09A928 43 00 31 00 Move r0, r49 r0 = arg_0
0001 0x09A930 15 01 00 00 LookupPointer r1, 0 r1 = ST[0] -> <absf>
0002 0x09A938 43 02 00 00 Move r2, r0 r2 = arg_0
0003 0x09A940 43 31 02 00 Move r49, r2 r49 = arg_0
0004 0x09A948 1B 01 01 01 Call r1, r1, 1 r1 = absf(arg_0)
0005 0x09A950 43 02 00 00 Move r2, r0 r2 = arg_0
0006 0x09A958 40 03 01 00 LoadStaticFloatImm r3, 1 r3 = ST[1] -> <0.000000>
0007 0x09A960 24 02 02 03 FGreaterThanEqual r2, r2, r3 r2 = r2 >= r3
0008 0x09A968 2F 0B 02 00 BranchIfNot r2, 0xB IF NOT r2 => L_0
0009 0x09A970 40 02 02 00 LoadStaticFloatImm r2, 2 r2 = ST[2] -> <1.000000>
000A 0x09A978 2D 0C 00 00 Branch 0xC GOTO => L_1
L_0:
000B 0x09A980 40 02 03 00 LoadStaticFloatImm r2, 3 r2 = ST[3] -> <-1.000000>
L_1:
000C 0x09A988 43 03 02 00 Move r3, r2 r3 = -1.000000
000D 0x09A990 15 04 04 00 LookupPointer r4, 4 r4 = ST[4] -> <sqrt>
000E 0x09A998 43 05 01 00 Move r5, r1 r5 = RET_absf
000F 0x09A9A0 43 31 05 00 Move r49, r5 r49 = RET_absf
0010 0x09A9A8 1C 04 04 01 CallFf r4, r4, 1 r4 = sqrt(RET_absf)
0011 0x09A9B0 07 03 03 04 FMul r3, r3, r4 -1.000000 = -1.000000 * RET_sqrt
0012 0x09A9B8 43 01 03 00 Move r1, r3 r1 = -1.000000
0013 0x09A9C0 00 01 01 00 Return r1 Return
SYMBOL TABLE:
0000 0x09A9C8 function: absf
0001 0x09A9D0 float: 0.000000
0002 0x09A9D8 float: 1.000000
0003 0x09A9E0 float: -1.000000
0004 0x09A9E8 function: sqrt
}
الكود الآن أكثر قابلية للقراءة، ولكن حتى الفرع الواحد مزعج في القراءة إذا لم تكن معتادًا على قراءة لغة التجميع.
بدون الخوض في التفاصيل أكثر من اللازم هنا، يقوم رسم بياني لتدفق التحكم (CFG) بتقسيم كود التجميع إلى "عقد" على طول تعليمات التفرع المختلفة. هذا أمر بالغ الأهمية عندما نحلل الكود لمعرفة أين يمكن أن ينحرف "تدفق" البرنامج إلى مسارات مختلفة، مما قد يتطلب منا إنشاء متغيرات، عبارات if، حلقات for، إلخ. يجب إنشاء هذه الرسوم البيانية في الخلفية، ولكن يمكنك طباعتها كصور باستخدام علم البرنامج --graphs.
u64? sqrt-sign(f32 arg_0) { f32 var_1; if (arg_0 >= 0.00) { var_1 = 1.00; } else { var_1 = -1.00; } return var_1 * sqrt(absf(arg_0)); }
الآن أصبح الغرض من الدالة واضحًا جدًا، نحن نأخذ القيمة المطلقة للوسيط، ونأخذ الجذر التربيعي لتلك القيمة، ثم نضربها في الإشارة الأصلية للوسيط. على سبيل المثال، `sqrt-sign(-9) = -3`.
## تمريرات التحسين
يطبق dconstruct تلقائيًا تمريرات التحسين على الكود الزائف. فيما يلي بعض الأمثلة:
### تضمين استدعاء الدالة
#### قبل```c
u64? set-arrow-explosive-handle-rootvars() {
u64? var_0 = get-uint64(fx-handle, self);
u64? var_1 = get-float(kill, self);
set-effect-float(var_0, killradius, var_1);
u64? var_2 = get-uint64(fx-handle, self);
u64? var_3 = get-float(strong, self);
set-effect-float(var_2, strongradius, var_3);
u64? var_4 = get-uint64(fx-handle, self);
u64? var_5 = get-float(weak, self);
u64? var_6 = set-effect-float(var_4, weakradius, var_5);
return var_6;
}
u64? set-arrow-explosive-handle-rootvars() { set-effect-float(get-uint64(fx-handle, self), killradius, get-float(kill, self)); set-effect-float(get-uint64(fx-handle, self), strongradius, get-float(strong, self)); return set-effect-float(get-uint64(fx-handle, self), weakradius, get-float(weak, self)); }
### حلقات foreach
### قبل```c#
u64? bmm-deactivate-all(u64? arg_0) {
u64? var_0 = darray-count(arg_0);
begin-foreach();
for (u64 i = 0; i < var_0; i++) {
u64? var_1 = darray-at(arg_0, i);
u16 var_2;
if (var_1 && *(u16*)(var_1 + 12) == 7) {
var_2 = *(u64*)var_1;
} else if (var_1 && *(u16*)(var_1 + 12) == 5) {
var_2 = *(u64*)var_1;
} else if (var_1 && *(u16*)(var_1 + 12) == 4) {
var_2 = *(u64*)var_1;
} else {
var_2 = 0;
}
net-send-event-all(deactivate, var_2);
}
u64? var_3 = end-foreach();
return var_3;
}
u64? bmm-deactivate-all(u64? arg_0) { foreach (u64? var_1 : arg_0) { u16 var_2; if (var_1 && (u16)(var_1 + 12) == 7) { var_2 = (u64)var_1; } else if (var_1 && (u16)(var_1 + 12) == 5) { var_2 = (u64)var_1; } else if (var_1 && (u16)(var_1 + 12) == 4) { var_2 = (u64)var_1; } else { var_2 = 0; } net-send-event-all(deactivate, var_2); } }
### تعبيرات المطابقة
### قبل```scala
string #C57EE0A64537AE8F(u16 arg_0) {
string var_0;
if (arg_0 == 0) {
var_0 = "Militia";
} else if (arg_0 == 1) {
var_0 = "Scars";
} else if (arg_0 == 2) {
var_0 = "Rattlers";
} else if (arg_0 == 3) {
var_0 = "Infected";
} else if (arg_0 == 4) {
var_0 = "Max Num Factions";
} else {
var_0 = "Invalid";
}
return var_0;
}
string #C57EE0A64537AE8F(u16 arg_0) { return match (arg_0) { 0 -> "Militia" 1 -> "Scars" 2 -> "Rattlers" 3 -> "Infected" 4 -> "Max Num Factions" else -> "Invalid" }; }
## مثال على بنية مفككة```c
*ellie-weapons* = symbol-array [0x00190] {
[0] int: 6
[1] int: 0
[2] array [0x198] {size: 6} {
[0] anonymous struct [0x780] {
[0] sid: pistol-beretta
}
[1] anonymous struct [0x788] {
[0] sid: pistol-revolver-taurus
}
[2] anonymous struct [0x790] {
[0] sid: rifle-remington-bolt
}
[3] anonymous struct [0x798] {
[0] sid: bow-ellie
}
[4] anonymous struct [0x7a0] {
[0] sid: shotgun-remington-pump
}
[5] anonymous struct [0x7a8] {
[0] sid: rifle-mpx5
}
}
}
تحرير ملفات DC باستخدام العلم -e
يمكنك استخدام العلم -e لتطبيق التحريرات على ملفات DC. يتم حفظ هذه التحريرات في نسخة جديدة من الملف الأصلي، تاركًا الأصل دون تغيير. يمكن تحديد عدة أعلام -e في نفس الوقت لإجراء عدة تحريرات دفعة واحدة.
بدلاً من ذلك، يمكنك توفير مسار ملف تحرير للبرنامج. يحتوي ملف التحرير على تحرير واحد لكل سطر. يستخدم نفس صيغة العلم -e، لكنه قد يكون أسهل قليلاً في الاستخدام إذا كنت ترغب في تطبيق عدة تحريرات دفعة واحدة.
يحدث التحرير قبل التفكيك وفك التجميع، لذلك سيظهر التحرير في الملفات المُنشأة.
كل تحرير يتبع هذه الصيغة:```xml
[]= ``` - ``: عنوان الذاكرة للهيكل الذي تريد تحريره (بالأساس الست عشري، بدءًا من `0x`. من الأسهل نسخه من نسخة مفككة من الملف الذي تريد تحريره). - ``: مؤشر المتغير العضو داخل الهيكل. يعادل الرقم الذي تراه على يسار العضو. - ``: القيمة الجديدة لتعيينها لذلك العضو. يجب أن تكون بنفس الحجم. (الأعداد الصحيحة والأعداد العشرية بحجم 4، المعرّفات/الهياكل بحجم 8). لن يتحقق البرنامج من أن الهياكل من نفس النوع.لنفترض أن لديك هيكلًا مثل هذا:```c++ [4] firearm-gameplay-def [0x11C28] { [0] float 0.7 // might represent the rate of fire, so i want to lower it for my mod ... }
لاستبدال أول متغير عضو (الفهرس 0) بقيمة عائمة 0.5، سيكون أمر التحرير:
`-e 0x11C28[0]=0.5`
الهيكل الذي نريد تحريره موجود عند `0x11C28`، ونريد أول متغير عضو (الـ 0 إلى يسار القيمة العائمة). ثم نضع القيمة الجديدة بعد علامة `=`، وهي 0.5 في هذه الحالة. إذا نجح التحرير، سيعرض البرنامج رسالة تشير إلى أن القيمة تغيرت من `0.7->0.5`.
بالنسبة لملف التحرير، ببساطة احذف `-e` وضع تحريرًا واحدًا لكل سطر:
### edit_file.txt
0x11C28[0]=0.5
0x11C28[1]=0.2
...
## أنواع متغيرات الأعضاء
يمكن أن تحتوي الهياكل على أنواع مختلفة من متغيرات الأعضاء:
- `float` - حدد القيم العشرية بنقطة (مثل 0.5).
- `int` - حدد القيم الصحيحة بدون نقطة (مثل 42).
- `sid` (معرّف سلسلة) - (مزيد من المعلومات أدناه)
- `string` - غير مدعوم حاليًا للاستبدال
- `structure` - عن طريق استبدال مؤشر (مزيد من المعلومات أدناه)
### استبدال sid بالبحث عن الاسم:
`-e 0xABC[5]=ellie`
يبحث هذا عن القيمة "ellie" في قاعدة sid الحالية (sidbase). إذا لم تكن موجودة، يتم إصدار تحذير ولا يتم تطبيق أي تحرير. إذا تم العثور على القيمة، فإن قيمة التجزئة الفعلية (رقم كبير) ستحل محل القيمة الحالية في متغير العضو.
### استبدال sid بالتجاوز اليدوي المباشر للتجزئة:
`-e 0xABC[5]=#XXXXXXXXXXXXXXXX`
يشير # إلى قيمة تجزئة خام، سيتم تطبيقها مباشرة دون بحث.
### استبدال هياكل الأعضاء
إذا كان الهيكل يحتوي على هيكل آخر كعضو، يمكنك استبدال هيكل العضو بالكامل بتعيينه عنوان هيكل آخر.
على سبيل المثال، افترض أن لديك ما يلي:```c++
[4] weapon-gameplay-def [0x0C523] {
...
[7] firearm-gameplay-def [0x11C28] {
...
}
}
إذن weapon-gameplay-def يحتوي على firearm-gameplay-def.
لاستبدال firearm-gameplay-def داخل weapon-gameplay-def بـ firearm-gameplay-def مختلف موجود في العنوان 0x0ABC، سيكون التعديل كالتالي:
-e 0x11C28[7]=0x0ABC
يدعم VS Code إنشاء إضافات مخصصة لإضافة تظليل بناء الجملة (syntax highlighting) للغات مخصصة. تأتي dconstruct مع ملف .vsix يضيف هذا الدعم لامتداد الملف .dcpl. نظرًا لأنها لغة استخدام واحد ليس المقصود بها البرمجة اليومية، فأنا لا أقوم برفع الإضافة إلى السوق، بل أقوم بشحنها كملف .vsix خام. لتثبيت هذه الإضافة في VS Code الخاص بك، قم بتشغيل الأمر التالي:```shell code --install-extension <path/to/extension/dcpl-lint-0.0.1.vsix>
أو افتح لوحة الأوامر باستخدام CTRL+SHIFT+P واكتب "تثبيت الإضافة عبر VSIX" وحدد ملف .vsix.
بعد ذلك، يجب أن يبدو كود .dcpl الخاص بك شيئًا كهذا:

# المشكلات المعروفة
المفكك حاليًا ليس مكتملاً بنسبة 100% وبالتالي فهو في حالة "تجريبية". إذا تلقيت تحذيرات أثناء عملية التفكيك، فلا تقلق لأن هذه الدوال غير مدعومة حاليًا، ولكن من المأمول أن تكون كذلك في المستقبل. بالإضافة إلى هذه، هناك المزيد من المشكلات المعروفة في الوقت الحالي:
- الدوال ذات التعبير الواحد مع الاختصار الثقيل (خاصة تلك الموجودة داخل الهياكل) لم يتم تنفيذها بعد. لقد بدأت العمل على خوارزمية لهذه ولكن لا أعرف كم سيستغرق الأمر للانتهاء، على الرغم من أنها على رأس قائمة الأولويات
- بعض الأنواع غير صحيحة، خاصة أنواع الوسائط
- كتل if الفارغة في تعبيرات if/else يمكن أن تسبب مسافة بادئة غريبة. هذا مرة أخرى ليس خطأي دائمًا، حيث أن هناك بعض الفروع التي لا تقوم بأي عمل حقيقي، وهو أمر صعب اكتشافه
بالإضافة إلى هذه، هناك بعض المشكلات التي ربما لن يتم إصلاحها:
- إرجاع بيانات غير صالحة عندما لا يكون معروفًا ما إذا كانت الدالة void أم لا
- أطنان من الكود الزائد
# الميزات المخطط لها
- تنسيق إخراج كامل لـ Racket و Python
# شكر خاص
- **icemesh** – لتوفير [الهياكل الأساسية لملفات DC](https://github.com/icemesh/dc/tree/main/t2) و[المفكك الخاص به](https://github.com/icemesh/t2-dc-disasm)، والذي كان بمثابة مصدر إلهام إلى حد كبير.
- **Specilizer** – لأداته DC-Tool، والتي كانت أيضًا مصدر إلهام لهذا البرنامج.
- **uxh** – لمعرفته بالبرمجة النصية.
- **bigdragon** و **Wedge** لاختبار بيتا
- مجتمع التعديل بأكمله على Discord – لكونهم ودودين ومفيدين.
## الدعم
جميع أدواتي وتعديلاتي ستكون دائمًا مجانية 100%، لكن برامج مثل هذه تتطلب الكثير من العمل.
إذا كنت ترغب في دعمي، يمكنك زيارة صفحتي على Ko-fi:
[](https://ko-fi.com/deepquantum)
## الترخيص
الملفات التي تنشئها باستخدام هذا التعديل هي ملكك بالكامل، ولديك الحرية في فعل ما تريد بها. سيكون التقدير موضع ترحيب ولكنه ليس مطلوبًا بشكل صارم.
البرنامج نفسه مرخص بموجب [رخصة المشاع الإبداعي نسبة العمل إلى المؤلف - غير تجاري - منع الاشتقاق 4.0 الدولية](https://creativecommons.org/licenses/by-nc-nd/4.0/).
هذا يعني أنه يُسمح لك بمشاركة البرنامج مع الآخرين إذا قمت بنسبة العمل، لكن لا يُسمح لك حاليًا بتعديله أو تحقيق ربح منه.