ぷち髪型再現全般に関する総合覚書
ぷち髪型再現全般に関する総合覚書
諸事情で、書き途中のメモを載せているだけです
導入方法
1. ファイル配置
https://imas.cg/
を例にとると、主に
https://imas.cg/js/hair/
系と
https://imas.cg/js/ajax_hair_styling_list_by_idol_id
系の階層に、
今回用意したファイルをそれぞれ配置
imcg (最上位相当)
|
├ js
| └ hair
| ├ 10000.js ( 千川ちひろ )
| ├ 10001.js
| ├ 10002.js
| ├ .....
| ├ 19952.js ( 本来の実装髪型末尾相当 )
| ├ .....
| └ 19999.js ( その他連番総当たりで取得されたファイル )
|
└ petit_cg
|
└ ajax_hair_styling_list_by_idol_id
|
├ index.php
└ json
|
├ petit_hair_1001.json
├ petit_hair_1002.json
├ ....
└ petit_hair_1193.json
2. ぷちプロフのページ内スクリプト差し替え
https://imas.cg/petit_cg/coordinate_idol/[petit_id]/
の各種ページに相当する .html(.php) 内部から、
function getHairstyleList()
で検索し、API URLを差し替え。
function getHairstyleList() {
$.ajax({
type: 'POST',
- url: 'https://imas.cg/petit_cg/ajax_hair_styling_list_by_idol_id?l_frm=Petit_cg_coordinate_idol_1%26rnd=861092554',
//API URL は必ずクエリ系のゴミを除去して、正しく / で閉じる事
+ url: 'https://imas.cg/petit_cg/ajax_hair_styling_list_by_idol_id/',
dataType: 'json',
data: {
"idol_id": mc.character[default_chara].idol_id
}
-
【注】アクセスするAPIのURLはキレイに書き直しておく事。
余計なクエリなどを付けたままだと
QueryStringParameters
で
l_frm : Petit_cg_coordinate_idol_1&rnd=861092554
みたいな形式となってしまい、
POST のはずなのに GET 扱いになったりするので注意。
API 動作確認用スクリプト
~/petit_cg/ajax_hair_styling_list_by_idol_id/
に
index.php
と、petit_hair_1XXX.json
が統括されている /json/
フォルダを配置したら、恐らくAPIとして機能する筈?
以下をコンソールからたたいて確認。
それっぽい JSON が返れば成功。
ぷちコーディネートページの髪型取得処理を抜き出し & 簡略化したもの。
(()=>{
$.ajax({
type: 'POST',
url: 'https://imas.cg/petit_cg/ajax_hair_styling_list_by_idol_id/',
dataType: 'json',
data: {
"idol_id": 1023 // 仮 @ 高森藍子
}
}).done(function(data) {
console.info(data);
});
})();
上下どちらも同性質の処理です
(()=>{
const _url = 'https://imas.cg/petit_cg/ajax_hair_styling_list_by_idol_id/';
const _req = new XMLHttpRequest();
_req.open("POST", _url , true);
_req.responseType = "json";
_req.onload = function(oEvent) {
const _data = _req.response;
console.log(_data);
};
const _formData = new FormData();
_formData.append( "idol_id" , "1023" ); // 仮 @ 高森藍子
_req.send( _formData );
})();
▼ 以下は覚え書きなどです。
ぷち髪型再現に必要と思われる仕組み/情報など
/ajax_hair_styling_list_by_idol_id/
API および そこから得られる情報の再現/js/petit_cg/hair/XXXXX.js
ファイル群
多分この2つだけでOKぽい
API仕様メモ
ぷちプロフ(着せ替えページ)上で「ヘアスタイリング」タブ選択時に呼ばれる模様
「着せ替え」というよりは 「髪型変更の選択肢を表示する機能」
髪型選択時、以下が FormData で POST されている。
value は4ケタのぷちデレラID ( 1001
島村卯月 ~ 1193
久川颯 )
{
idol_id : 1001
}
API 応答内容
POSTされた ぷちデレラIDに対応する髪型系?JSONを返す。
JSON内にセリフなども含まれている模様
公式のAPI仕様だと、そのアカウントで解放済みのぷち髪型のみ返る
(=そのぷちで実装されている全髪型が得られるとは限らない)ので、取得検証には注意が必要。
コード的には index.php
を通じて /json/petit_hair_1XXX.json
の中身を出力してるだけ。
ちな、応答オブジェクト内にある idol_message
(セリフ) は、
本来であれば2種類のぷちセリフからどちらかランダムで出力される模様。
※ 今回の疑似APIは static な json を出力しているだけなのでセリフ1種固定。
定義js
/js/petit_cg/hair/[ID].js
に存在。
5ケタの数値から成っている。
先のAPIから得たJSON内部に含まれている髪型IDが、ここの .js ファイル名と連動しているため、
有効な髪型IDさえ判明すれば、当該する髪型定義 .js ファイルも確保可能。
- 例
10001
卯月の髪型 なら
/js/petit_cg/hair/10001.js
11001
卯月+の髪型 なら
/js/petit_cg/hair/11001.js
19001
卯月+の髪型(ハーフアップ左) なら
/js/petit_cg/hair/19001.js
衛藤美紗希 19952
美紗希+の髪型(アクセ無し) が恐らく最大値?
10000
は千川ちひろ相当 ( リフレッシュルームとかで使われる筈 )
jsのファイル数について
本来であれば全ぷちデレラ
10001 卯月 ~ 10193 久川颯 の実装開放髪型数が 398(のハズ)
10000 が千川ちひろ相当なので、さらに +1 = 399ファイル
更に、サーバーに推測総当たりで 20500 番台までアクセスし、有効DLされた物をさらに追加。
ざっと垣間見た限り、内容(画像としてのビジュアル)が被っている髪型っぽい?
とはいえ精査はしておらず「とりあえずアクセス出来たからDL確保したものをそのまま持たせている」感じです。
髪型定義 .js の中身・base64 とか
base64 png 形式で描かれた画像が複数 key-value ペアで格納されている模様。
https://base64.guru/converter/decode/image/png
うまくコピペすれば、この辺のテキトーなデコードサイトで髪型のパーツごとに覗ける。
が、もう少しサクッと確認できるスクリプトを以下に組んだ。
通信 + DevTool経由で髪型を可視化させてみる。
ちょっとした簡易お遊び確認用。
_accessDefList
で確認ID範囲を設定し、
_dlInfo.accessUrl
のURLで、staticな 髪型定義 .js のURLを指定。
Console上で実行すると、その定義内で使われている画像がログで可視化される
(async()=>{
class DownloadInfoItem{
/**@type {string} アクセス先URL*/
accessUrl;
/**@type {string|number} 対象ID */
targetId;
};
const _accessDefList = [
// ★ オプション1 @ [0]~[1]までの範囲を対象
[ 10000 , 10010 ],
];
/** @type {Array.<DownloadInfoItem>} */
const _dlTargetInfoList = [];
// DL範囲の配列を抱えた親配列
_accessDefList.forEach(_rangeArr=>{
const _minFromId = _rangeArr[0];
const _maxToId = _rangeArr[1];
const _rangeCount = _maxToId - _minFromId;
if(0 > _rangeCount ){ throw "範囲逆じゃない?"; }
console.info(`${_minFromId} ~ ${_maxToId} まで アクセス 個数 → ` , (_rangeCount+1));
for(let _targetId=_minFromId; _targetId<=_maxToId; _targetId++){
const _dlInfo = new DownloadInfoItem();
// ★ オプション2 @ アクセスしたい js ファイルのURLに書き換え
//_dlInfo.accessUrl = `https://imas.cg/js/petit_cg/hair/${_targetId}.js`;
_dlInfo.accessUrl = `https://mobamas.net/idolmaster/js/petit_cg/hair/${_targetId}.js`;
_dlInfo.targetId = _targetId;
_dlTargetInfoList.push( _dlInfo );
}
});
function b64pngConsole(_obj){
for(let _key in _obj){
const _val = _obj[_key];
if(_val.indexOf("data:image/png;base64,")==0){
console.log('%c ', `background-image: url(${_val});
background-size: contain;background-repeat: no-repeat;
border: solid 1px gray;font-size: 0;padding: 50px;`
);
}
}
}
_dlTargetInfoList.forEach(_dlInfo=>{
const _hairId = _dlInfo.targetId;
const image_script = document.createElement('script');
image_script.onload = function() {
const _targetvariable = window[`hair_${_hairId}`];
console.info(_hairId);
b64pngConsole(_targetvariable);
}
image_script.src = _dlInfo.accessUrl;
document.getElementsByTagName('head')[0].appendChild(image_script);
});
})();
以上 ✨